/* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ /*************************************************************************** Author: ShonK Project: Kauai Reviewed: Copyright (c) Microsoft Corporation Windows menu code. REVIEW shonk: error codes ***************************************************************************/ #include "frame.h" ASSERTNAME const achar kchList = '_'; const achar kchFontList = '$'; PMUB vpmubCur; RTCLASS(MUB) /*************************************************************************** Destructor - make sure vpmubCur is not this mub. ***************************************************************************/ MUB::~MUB(void) { //REVIEW shonk: free mem and the _hmenu if (vpmubCur == this) vpmubCur = pvNil; } /*************************************************************************** Static method to load and set a new menu bar. ***************************************************************************/ PMUB MUB::PmubNew(ulong ridMenuBar) { PMUB pmub; if ((pmub = NewObj MUB) == pvNil) return pvNil; if ((pmub->_hmenu = LoadMenu(vwig.hinst, MIR(ridMenuBar))) == hNil) { ReleasePpo(&pmub); return pvNil; } pmub->_cmnu = GetMenuItemCount(pmub->_hmenu); if (!pmub->_FInitLists()) { ReleasePpo(&pmub); return pvNil; } pmub->Set(); return pmub; } /*************************************************************************** Make this the current menu bar. ***************************************************************************/ void MUB::Set(void) { if (vwig.hwndClient != hNil) { Assert(IsWindow(vwig.hwndClient), "bad client window"); SendMessage(vwig.hwndClient, WM_MDISETMENU, (WPARAM)_hmenu, hNil); } else SetMenu(vwig.hwndApp, _hmenu); vpmubCur = this; } /*************************************************************************** Make sure the menu's are clean - ie, items are enabled/disabled/marked correctly. Called immediately before dropping the menus. ***************************************************************************/ void MUB::Clean(void) { long imnu, imni, cmnu; ulong grfeds; HMENU hmenu; long wcid; CMD cmd; //adjust for the goofy mdi window's menu cmnu = GetMenuItemCount(_hmenu); Assert(cmnu >= _cmnu, "somebody took some menus out of the menu bar!"); if (cmnu > _cmnu) { imnu = 1; cmnu = _cmnu + 1; } else imnu = 0; for ( ; imnu < cmnu; imnu++) { if ((hmenu = GetSubMenu(_hmenu, imnu)) == hNil) continue; for (imni = GetMenuItemCount(hmenu); imni-- != 0; ) { if ((wcid = GetMenuItemID(hmenu, imni)) == cidNil) continue; if (!_FGetCmdForWcid(wcid, &cmd)) grfeds = fedsDisable; else { grfeds = vpcex->GrfedsForCmd(&cmd); ReleasePpo(&cmd.pgg); } if (grfeds & fedsEnable) EnableMenuItem(hmenu, imni, MF_BYPOSITION | MF_ENABLED); else if (grfeds & fedsDisable) EnableMenuItem(hmenu, imni, MF_BYPOSITION | MF_GRAYED); if (grfeds & kgrfedsMark) { //REVIEW shonk: bullet doesn't work (uses a check mark) CheckMenuItem(hmenu, imni, (grfeds & (fedsCheck | fedsBullet)) ? MF_BYPOSITION | MF_CHECKED : MF_BYPOSITION | MF_UNCHECKED); } } } } /*************************************************************************** The given wcid is the value Windows handed us in a WM_COMMAND message. Remap it to a real command id and enqueue the result. ***************************************************************************/ void MUB::EnqueueWcid(long wcid) { CMD cmd; if (_FGetCmdForWcid(wcid, &cmd)) vpcex->EnqueueCmd(&cmd); } /*************************************************************************** Adds an item identified by the given list cid, long parameter and string. ***************************************************************************/ bool MUB::FAddListCid(long cid, long lw0, PSTN pstn) { AssertThis(0); AssertPo(pstn, 0); long imlst, ilw; MLST mlst; HMENU hmenuPrev; long dimni; bool fSeparator; bool fRet = fTrue; if (pvNil == _pglmlst) return fTrue; hmenuPrev = hNil; dimni = 0; for (imlst = 0; imlst < _pglmlst->IvMac(); imlst++) { _pglmlst->Get(imlst, &mlst); if (mlst.hmenu == hmenuPrev) { mlst.imniBase += dimni; _pglmlst->Put(imlst, &mlst); } else { hmenuPrev = mlst.hmenu; dimni = 0; } if (cid != mlst.cid) goto LAdjustSeparator; if (pvNil == mlst.pgllw) { if (pvNil == (mlst.pgllw = GL::PglNew(size(long)))) { fRet = fFalse; goto LAdjustSeparator; } _pglmlst->Put(imlst, &mlst); mlst.pgllw->SetMinGrow(10); } ilw = mlst.pgllw->IvMac(); if (!mlst.pgllw->FPush(&lw0)) { fRet = fFalse; goto LAdjustSeparator; } if (!InsertMenu(mlst.hmenu, mlst.imniBase + ilw, MF_BYPOSITION | MF_STRING, mlst.wcidList + ilw, pstn->Psz())) { fRet = fFalse; AssertDo(mlst.pgllw->FPop(), 0); goto LAdjustSeparator; } dimni++; LAdjustSeparator: fSeparator = mlst.imniBase > FPure(mlst.fSeparator) && pvNil != mlst.pgllw && mlst.pgllw->IvMac() > 0; if (fSeparator && !mlst.fSeparator) { //add a separator if (!InsertMenu(mlst.hmenu, mlst.imniBase, MF_BYPOSITION | MF_SEPARATOR, cidNil, pvNil)) { fRet = fFalse; } else { mlst.imniBase++; mlst.fSeparator = fTrue; _pglmlst->Put(imlst, &mlst); dimni++; } } else if (!fSeparator && mlst.fSeparator) { //delete a separator if (!DeleteMenu(mlst.hmenu, mlst.imniBase - 1, MF_BYPOSITION)) fRet = fFalse; else { mlst.imniBase--; mlst.fSeparator = fFalse; _pglmlst->Put(imlst, &mlst); dimni--; } } } return fRet; } /*************************************************************************** Removes all items identified by the given list cid, and long parameter or string. If pstn is non-nil, it is used to find the item. If pstn is nil, lw0 is used to identify the item. ***************************************************************************/ bool MUB::FRemoveListCid(long cid, long lw0, PSTN pstn) { AssertThis(0); AssertNilOrPo(pstn, 0); long imlst, ilw, cch; MLST mlst; SZ sz; HMENU hmenuPrev; long dimni; long lw; bool fSeparator, fSetWcid; bool fRet = fTrue; if (pvNil == _pglmlst) return fTrue; hmenuPrev = hNil; dimni = 0; for (imlst = 0; imlst < _pglmlst->IvMac(); imlst++) { _pglmlst->Get(imlst, &mlst); if (mlst.hmenu == hmenuPrev) { mlst.imniBase += dimni; Assert(mlst.imniBase >= FPure(mlst.fSeparator), "bad imniBase"); _pglmlst->Put(imlst, &mlst); } else { hmenuPrev = mlst.hmenu; dimni = 0; } if (cid != mlst.cid || pvNil == mlst.pgllw) goto LAdjustSeparator; fSetWcid = fFalse; for (ilw = 0; ilw < mlst.pgllw->IvMac(); ilw++) { if (pvNil == pstn) { mlst.pgllw->Get(ilw, &lw); if (lw != lw0) goto LSetWcid; } else { cch = GetMenuString(mlst.hmenu, mlst.imniBase + ilw, sz, kcchMaxSz, MF_BYPOSITION); if (!pstn->FEqualRgch(sz, cch)) goto LSetWcid; } if (!DeleteMenu(mlst.hmenu, mlst.imniBase + ilw, MF_BYPOSITION)) { fRet = fFalse; LSetWcid: if (fSetWcid) { cch = GetMenuString(mlst.hmenu, mlst.imniBase + ilw, sz, kcchMaxSz, MF_BYPOSITION); if (cch == 0 || !ModifyMenu(mlst.hmenu, mlst.imniBase + ilw, MF_BYPOSITION, mlst.wcidList + ilw, sz)) { fRet = fFalse; } } continue; } mlst.pgllw->Delete(ilw--); dimni--; fSetWcid = fTrue; } LAdjustSeparator: fSeparator = mlst.imniBase > FPure(mlst.fSeparator) && pvNil != mlst.pgllw && mlst.pgllw->IvMac() > 0; if (fSeparator && !mlst.fSeparator) { //add a separator if (!InsertMenu(mlst.hmenu, mlst.imniBase, MF_BYPOSITION | MF_SEPARATOR, cidNil, pvNil)) { fRet = fFalse; } else { mlst.imniBase++; mlst.fSeparator = fTrue; _pglmlst->Put(imlst, &mlst); dimni++; } } else if (!fSeparator && mlst.fSeparator) { //delete a separator if (!DeleteMenu(mlst.hmenu, mlst.imniBase - 1, MF_BYPOSITION)) fRet = fFalse; else { mlst.imniBase--; mlst.fSeparator = fFalse; _pglmlst->Put(imlst, &mlst); dimni--; } } } return fRet; } /*************************************************************************** Removes all items identified by the given list cid. ***************************************************************************/ bool MUB::FRemoveAllListCid(long cid) { AssertThis(0); long imlst, ilw; MLST mlst; HMENU hmenuPrev; long dimni; bool fSeparator; bool fRet = fTrue; if (pvNil == _pglmlst) return fTrue; hmenuPrev = hNil; dimni = 0; for (imlst = 0; imlst < _pglmlst->IvMac(); imlst++) { _pglmlst->Get(imlst, &mlst); if (mlst.hmenu == hmenuPrev) { mlst.imniBase += dimni; Assert(mlst.imniBase >= FPure(mlst.fSeparator), "bad imniBase"); _pglmlst->Put(imlst, &mlst); } else { hmenuPrev = mlst.hmenu; dimni = 0; } if (cid == mlst.cid && pvNil != mlst.pgllw) { for (ilw = 0; ilw < mlst.pgllw->IvMac(); ilw++) { if (!DeleteMenu(mlst.hmenu, mlst.imniBase + ilw, MF_BYPOSITION)) { fRet = fFalse; continue; } mlst.pgllw->Delete(ilw--); dimni--; } } fSeparator = mlst.imniBase > FPure(mlst.fSeparator) && pvNil != mlst.pgllw && mlst.pgllw->IvMac() > 0; if (fSeparator && !mlst.fSeparator) { //add a separator if (!InsertMenu(mlst.hmenu, mlst.imniBase, MF_BYPOSITION | MF_SEPARATOR, cidNil, pvNil)) { fRet = fFalse; } else { mlst.imniBase++; mlst.fSeparator = fTrue; _pglmlst->Put(imlst, &mlst); dimni++; } } else if (!fSeparator && mlst.fSeparator) { //delete a separator if (!DeleteMenu(mlst.hmenu, mlst.imniBase - 1, MF_BYPOSITION)) fRet = fFalse; else { mlst.imniBase--; mlst.fSeparator = fFalse; _pglmlst->Put(imlst, &mlst); dimni--; } } } return fRet; } /*************************************************************************** Changes the long parameter and the menu text associated with a menu list item. If pstnOld is non-nil, it is used to find the item. If pstnOld is nil, lwOld is used to identify the item. In either case lwNew is set as the new long parameter and if pstnNew is non-nil, it is used as the new menu item text. ***************************************************************************/ bool MUB::FChangeListCid(long cid, long lwOld, PSTN pstnOld, long lwNew, PSTN pstnNew) { AssertThis(0); AssertNilOrPo(pstnOld, 0); AssertNilOrPo(pstnNew, 0); long imlst, ilw, cch, lw; MLST mlst; SZ sz; bool fRet = fTrue; if (pvNil == _pglmlst) return fTrue; for (imlst = 0; imlst < _pglmlst->IvMac(); imlst++) { _pglmlst->Get(imlst, &mlst); if (cid != mlst.cid || pvNil == mlst.pgllw) continue; for (ilw = 0; ilw < mlst.pgllw->IvMac(); ilw++) { if (pvNil == pstnOld) { mlst.pgllw->Get(ilw, &lw); if (lw != lwOld) continue; } else { cch = GetMenuString(mlst.hmenu, mlst.imniBase + ilw, sz, kcchMaxSz, MF_BYPOSITION); if (!pstnOld->FEqualRgch(sz, cch)) continue; } if (pvNil != pstnNew) { //change the string fRet = ModifyMenu(mlst.hmenu, mlst.imniBase + ilw, MF_BYPOSITION | MF_STRING, mlst.wcidList + ilw, pstnNew->Psz()) && fRet; } mlst.pgllw->Put(ilw, &lwNew); } } return fRet; } /*************************************************************************** Fill in the CMD structure for the given wcid. ***************************************************************************/ bool MUB::_FGetCmdForWcid(long wcid, PCMD pcmd) { AssertVarMem(pcmd); MLST mlst; ClearPb(pcmd, size(*pcmd)); if (wcid >= wcidListBase && _FFindMlst(wcid, &mlst)) { long lw, cch; SZ sz; STN stn; mlst.pgllw->Get(wcid - mlst.wcidList, &lw); cch = GetMenuString(mlst.hmenu, mlst.imniBase + wcid - mlst.wcidList, sz, kcchMaxSz, MF_BYPOSITION); stn = sz; if (cch == 0 || (pcmd->pgg = GG::PggNew(0, 1, stn.CbData())) == pvNil) return fFalse; AssertDo(pcmd->pgg->FInsert(0, stn.CbData(), pvNil), 0); stn.GetData(pcmd->pgg->PvLock(0)); pcmd->pgg->Unlock(); pcmd->cid = mlst.cid; pcmd->rglw[0] = lw; } else pcmd->cid = wcid; return fTrue; } /*************************************************************************** See if the given item is in a list. ***************************************************************************/ bool MUB::_FFindMlst(long wcid, MLST *pmlst, long *pimlst) { long imlst; MLST mlst; if (pvNil == _pglmlst) return fFalse; for (imlst = _pglmlst->IvMac(); imlst-- > 0; ) { _pglmlst->Get(imlst, &mlst); if (wcid < mlst.wcidList || pvNil == mlst.pgllw) continue; if (wcid < mlst.pgllw->IvMac() + mlst.wcidList) { if (pvNil != pmlst) *pmlst = mlst; if (pvNil != pimlst) *pimlst = imlst; return fTrue; } } TrashVar(pmlst); TrashVar(pimlst); return fFalse; } /*************************************************************************** If the menu bar has a font list item or other list item, do the right thing. ***************************************************************************/ bool MUB::_FInitLists(void) { long imnu, imni, cmni, cmnu; HMENU hmenu; long cid; MLST mlst; SZ sz; STN stn; long onn; long wcidList = wcidListBase; cmnu = GetMenuItemCount(_hmenu); Assert(cmnu == _cmnu, "bad _cmnu"); for (imnu = 0; imnu < cmnu; imnu++) { if ((hmenu = GetSubMenu(_hmenu, imnu)) == hNil) continue; for (cmni = GetMenuItemCount(hmenu), imni = 0; imni < cmni; imni++) { if ((cid = GetMenuItemID(hmenu, imni)) == cidNil) continue; if (1 != GetMenuString(hmenu, imni, sz, kcchMaxSz, MF_BYPOSITION)) { continue; } switch (sz[0]) { default: break; case kchFontList: //insert all the fonts mlst.fSeparator = (0 < imni); if (!mlst.fSeparator) { if (!DeleteMenu(hmenu, imni, MF_BYPOSITION)) return fFalse; imni--; cmni--; } else if (!ModifyMenu(hmenu, imni, MF_BYPOSITION | MF_SEPARATOR, cidNil, pvNil)) { return fFalse; } mlst.imniBase = imni + 1; mlst.hmenu = hmenu; mlst.wcidList = wcidList; wcidList += dwcidList; mlst.cid = cid; if (pvNil == (mlst.pgllw = GL::PglNew(size(long), vntl.OnnMac()))) return fFalse; for (onn = 0; onn < vntl.OnnMac(); onn++) { vntl.GetStn(onn, &stn); if (!mlst.pgllw->FPush(&onn) || !InsertMenu(hmenu, ++imni, MF_BYPOSITION | MF_STRING, mlst.wcidList + onn, stn.Psz())) { ReleasePpo(&mlst.pgllw); return fFalse; } cmni++; } goto LInsertMlst; case kchList: mlst.hmenu = hmenu; mlst.imniBase = imni; mlst.wcidList = wcidList; wcidList += dwcidList; mlst.cid = cid; mlst.fSeparator = fFalse; mlst.pgllw = pvNil; if (!DeleteMenu(hmenu, imni, MF_BYPOSITION)) return fFalse; imni--; cmni--; LInsertMlst: if (pvNil == _pglmlst && pvNil == (_pglmlst = GL::PglNew(size(MLST), 1)) || !_pglmlst->FPush(&mlst)) { ReleasePpo(&mlst.pgllw); return fFalse; } break; } } } return fTrue; } #ifdef DEBUG /*************************************************************************** Mark mem used by the menu bar. ***************************************************************************/ void MUB::MarkMem(void) { AssertThis(0); long imlst; MLST mlst; MUB_PAR::MarkMem(); if (pvNil == _pglmlst) return; MarkMemObj(_pglmlst); for (imlst = _pglmlst->IvMac(); imlst-- > 0; ) { _pglmlst->Get(imlst, &mlst); MarkMemObj(mlst.pgllw); } } #endif //DEBUG