Microsoft-3D-Movie-Maker/kauai/SRC/MENUWIN.CPP

697 lines
16 KiB
C++

/* 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