mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 02:12:33 +01:00
1929 lines
44 KiB
C++
1929 lines
44 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Common base application class methods.
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
ASSERTNAME
|
|
|
|
|
|
PAPPB vpappb;
|
|
PCEX vpcex;
|
|
PSNDM vpsndm;
|
|
|
|
|
|
// basic commands common to most apps
|
|
BEGIN_CMD_MAP(APPB, CMH)
|
|
ON_CID_GEN(cidQuit, &APPB::FCmdQuit, pvNil)
|
|
ON_CID_GEN(cidShowClipboard, &APPB::FCmdShowClipboard, &APPB::FEnableAppCmd)
|
|
ON_CID_GEN(cidChooseWnd, &APPB::FCmdChooseWnd, &APPB::FEnableAppCmd)
|
|
#ifdef MAC
|
|
ON_CID_GEN(cidOpenDA, &APPB::FCmdOpenDA, pvNil)
|
|
#endif //MAC
|
|
ON_CID_GEN(cidIdle, &APPB::FCmdIdle, pvNil)
|
|
ON_CID_GEN(cidEndModal, &APPB::FCmdEndModal, pvNil)
|
|
END_CMD_MAP_NIL()
|
|
|
|
|
|
RTCLASS(APPB)
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the app class. Assumes that the block is initially
|
|
zeroed. This implies that the block has to either be allocated
|
|
(using NewObj) or a global.
|
|
***************************************************************************/
|
|
APPB::APPB(void) : CMH(khidApp)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
vpappb = this;
|
|
Debug( _fCheckForLostMem = fTrue; )
|
|
_onnDefFixed = _onnDefVariable = onnNil;
|
|
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the app. Assumes we don't have to free anything.
|
|
***************************************************************************/
|
|
APPB::~APPB(void)
|
|
{
|
|
vpappb = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Calls _FInit and if successful, calls _Loop then _CleanUp.
|
|
***************************************************************************/
|
|
void APPB::Run(ulong grfapp, ulong grfgob, long ginDef)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_FInit(grfapp, grfgob, ginDef))
|
|
_Loop();
|
|
|
|
_CleanUp();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Quit routine. May or may not initiate the quit sequence (depending
|
|
on user input).
|
|
***************************************************************************/
|
|
void APPB::Quit(bool fForce)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_fQuit || DOCB::FQueryCloseAll(fForce ? fdocForceClose : fdocNil) ||
|
|
fForce)
|
|
{
|
|
_fQuit = fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return a default app name.
|
|
***************************************************************************/
|
|
void APPB::GetStnAppName(PSTN pstn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
|
|
*pstn = PszLit("Generic");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Sets the cursor. Increments the reference count on the cursor. If
|
|
fLongOp is true, the cursor will get used as the wait cursor, but
|
|
won't necessarily be displayed immediately.
|
|
***************************************************************************/
|
|
void APPB::SetCurs(PCURS pcurs, bool fLongOp)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pcurs, 0);
|
|
|
|
PCURS *ppcurs = fLongOp ? &_pcursWait : &_pcurs;
|
|
|
|
if (*ppcurs == pcurs)
|
|
return;
|
|
|
|
SwapVars(ppcurs, &pcurs);
|
|
if (pvNil != *ppcurs)
|
|
(*ppcurs)->AddRef();
|
|
|
|
// set the new one before we release the old one.
|
|
RefreshCurs();
|
|
ReleasePpo(&pcurs);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the indicated cursor as the current one.
|
|
***************************************************************************/
|
|
void APPB::SetCursCno(PRCA prca, CNO cno, bool fLongOp)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(prca, 0);
|
|
|
|
PCURS pcurs;
|
|
|
|
if (pvNil == (pcurs = (PCURS)prca->PbacoFetch(kctgCursor,
|
|
cno, CURS::FReadCurs)))
|
|
{
|
|
Warn("cursor not found");
|
|
return;
|
|
}
|
|
SetCurs(pcurs, fLongOp);
|
|
ReleasePpo(&pcurs);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure the current cursor is being used by the system.
|
|
***************************************************************************/
|
|
void APPB::RefreshCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
PCURS *ppcurs = _cactLongOp > 0 ? &_pcursWait : &_pcurs;
|
|
|
|
if (pvNil != *ppcurs)
|
|
(*ppcurs)->Set();
|
|
else
|
|
{
|
|
#ifdef WIN
|
|
SetCursor(LoadCursor(hNil,
|
|
_cactLongOp > 0 ? IDC_WAIT : IDC_ARROW));
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
HCURS hcurs;
|
|
|
|
if (_cactLongOp > 0 && hNil != (hcurs = GetCursor(watchCursor)))
|
|
SetCursor(*hcurs);
|
|
else
|
|
SetCursor(&qd.arrow);
|
|
#endif //MAC
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Starting a long operation, put up the wait cursor.
|
|
***************************************************************************/
|
|
void APPB::BeginLongOp(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_cactLongOp++ == 0)
|
|
RefreshCurs();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Done with a long operation. Decrement the long op count and if it
|
|
becomes zero, use the normal cursor. If fAll is true, set the
|
|
long op count to 0.
|
|
***************************************************************************/
|
|
void APPB::EndLongOp(bool fAll)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_cactLongOp == 0)
|
|
return;
|
|
|
|
if (fAll)
|
|
_cactLongOp = 0;
|
|
else
|
|
_cactLongOp--;
|
|
|
|
if (_cactLongOp == 0)
|
|
RefreshCurs();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the current cursor/modifier state. If fAsync is set, the key state
|
|
returned is the actual current values at the hardware level, ie, not
|
|
synchronized with the command stream.
|
|
***************************************************************************/
|
|
ulong APPB::GrfcustCur(bool fAsync)
|
|
{
|
|
AssertThis(0);
|
|
|
|
#ifdef WIN
|
|
short (*pfnT)(int);
|
|
|
|
pfnT = fAsync ? GetAsyncKeyState : GetKeyState;
|
|
_grfcust &= ~kgrfcustUser;
|
|
if (pfnT(VK_CONTROL) < 0)
|
|
_grfcust |= fcustCmd;
|
|
if (pfnT(VK_SHIFT) < 0)
|
|
_grfcust |= fcustShift;
|
|
if (pfnT(VK_MENU) < 0)
|
|
_grfcust |= fcustOption;
|
|
if (pfnT(VK_LBUTTON) < 0)
|
|
_grfcust |= fcustMouse;
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
Assert(!fAsync, "Unimplemented code"); //REVIEW shonk: Mac: implement
|
|
#endif //MAC
|
|
|
|
return _grfcust;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Modify the current cursor/modifier state. Doesn't affect the key
|
|
states or mouse state.
|
|
***************************************************************************/
|
|
void APPB::ModifyGrfcust(ulong grfcustOr, ulong grfcustXor)
|
|
{
|
|
AssertThis(0);
|
|
|
|
grfcustOr &= ~kgrfcustUser;
|
|
grfcustXor &= ~kgrfcustUser;
|
|
|
|
_grfcust |= grfcustOr;
|
|
_grfcust ^= grfcustXor;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Hide the cursor
|
|
***************************************************************************/
|
|
void APPB::HideCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
MacWin( HideCursor(), ShowCursor(fFalse) );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Show the cursor
|
|
***************************************************************************/
|
|
void APPB::ShowCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
MacWin( ShowCursor(), ShowCursor(fTrue) );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Warp the cursor to (xpScreen, ypScreen)
|
|
***************************************************************************/
|
|
void APPB::PositionCurs(long xpScreen, long ypScreen)
|
|
{
|
|
AssertThis(0);
|
|
|
|
// REVIEW shonk: implement on Mac
|
|
MacWin( RawRtn(), SetCursorPos(xpScreen, ypScreen) );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the default variable pitch font.
|
|
***************************************************************************/
|
|
long APPB::OnnDefVariable(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_onnDefVariable == onnNil)
|
|
{
|
|
STN stn;
|
|
|
|
stn = MacWin( PszLit("New York"), PszLit("Times New Roman") );
|
|
if (!vntl.FGetOnn(&stn, &_onnDefVariable))
|
|
_onnDefVariable = vntl.OnnSystem();
|
|
}
|
|
return _onnDefVariable;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the default fixed pitch font.
|
|
***************************************************************************/
|
|
long APPB::OnnDefFixed(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_onnDefFixed == onnNil)
|
|
{
|
|
STN stn;
|
|
|
|
stn = MacWin( PszLit("Courier"), PszLit("Courier New") );
|
|
if (!vntl.FGetOnn(&stn, &_onnDefFixed))
|
|
{
|
|
// just use the first fixed pitch font
|
|
for (_onnDefFixed = 0; ; _onnDefFixed++)
|
|
{
|
|
if (_onnDefFixed >= vntl.OnnMac())
|
|
{
|
|
_onnDefFixed = vntl.OnnSystem();
|
|
break;
|
|
}
|
|
if (vntl.FFixedPitch(_onnDefFixed))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return _onnDefFixed;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to return the default text size.
|
|
REVIEW shonk: DypTextDef: what's the right way to do this?
|
|
***************************************************************************/
|
|
long APPB::DypTextDef(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return 12;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Quit the app (don't force it).
|
|
***************************************************************************/
|
|
bool APPB::FCmdQuit(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
Quit(fFalse);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Open a window onto the clipboard, if it exists.
|
|
***************************************************************************/
|
|
bool APPB::FCmdShowClipboard(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
vpclip->Show();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handles an idle command.
|
|
***************************************************************************/
|
|
bool APPB::FCmdIdle(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
static long _cactIdle = 0;
|
|
PGOB pgob;
|
|
|
|
if (_fQuit)
|
|
return fFalse;
|
|
|
|
++_cactIdle;
|
|
|
|
#ifdef DEBUG
|
|
if ((_cactIdle & 0x00FF) == 0 && _fCheckForLostMem && _cactModal <= 0)
|
|
{
|
|
UnmarkAllMem();
|
|
UnmarkAllObjs();
|
|
MarkMem(); //marks all frame-work memory
|
|
MarkUtilMem(); //marks all util memory
|
|
AssertUnmarkedObjs();
|
|
AssertUnmarkedMem();
|
|
}
|
|
#endif //DEBUG
|
|
|
|
if ((_cactIdle & 0x0F) == 1 && pvNil != (pgob = GOB::PgobScreen()))
|
|
{
|
|
//check to see if the mouse moved
|
|
PT pt;
|
|
bool fDown;
|
|
ulong grfcust;
|
|
CMD_MOUSE cmd;
|
|
|
|
pgob->GetPtMouse(&pt, &fDown);
|
|
if (fDown)
|
|
return fTrue;
|
|
|
|
pgob->MapPt(&pt, cooLocal, cooGlobal);
|
|
pgob = GOB::PgobFromPtGlobal(pt.xp, pt.yp, &pt);
|
|
grfcust = GrfcustCur();
|
|
if (pgob != _pgobMouse || pt.xp != _xpMouse || pt.yp != _ypMouse ||
|
|
_grfcustMouse != grfcust)
|
|
{
|
|
if (_pgobMouse != pgob)
|
|
{
|
|
_tsMouse = 0;
|
|
if (pvNil != _pgobMouse)
|
|
{
|
|
AssertPo(_pgobMouse, 0);
|
|
vpcex->EnqueueCid(cidRollOff, _pgobMouse);
|
|
_TakeDownToolTip();
|
|
}
|
|
_tsMouseEnter = TsCurrent();
|
|
_pgobMouse = pgob;
|
|
}
|
|
_xpMouse = pt.xp;
|
|
_ypMouse = pt.yp;
|
|
_grfcustMouse = grfcust;
|
|
|
|
ClearPb(&cmd, size(cmd));
|
|
cmd.cid = cidMouseMove;
|
|
cmd.pcmh = pgob;
|
|
cmd.xp = pt.xp;
|
|
cmd.yp = pt.yp;
|
|
cmd.grfcust = grfcust;
|
|
vpcex->EnqueueCmd((PCMD)&cmd);
|
|
}
|
|
|
|
// adjust tool tips
|
|
if (pvNil != _pgobMouse &&
|
|
(_fToolTip || TsCurrent() - _tsMouseEnter > _dtsToolTip))
|
|
{
|
|
_EnsureToolTip();
|
|
}
|
|
}
|
|
|
|
// Flush the sound manager occasionally to free up idle memory
|
|
if (pvNil != vpsndm && (_cactIdle & 0x003F) == 3)
|
|
vpsndm->Flush();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure no tool tip is up.
|
|
***************************************************************************/
|
|
void APPB::_TakeDownToolTip(void)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
if (pvNil != (pgob = GOB::PgobFromHidScr(khidToolTip)))
|
|
{
|
|
if (pgob == _pgobMouse)
|
|
{
|
|
Bug("how did a tooltip become _pgobMouse???");
|
|
_pgobMouse = pvNil;
|
|
}
|
|
ReleasePpo(&pgob);
|
|
_pgobToolTipTarget = pvNil;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make sure a tool tip is up, if the current gob wants one.
|
|
***************************************************************************/
|
|
void APPB::_EnsureToolTip(void)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
if (pvNil == _pgobMouse)
|
|
return;
|
|
|
|
if (_pgobToolTipTarget == _pgobMouse)
|
|
return;
|
|
|
|
pgob = GOB::PgobFromHidScr(khidToolTip);
|
|
_fToolTip = FPure(_pgobMouse->FEnsureToolTip(&pgob, _xpMouse, _ypMouse));
|
|
if (!_fToolTip)
|
|
{
|
|
ReleasePpo(&pgob);
|
|
_pgobToolTipTarget = pvNil;
|
|
}
|
|
else
|
|
_pgobToolTipTarget = _pgobMouse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Take down any existing tool tip and resest tool tip timing.
|
|
***************************************************************************/
|
|
void APPB::ResetToolTip(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_TakeDownToolTip();
|
|
_fToolTip = fFalse;
|
|
_tsMouseEnter = TsCurrent();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Enable app level commands
|
|
***************************************************************************/
|
|
bool APPB::FEnableAppCmd(PCMD pcmd, ulong *pgrfeds)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
AssertVarMem(pgrfeds);
|
|
|
|
*pgrfeds = fedsEnable;
|
|
switch (pcmd->cid)
|
|
{
|
|
case cidShowClipboard:
|
|
if (vpclip->FDocIsClip(pvNil))
|
|
goto LDisable;
|
|
break;
|
|
|
|
case cidChooseWnd:
|
|
if ((HWND)pcmd->rglw[0] == GOB::HwndMdiActive())
|
|
*pgrfeds |= fedsCheck;
|
|
else
|
|
*pgrfeds |= fedsUncheck;
|
|
break;
|
|
|
|
default:
|
|
BugVar("unhandled cid in FEnableAppCmd", &pcmd->cid);
|
|
LDisable:
|
|
*pgrfeds = fedsDisable;
|
|
break;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Respond to a cidChooseWnd command.
|
|
***************************************************************************/
|
|
bool APPB::FCmdChooseWnd(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
GOB::MakeHwndActive((HWND)pcmd->rglw[0]);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Application initialization.
|
|
***************************************************************************/
|
|
bool APPB::_FInit(ulong grfapp, ulong grfgob, long ginDef)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_fOffscreen = FPure(grfapp & fappOffscreen);
|
|
|
|
#ifdef DEBUG
|
|
if (!_FInitDebug())
|
|
return fFalse;
|
|
#endif
|
|
|
|
// initialize the command dispatcher
|
|
if (pvNil == (vpcex = CEX::PcexNew(20, 20)))
|
|
return fFalse;
|
|
|
|
// add the app as a handler (so it can catch menu commands)
|
|
if (!vpcex->FAddCmh(vpappb, kcmhlAppb))
|
|
return fFalse;
|
|
|
|
// do OS specific initialization
|
|
if (!_FInitOS())
|
|
return fFalse;
|
|
|
|
// Initialize the graphics stuff.
|
|
if (!FInitGfx())
|
|
return fFalse;
|
|
|
|
// set up the menus
|
|
if (!_FInitMenu())
|
|
return fFalse;
|
|
|
|
// initialize the screen gob
|
|
if (!GOB::FInitScreen(grfgob, ginDef))
|
|
return fFalse;
|
|
|
|
// initialize sound functionality
|
|
if (!_FInitSound(kwav22M16))
|
|
return fFalse;
|
|
|
|
// import any external clipboard
|
|
vpclip->Import();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the sound manager. Default is to return true whether or not
|
|
we could create the sound manager.
|
|
***************************************************************************/
|
|
bool APPB::_FInitSound(long wav)
|
|
{
|
|
AssertBaseThis(0);
|
|
PSNDV psndv;
|
|
|
|
if (pvNil != vpsndm)
|
|
return fTrue;
|
|
|
|
// create the Sound manager
|
|
if (pvNil == (vpsndm = SNDM::PsndmNew()))
|
|
return fTrue;
|
|
|
|
if (pvNil != (psndv = SDAM::PsdamNew(wav)))
|
|
{
|
|
vpsndm->FAddDevice(kctgWave, psndv);
|
|
ReleasePpo(&psndv);
|
|
}
|
|
|
|
// create the midi playback device - use the stream one
|
|
if (pvNil != (psndv = MDPS::PmdpsNew()))
|
|
{
|
|
vpsndm->FAddDevice(kctgMidi, psndv);
|
|
ReleasePpo(&psndv);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Standard menu initialization. Just loads menu number 128.
|
|
***************************************************************************/
|
|
bool APPB::_FInitMenu(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return MUB::PmubNew(128) != pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Main program loop.
|
|
***************************************************************************/
|
|
void APPB::_Loop(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
EVT evt;
|
|
Assert(chNil == 0 && vkNil == 0, "nil key events wrong");
|
|
|
|
while (!_fQuit && (!_fEndModal || _cactModal <= 0))
|
|
{
|
|
// do top of the loop stuff
|
|
TopOfLoop();
|
|
|
|
// internal commands have priority
|
|
if (vpcex->FDispatchNextCmd())
|
|
continue;
|
|
|
|
// handle system events
|
|
if (_FGetNextEvt(&evt))
|
|
_DispatchEvt(&evt);
|
|
else
|
|
{
|
|
// nothing to do, so enqueue some idle commands
|
|
vpcex->EnqueueCid(cidSelIdle, pvNil, pvNil, _fForeground);
|
|
vpcex->EnqueueCid(cidIdle);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef WIN
|
|
STDAPI_(int) DetectLeaks(BOOL fDebugOut, BOOL fMessageBox);
|
|
#endif //WIN
|
|
|
|
/***************************************************************************
|
|
Clean up routine for the app base class.
|
|
***************************************************************************/
|
|
void APPB::_CleanUp(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
PSNDM psndm;
|
|
|
|
if (pvNil != vpsndm)
|
|
{
|
|
// do this so if we pop into the debugger, or whatever while releasing
|
|
// vpsndm, we don't deactivate the sound manager.
|
|
psndm = vpsndm;
|
|
vpsndm = pvNil;
|
|
// deactivate the sndm to release all devices
|
|
psndm->Activate(fFalse);
|
|
ReleasePpo(&psndm);
|
|
}
|
|
|
|
#ifdef WIN
|
|
#ifdef DEBUG
|
|
// check for Audioman memory leaks
|
|
DetectLeaks(fTrue, fFalse);
|
|
#endif //DEBUG
|
|
#endif //WIN
|
|
|
|
GOB::ShutDown();
|
|
FIL::ShutDown();
|
|
#ifdef WIN
|
|
_ShutDownViewer();
|
|
#endif //WIN
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Activate or deactivate the application.
|
|
***************************************************************************/
|
|
void APPB::_Activate(bool fActive)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (pvNil != vpsndm)
|
|
vpsndm->Activate(fActive);
|
|
_fForeground = FPure(fActive);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
This gets called every time through the main app loop.
|
|
***************************************************************************/
|
|
void APPB::TopOfLoop(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
#ifdef DEBUG
|
|
if (_fRefresh)
|
|
{
|
|
//need to redraw all our windows - we ignored some paint
|
|
//events while in an assert
|
|
_fRefresh = fFalse;
|
|
GTE gte;
|
|
PGOB pgob;
|
|
ulong grfgte;
|
|
|
|
gte.Init(GOB::PgobScreen(), fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if ((grfgte & fgtePre) && pgob->Hwnd() != hNil)
|
|
pgob->InvalRc(pvNil);
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
// update any marked stuff
|
|
UpdateMarked();
|
|
|
|
// take down the wait cursor
|
|
EndLongOp(fTrue);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Update the given window. *prc is the bounding rectangle of the update
|
|
region.
|
|
***************************************************************************/
|
|
void APPB::UpdateHwnd(HWND hwnd, RC *prc, ulong grfapp)
|
|
{
|
|
AssertThis(0);
|
|
Assert(hNil != hwnd, "nil hwnd in UpdateHwnd");
|
|
AssertVarMem(prc);
|
|
|
|
PGOB pgob;
|
|
PGPT pgpt = pvNil;
|
|
|
|
if (pvNil == (pgob = GOB::PgobFromHwnd(hwnd)))
|
|
return;
|
|
|
|
#ifdef DEBUG
|
|
if (_fInAssert)
|
|
{
|
|
// don't do the update, just set _fRefresh so we'll invalidate
|
|
// everything the next time thru the main loop.
|
|
_fRefresh = fTrue;
|
|
return;
|
|
}
|
|
#endif //DEBUG
|
|
|
|
if ((grfapp & fappOffscreen) || (_fOffscreen && !(grfapp & fappOnscreen)))
|
|
{
|
|
//do offscreen drawing
|
|
pgpt = _PgptEnsure(prc);
|
|
}
|
|
|
|
//NOTE: technically we should map from hwnd to local coordinates
|
|
//but they are the same for an hwnd based gob.
|
|
pgob->DrawTree(pgpt, pvNil, prc, fgobUseVis);
|
|
|
|
if (pvNil != pgpt)
|
|
{
|
|
// put the image on the screen
|
|
GNV gnv(pgob);
|
|
GNV gnvSrc(pgpt);
|
|
|
|
gnv.CopyPixels(&gnvSrc, prc, prc);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map a handler id to a handler.
|
|
***************************************************************************/
|
|
PCMH APPB::PcmhFromHid(long hid)
|
|
{
|
|
AssertThis(0);
|
|
PCMH pcmh;
|
|
|
|
switch (hid)
|
|
{
|
|
case hidNil:
|
|
return pvNil;
|
|
|
|
case khidApp:
|
|
return this;
|
|
}
|
|
|
|
if (pvNil != (pcmh = CLOK::PclokFromHid(hid)))
|
|
return pcmh;
|
|
return GOB::PgobFromHidScr(hid);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
The command handler is dying - take it out of any lists it's in.
|
|
***************************************************************************/
|
|
void APPB::BuryCmh(PCMH pcmh)
|
|
{
|
|
AssertThis(0);
|
|
long imodcx;
|
|
MODCX modcx;
|
|
|
|
// NOTE: don't do an AssertPo(pcmh, 0)!
|
|
Assert(pvNil != pcmh, 0);
|
|
|
|
if (pvNil != vpcex)
|
|
vpcex->BuryCmh(pcmh);
|
|
|
|
if (pvNil != _pglmodcx && 0 < (imodcx = _pglmodcx->IvMac()))
|
|
{
|
|
while (imodcx-- > 0)
|
|
{
|
|
_pglmodcx->Get(imodcx, &modcx);
|
|
AssertPo(modcx.pcex, 0);
|
|
modcx.pcex->BuryCmh(pcmh);
|
|
}
|
|
}
|
|
|
|
CLOK::BuryCmh(pcmh);
|
|
if (pcmh == _pgobMouse)
|
|
{
|
|
_pgobMouse = pvNil;
|
|
_TakeDownToolTip();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark the rectangle for a fast update. The rectangle is given in
|
|
pgobCoo coordinates. If prc is nil, the entire rectangle for pgobCoo
|
|
is used.
|
|
***************************************************************************/
|
|
void APPB::MarkRc(RC *prc, PGOB pgobCoo)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertPo(pgobCoo, 0);
|
|
|
|
_MarkRegnRc(pvNil, prc, pgobCoo);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark a region dirty.
|
|
***************************************************************************/
|
|
void APPB::MarkRegn(PREGN pregn, PGOB pgobCoo)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pregn, 0);
|
|
AssertPo(pgobCoo, 0);
|
|
|
|
_MarkRegnRc(pregn, pvNil, pgobCoo);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark the rectangle for a fast update. The rectangle is given in
|
|
pgobCoo coordinates. If prc is nil, the entire rectangle for pgobCoo
|
|
is used.
|
|
***************************************************************************/
|
|
void APPB::_MarkRegnRc(PREGN pregn, RC *prc, PGOB pgobCoo)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pregn, 0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertPo(pgobCoo, 0);
|
|
|
|
MKRGN mkrgn;
|
|
long imkrgn;
|
|
RC rc;
|
|
PT pt;
|
|
HWND hwnd;
|
|
|
|
//get the offset
|
|
pgobCoo->GetRc(&rc, cooHwnd);
|
|
pt = rc.PtTopLeft();
|
|
|
|
if (pvNil == prc)
|
|
{
|
|
if (pvNil == pregn)
|
|
{
|
|
//use the full rectangle for the GOB
|
|
if (rc.FEmpty())
|
|
return;
|
|
prc = &rc;
|
|
}
|
|
else if (pregn->FEmpty())
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//offset *prc to hwnd coordinates
|
|
rc.OffsetCopy(prc, pt.xp, pt.yp);
|
|
if (rc.FEmpty() && (pvNil == pregn || pregn->FEmpty()))
|
|
return;
|
|
prc = &rc;
|
|
}
|
|
|
|
//offset the region to hwnd coordinates
|
|
if (pvNil != pregn)
|
|
pregn->Offset(pt.xp, pt.yp);
|
|
hwnd = pgobCoo->HwndContainer();
|
|
|
|
if (pvNil == _pglmkrgn)
|
|
{
|
|
if (pvNil == (_pglmkrgn = GL::PglNew(size(MKRGN))))
|
|
goto LFail;
|
|
}
|
|
else
|
|
{
|
|
for (imkrgn = _pglmkrgn->IvMac(); imkrgn-- > 0; )
|
|
{
|
|
_pglmkrgn->Get(imkrgn, &mkrgn);
|
|
if (mkrgn.hwnd == hwnd)
|
|
{
|
|
//already something marked dirty, union in the new stuff
|
|
if (pvNil != prc && !mkrgn.pregn->FUnionRc(prc))
|
|
goto LFail;
|
|
if (pvNil != pregn && !mkrgn.pregn->FUnion(pregn))
|
|
goto LFail;
|
|
goto LDone;
|
|
}
|
|
}
|
|
}
|
|
|
|
//create a new entry
|
|
mkrgn.hwnd = hwnd;
|
|
if (pvNil == (mkrgn.pregn = REGN::PregnNew(prc)) ||
|
|
pvNil != pregn && !mkrgn.pregn->FUnion(pregn) ||
|
|
!_pglmkrgn->FPush(&mkrgn))
|
|
{
|
|
ReleasePpo(&mkrgn.pregn);
|
|
LFail:
|
|
Warn("marking failed");
|
|
pgobCoo->InvalRc(pvNil, kginSysInval);
|
|
}
|
|
|
|
LDone:
|
|
//put the region back the way it was
|
|
if (pvNil != pregn)
|
|
pregn->Offset(-pt.xp, -pt.yp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unmark the rectangle for a fast update. The rectangle is given in
|
|
pgobCoo coordinates. If prc is nil, the entire rectangle for pgobCoo
|
|
is used.
|
|
***************************************************************************/
|
|
void APPB::UnmarkRc(RC *prc, PGOB pgobCoo)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertPo(pgobCoo, 0);
|
|
|
|
_UnmarkRegnRc(pvNil, prc, pgobCoo);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark a region clean.
|
|
***************************************************************************/
|
|
void APPB::UnmarkRegn(PREGN pregn, PGOB pgobCoo)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pregn, 0);
|
|
AssertPo(pgobCoo, 0);
|
|
|
|
_UnmarkRegnRc(pregn, pvNil, pgobCoo);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Unmark the rectangle for a fast update. The rectangle is given in
|
|
pgobCoo coordinates. If prc is nil, the entire rectangle for pgobCoo
|
|
is used.
|
|
***************************************************************************/
|
|
void APPB::_UnmarkRegnRc(PREGN pregn, RC *prc, PGOB pgobCoo)
|
|
{
|
|
AssertNilOrPo(pregn, 0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertPo(pgobCoo, 0);
|
|
|
|
MKRGN mkrgn;
|
|
long imkrgn;
|
|
RC rc;
|
|
PT pt;
|
|
HWND hwnd;
|
|
|
|
if (pvNil == _pglmkrgn || _pglmkrgn->IvMac() == 0)
|
|
return;
|
|
|
|
//get the mkrgn and imkrgn
|
|
hwnd = pgobCoo->HwndContainer();
|
|
for (imkrgn = _pglmkrgn->IvMac(); ; )
|
|
{
|
|
if (imkrgn-- <= 0)
|
|
return;
|
|
_pglmkrgn->Get(imkrgn, &mkrgn);
|
|
if (mkrgn.hwnd == hwnd)
|
|
break;
|
|
}
|
|
|
|
//get the offset
|
|
pgobCoo->GetRc(&rc, cooHwnd);
|
|
pt = rc.PtTopLeft();
|
|
|
|
if (pvNil == prc)
|
|
{
|
|
if (pvNil == pregn)
|
|
{
|
|
//use the full rectangle for the GOB
|
|
if (rc.FEmpty())
|
|
return;
|
|
prc = &rc;
|
|
}
|
|
else if (pregn->FEmpty())
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//offset *prc to hwnd coordinates
|
|
rc.OffsetCopy(prc, pt.xp, pt.yp);
|
|
if (rc.FEmpty() && (pvNil == pregn || pregn->FEmpty()))
|
|
return;
|
|
prc = &rc;
|
|
}
|
|
|
|
if (pvNil != pregn)
|
|
{
|
|
pregn->Offset(pt.xp, pt.yp);
|
|
mkrgn.pregn->FDiff(pregn);
|
|
pregn->Offset(-pt.xp, -pt.yp);
|
|
}
|
|
if (pvNil != prc)
|
|
mkrgn.pregn->FDiffRc(prc);
|
|
|
|
if (mkrgn.pregn->FEmpty())
|
|
{
|
|
ReleasePpo(&mkrgn.pregn);
|
|
_pglmkrgn->Delete(imkrgn);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the bounding rectangle of any marked portion of the given hwnd.
|
|
***************************************************************************/
|
|
bool APPB::FGetMarkedRc(HWND hwnd, RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
Assert(hNil != hwnd, "bad hwnd");
|
|
AssertVarMem(prc);
|
|
|
|
MKRGN mkrgn;
|
|
long imkrgn;
|
|
|
|
if (pvNil != _pglmkrgn)
|
|
{
|
|
//get the mkrgn and imkrgn
|
|
for (imkrgn = _pglmkrgn->IvMac(); imkrgn-- > 0; )
|
|
{
|
|
_pglmkrgn->Get(imkrgn, &mkrgn);
|
|
if (mkrgn.hwnd == hwnd)
|
|
return !mkrgn.pregn->FEmpty(prc);
|
|
}
|
|
}
|
|
|
|
prc->Zero();
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If there is a marked region for this HWND, remove it from the list
|
|
and invalidate it. This is called when we get a system paint/update
|
|
event.
|
|
***************************************************************************/
|
|
void APPB::InvalMarked(HWND hwnd)
|
|
{
|
|
AssertThis(0);
|
|
Assert(hNil != hwnd, "bad hwnd");
|
|
|
|
long imkrgn;
|
|
MKRGN mkrgn;
|
|
RC rc;
|
|
RCS rcs;
|
|
|
|
if (pvNil == _pglmkrgn)
|
|
return;
|
|
|
|
for (imkrgn = _pglmkrgn->IvMac(); imkrgn-- > 0; )
|
|
{
|
|
_pglmkrgn->Get(imkrgn, &mkrgn);
|
|
if (mkrgn.hwnd == hwnd)
|
|
{
|
|
if (!mkrgn.pregn->FEmpty(&rc))
|
|
{
|
|
rcs = RCS(rc);
|
|
InvalHwndRcs(hwnd, &rcs);
|
|
}
|
|
ReleasePpo(&mkrgn.pregn);
|
|
_pglmkrgn->Delete(imkrgn);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Update all marked regions.
|
|
***************************************************************************/
|
|
void APPB::UpdateMarked(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
MKRGN mkrgn;
|
|
PGOB pgob;
|
|
|
|
if (pvNil == _pglmkrgn)
|
|
return;
|
|
|
|
while (_pglmkrgn->FPop(&mkrgn))
|
|
{
|
|
if (pvNil != (pgob = GOB::PgobFromHwnd(mkrgn.hwnd)))
|
|
_FastUpdate(pgob, mkrgn.pregn);
|
|
ReleasePpo(&mkrgn.pregn);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Do a fast update of the gob and its descendents into the given gpt.
|
|
***************************************************************************/
|
|
void APPB::_FastUpdate(PGOB pgob, PREGN pregnClip, ulong grfapp, PGPT pgpt)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgob, 0);
|
|
AssertPo(pregnClip, 0);
|
|
AssertNilOrPo(pgpt, 0);
|
|
|
|
RC rc, rcT;
|
|
bool fOffscreen;
|
|
|
|
if (pregnClip->FEmpty(&rc))
|
|
return;
|
|
|
|
pgob->GetRc(&rcT, cooLocal);
|
|
if (!rc.FIntersect(&rcT))
|
|
return;
|
|
|
|
fOffscreen = (FPure(grfapp & fappOffscreen) ||
|
|
(_fOffscreen && !(grfapp & fappOnscreen))) &&
|
|
pvNil == pgpt && hNil != pgob->Hwnd() &&
|
|
pvNil != (pgpt = _PgptEnsure(&rc));
|
|
|
|
pgob->DrawTreeRgn(pgpt, pvNil, pregnClip, fgobUseVis);
|
|
|
|
if (fOffscreen)
|
|
{
|
|
//copy the stuff to the screen
|
|
GNV gnvOff(pgpt);
|
|
GNV gnv(pgob);
|
|
PGPT pgptDst = pgob->Pgpt();
|
|
|
|
pgptDst->ClipToRegn(&pregnClip);
|
|
_CopyPixels(&gnvOff, &rc, &gnv, &rc);
|
|
pgptDst->ClipToRegn(&pregnClip);
|
|
GPT::Flush();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the transition to apply the next time we do offscreen fast updating.
|
|
gft is the transition type. The meaning of lwGft depends on the
|
|
transition. dts is how long each phase of the transition should take.
|
|
pglclr is the new palette (may be nil). acr is an intermediary color
|
|
to transition to. If acr is clear, no intermediary transition is done.
|
|
|
|
If pglclr is not nil, this AddRef's it and holds onto it until after
|
|
the transition is done.
|
|
***************************************************************************/
|
|
void APPB::SetGft(long gft, long lwGft, ulong dts, PGL pglclr, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pglclr, 0);
|
|
AssertPo(&acr, 0);
|
|
|
|
_gft = gft;
|
|
_lwGft = lwGft;
|
|
_dtsGft = dts;
|
|
ReleasePpo(&_pglclr);
|
|
if (pvNil != pglclr)
|
|
{
|
|
_pglclr = pglclr;
|
|
_pglclr->AddRef();
|
|
}
|
|
_acr = acr;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copy pixels from an offscreen buffer (pgnvSrc, prcSrc) to the screen
|
|
(pgnvDst, prcDst). This gives the app a chance to do any transition
|
|
affects they want.
|
|
***************************************************************************/
|
|
void APPB::_CopyPixels(PGNV pgnvSrc, RC *prcSrc, PGNV pgnvDst, RC *prcDst)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertPo(pgnvDst, 0);
|
|
AssertVarMem(prcDst);
|
|
|
|
switch (_gft)
|
|
{
|
|
default:
|
|
pgnvDst->CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
break;
|
|
|
|
case kgftWipe:
|
|
pgnvDst->Wipe(_lwGft, _acr, pgnvSrc, prcSrc, prcDst, _dtsGft, _pglclr);
|
|
break;
|
|
|
|
case kgftSlide:
|
|
pgnvDst->Slide(_lwGft, _acr, pgnvSrc, prcSrc, prcDst, _dtsGft, _pglclr);
|
|
break;
|
|
|
|
case kgftDissolve:
|
|
// high word of _lwGft is number for columns, low word is number of rows
|
|
// of the dissolve grid. If one or both is zero, the dissolve is done
|
|
// at the pixel level offscreen.
|
|
pgnvDst->Dissolve(SwHigh(_lwGft), SwLow(_lwGft), _acr,
|
|
pgnvSrc, prcSrc, prcDst, _dtsGft, _pglclr);
|
|
break;
|
|
|
|
case kgftFade:
|
|
pgnvDst->Fade(_lwGft, _acr, pgnvSrc, prcSrc, prcDst, _dtsGft, _pglclr);
|
|
break;
|
|
|
|
case kgftIris:
|
|
// top 15 bits are the xp value, next 15 bits are the (signed) yp value,
|
|
// bottom 2 bits are the gfd.
|
|
pgnvDst->Iris(_lwGft & 0x03, _lwGft >> 17, (_lwGft << 15) >> 17, _acr,
|
|
pgnvSrc, prcSrc, prcDst, _dtsGft, _pglclr);
|
|
break;
|
|
}
|
|
|
|
_gft = gftNil;
|
|
ReleasePpo(&_pglclr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get an offscreen GPT big enough to enclose the given rectangle.
|
|
Should minimize reallocations. Doesn't increment a ref count.
|
|
APPB maintains ownership of the GPT.
|
|
***************************************************************************/
|
|
PGPT APPB::_PgptEnsure(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
|
|
RC rc;
|
|
|
|
if (prc->Dxp() > _dxpOff || prc->Dyp() > _dypOff)
|
|
{
|
|
ReleasePpo(&_pgptOff);
|
|
rc.Set(0, 0, LwMax(prc->Dxp(), _dxpOff), LwMax(prc->Dyp(), _dypOff));
|
|
_pgptOff = GPT::PgptNewOffscreen(&rc, 8);
|
|
if (pvNil != _pgptOff)
|
|
{
|
|
_dxpOff = rc.Dxp();
|
|
_dypOff = rc.Dyp();
|
|
}
|
|
else
|
|
_dxpOff = _dypOff = 0;
|
|
}
|
|
if (pvNil != _pgptOff)
|
|
{
|
|
PT pt = prc->PtTopLeft();
|
|
_pgptOff->SetPtBase(&pt);
|
|
}
|
|
return _pgptOff;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
See if the given property is in the property list.
|
|
***************************************************************************/
|
|
bool APPB::_FFindProp(long prid, PROP *pprop, long *piprop)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(pprop);
|
|
AssertNilOrVarMem(piprop);
|
|
long ivMin, ivLim, iv;
|
|
PROP prop;
|
|
|
|
if (pvNil == _pglprop)
|
|
{
|
|
if (pvNil != piprop)
|
|
*piprop = 0;
|
|
TrashVar(pprop);
|
|
return fFalse;
|
|
}
|
|
|
|
for (ivMin = 0, ivLim = _pglprop->IvMac(); ivMin < ivLim; )
|
|
{
|
|
iv = (ivMin + ivLim) / 2;
|
|
_pglprop->Get(iv, &prop);
|
|
if (prop.prid < prid)
|
|
ivMin = iv + 1;
|
|
else if (prop.prid > prid)
|
|
ivLim = iv;
|
|
else
|
|
{
|
|
if (pvNil != piprop)
|
|
*piprop = iv;
|
|
if (pvNil != pprop)
|
|
*pprop = prop;
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
if (pvNil != piprop)
|
|
*piprop = ivMin;
|
|
TrashVar(pprop);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the given property in the property list.
|
|
***************************************************************************/
|
|
bool APPB::_FSetProp(long prid, long lw)
|
|
{
|
|
AssertThis(0);
|
|
PROP prop;
|
|
long iprop;
|
|
|
|
if (_FFindProp(prid, &prop, &iprop))
|
|
{
|
|
Assert(prop.prid == prid, 0);
|
|
prop.lw = lw;
|
|
_pglprop->Put(iprop, &prop);
|
|
return fTrue;
|
|
}
|
|
|
|
if (pvNil == _pglprop)
|
|
{
|
|
Assert(iprop == 0, 0);
|
|
if (pvNil == (_pglprop = GL::PglNew(size(PROP))))
|
|
return fFalse;
|
|
}
|
|
|
|
prop.prid = prid;
|
|
prop.lw = lw;
|
|
return _pglprop->FInsert(iprop, &prop);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the indicated property, using the given parameter.
|
|
***************************************************************************/
|
|
bool APPB::FSetProp(long prid, long lw)
|
|
{
|
|
AssertThis(0);
|
|
|
|
switch (prid)
|
|
{
|
|
case kpridFullScreen:
|
|
if (FPure(_fFullScreen) == FPure(lw))
|
|
break;
|
|
#ifdef WIN
|
|
long lwT;
|
|
|
|
// if we're already maximized, we have to restore and maximize
|
|
// to force the system to use our new MINMAXINFO.
|
|
// REVIEW shonk: full screen: is there a better way to do this?
|
|
if (!FGetProp(kpridMaximized, &lwT))
|
|
return fFalse;
|
|
if (lwT)
|
|
{
|
|
if (!FSetProp(kpridMaximized, fFalse))
|
|
return fFalse;
|
|
_fFullScreen = FPure(lw);
|
|
if (!FSetProp(kpridMaximized, fTrue))
|
|
{
|
|
_fFullScreen = !lw;
|
|
return fFalse;
|
|
}
|
|
}
|
|
#else //!WIN
|
|
// REVIEW shonk: Mac: implement
|
|
#endif //!WIN
|
|
_fFullScreen = FPure(lw);
|
|
break;
|
|
|
|
case kpridMaximized:
|
|
#ifdef WIN
|
|
if (FPure(IsZoomed(vwig.hwndApp)) == FPure(lw))
|
|
break;
|
|
return FPure(ShowWindow(vwig.hwndApp,
|
|
lw ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL));
|
|
#else //!WIN
|
|
// REVIEW shonk: Mac: implement
|
|
return FPure(lw);
|
|
#endif //!WIN
|
|
|
|
case kpridToolTipDelay:
|
|
lw = LwBound(lw, 0, LwMulDiv(klwMax, kdtimSecond, kdtsSecond));
|
|
_dtsToolTip = LwMulDiv(lw, kdtsSecond, kdtimSecond);
|
|
ResetToolTip();
|
|
break;
|
|
|
|
default:
|
|
return _FSetProp(prid, lw);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the current value of the given property.
|
|
***************************************************************************/
|
|
bool APPB::FGetProp(long prid, long *plw)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(plw);
|
|
PROP prop;
|
|
|
|
TrashVar(plw);
|
|
switch (prid)
|
|
{
|
|
case kpridFullScreen:
|
|
*plw = _fFullScreen;
|
|
break;
|
|
|
|
case kpridMaximized:
|
|
#ifdef WIN
|
|
*plw = FPure(IsZoomed(vwig.hwndApp));
|
|
#else //!WIN
|
|
//REVIEW shonk: Mac: implement
|
|
*plw = fTrue;
|
|
#endif //!WIN
|
|
break;
|
|
|
|
case kpridToolTipDelay:
|
|
*plw = LwMulDivAway(_dtsToolTip, kdtimSecond, kdtsSecond);
|
|
break;
|
|
|
|
default:
|
|
if (!_FFindProp(prid, &prop))
|
|
return fFalse;
|
|
*plw = prop.lw;
|
|
break;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Import some data in the given clip format to a docb. If pv is nil
|
|
(or cb is 0), just return whether we can import the format. Otherwise,
|
|
actually create a document and set *ppdocb to point to it. To delay
|
|
import, we create an empty document and set *pfDelay to true. If
|
|
pfDelay is nil, importing cannot be delayed. If *ppdocb is not nil,
|
|
import into *ppdocb if we can.
|
|
***************************************************************************/
|
|
bool APPB::FImportClip(long clfm, void *pv, long cb, PDOCB *ppdocb,
|
|
bool *pfDelay)
|
|
{
|
|
AssertThis(0);
|
|
AssertPvCb(pv, cb);
|
|
AssertNilOrVarMem(ppdocb);
|
|
AssertNilOrVarMem(pfDelay);
|
|
long cpMac;
|
|
|
|
switch (clfm)
|
|
{
|
|
default:
|
|
return fFalse;
|
|
|
|
// we can only import these types
|
|
case kclfmText:
|
|
break;
|
|
}
|
|
|
|
if (pvNil == pv || 0 == cb || pvNil == ppdocb)
|
|
return fTrue;
|
|
|
|
switch (clfm)
|
|
{
|
|
default:
|
|
Bug("unknown clip format");
|
|
return fFalse;
|
|
|
|
case kclfmText:
|
|
AssertNilOrPo(*ppdocb, 0);
|
|
|
|
if (pvNil == *ppdocb || !(*ppdocb)->FIs(kclsTXTB))
|
|
{
|
|
ReleasePpo(ppdocb);
|
|
if (pvNil == (*ppdocb = TXRD::PtxrdNew()))
|
|
return fFalse;
|
|
}
|
|
|
|
// if we can delay and there is more than 1K of text, delay it
|
|
if (pvNil != pfDelay)
|
|
{
|
|
if (cb > 0x0400)
|
|
{
|
|
*pfDelay = fTrue;
|
|
return fTrue;
|
|
}
|
|
*pfDelay = fFalse;
|
|
}
|
|
|
|
((PTXTB)(*ppdocb))->SuspendUndo();
|
|
cpMac = ((PTXTB)(*ppdocb))->CpMac();
|
|
((PTXTB)(*ppdocb))->FReplaceRgch(pv, cb / size(achar), 0, cpMac - 1);
|
|
((PTXTB)(*ppdocb))->ResumeUndo();
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Push the current modal context and create a new one. This should be
|
|
balanced with a call to PopModal (if successful).
|
|
***************************************************************************/
|
|
bool APPB::FPushModal(PCEX pcex)
|
|
{
|
|
AssertThis(0);
|
|
MODCX modcx;
|
|
PUSAC pusacNew = pvNil;
|
|
|
|
if (pvNil == _pglmodcx &&
|
|
pvNil == (_pglmodcx = GL::PglNew(size(MODCX))))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
modcx.cactLongOp = _cactLongOp;
|
|
modcx.pcex = vpcex;
|
|
modcx.pusac = vpusac;
|
|
modcx.luScale = vpusac->LuScale();
|
|
|
|
if (!_pglmodcx->FPush(&modcx))
|
|
return fFalse;
|
|
|
|
if (pcex != pvNil)
|
|
pcex->AddRef();
|
|
|
|
if (pvNil == pcex && pvNil == (pcex = CEX::PcexNew(20, 20)) ||
|
|
!pcex->FAddCmh(vpappb, kcmhlAppb) ||
|
|
pvNil == (pusacNew = NewObj USAC))
|
|
{
|
|
ReleasePpo(&pcex);
|
|
AssertDo(_pglmodcx->FPop(), 0);
|
|
return fFalse;
|
|
}
|
|
modcx.pcex->Suspend();
|
|
vpcex = pcex;
|
|
pcex->Suspend(fFalse);
|
|
|
|
_cactLongOp = 0;
|
|
vpusac->Scale(0);
|
|
vpusac = pusacNew;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Go into a modal loop and don't return until _fQuit is set or a
|
|
cidEndModal comes to the app. Normally should be bracketed by an
|
|
FPushModal/PopModal pair. Returns false iff the modal terminated
|
|
abnormally (eg, we're quitting).
|
|
***************************************************************************/
|
|
bool APPB::FModalLoop(long *plwRet)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(plwRet);
|
|
bool fRet;
|
|
|
|
_fEndModal = fFalse;
|
|
_cactModal++;
|
|
_Loop();
|
|
_cactModal--;
|
|
fRet = FPure(_fEndModal);
|
|
_fEndModal = fFalse;
|
|
*plwRet = _lwModal;
|
|
|
|
AssertThis(0);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Cause the topmost modal loop to terminate (next time through) with the
|
|
given return value.
|
|
***************************************************************************/
|
|
void APPB::EndModal(long lwRet)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_lwModal = lwRet;
|
|
_fEndModal = fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Pop the topmost modal context.
|
|
***************************************************************************/
|
|
void APPB::PopModal(void)
|
|
{
|
|
AssertThis(0);
|
|
MODCX modcx;
|
|
|
|
if (pvNil == _pglmodcx || !_pglmodcx->FPop(&modcx))
|
|
{
|
|
Bug("Unbalanced call to PopModal");
|
|
return;
|
|
}
|
|
|
|
SwapVars(&vpcex, &modcx.pcex);
|
|
SwapVars(&vpusac, &modcx.pusac);
|
|
modcx.pcex->Suspend();
|
|
vpcex->Suspend(fFalse);
|
|
vpusac->Scale(modcx.luScale);
|
|
_cactLongOp = modcx.cactLongOp;
|
|
|
|
ReleasePpo(&modcx.pcex);
|
|
ReleasePpo(&modcx.pusac);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
End the topmost modal loop.
|
|
***************************************************************************/
|
|
bool APPB::FCmdEndModal(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
if (_cactModal <= 0)
|
|
return fFalse;
|
|
|
|
EndModal(pcmd->rglw[0]);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handle any bad modal commands. Default is to put the command in the
|
|
next modal context's CEX.
|
|
***************************************************************************/
|
|
void APPB::BadModalCmd(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pcmd, 0);
|
|
long imodcx;
|
|
MODCX modcx;
|
|
|
|
if (pvNil == _pglmodcx || 0 >= (imodcx = _pglmodcx->IvMac()))
|
|
{
|
|
Bug("Don't know what to do with the modal command");
|
|
return;
|
|
}
|
|
|
|
_pglmodcx->Get(--imodcx, &modcx);
|
|
if (pvNil != pcmd->pgg)
|
|
pcmd->pgg->AddRef();
|
|
modcx.pcex->EnqueueCmd(pcmd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Ask the user if they want to save changes to the given doc.
|
|
***************************************************************************/
|
|
bool APPB::TQuerySaveDoc(PDOCB pdocb, bool fForce)
|
|
{
|
|
AssertThis(0);
|
|
STN stn;
|
|
STN stnName;
|
|
|
|
pdocb->GetName(&stnName);
|
|
stn.FFormatSz(PszLit("Save changes to \"%s\" before closing?"), &stnName);
|
|
return vpappb->TGiveAlertSz(stn.Psz(), fForce ? bkYesNo : bkYesNoCancel,
|
|
cokQuestion);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return whether we should allow a screen saver to come up. Defaults
|
|
to returning true.
|
|
***************************************************************************/
|
|
bool APPB::FAllowScreenSaver(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a APPB.
|
|
***************************************************************************/
|
|
void APPB::AssertValid(ulong grf)
|
|
{
|
|
APPB_PAR::AssertValid(0);
|
|
|
|
AssertNilOrPo(_pgptOff, 0);
|
|
AssertNilOrPo(_pcurs, 0);
|
|
AssertNilOrPo(_pcursWait, 0);
|
|
AssertNilOrPo(_pglclr, 0);
|
|
AssertNilOrPo(_pglprop, 0);
|
|
AssertNilOrPo(_pglmkrgn, 0);
|
|
AssertNilOrPo(_pglmodcx, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Registers memory for frame specific memory (command dispatcher, menu
|
|
bar, screen gobs, etc).
|
|
***************************************************************************/
|
|
void APPB::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
APPB_PAR::MarkMem();
|
|
MarkMemObj(vpcex);
|
|
MarkMemObj(vpmubCur);
|
|
MarkMemObj(&vntl);
|
|
MarkMemObj(vpclip);
|
|
MarkMemObj(vpsndm);
|
|
|
|
MarkMemObj(_pgptOff);
|
|
MarkMemObj(_pcurs);
|
|
MarkMemObj(_pcursWait);
|
|
MarkMemObj(_pglclr);
|
|
MarkMemObj(_pglprop);
|
|
|
|
if (pvNil != _pglmkrgn)
|
|
{
|
|
long imkrgn;
|
|
MKRGN mkrgn;
|
|
|
|
MarkMemObj(_pglmkrgn);
|
|
for (imkrgn = _pglmkrgn->IvMac(); imkrgn-- > 0; )
|
|
{
|
|
_pglmkrgn->Get(imkrgn, &mkrgn);
|
|
MarkMemObj(mkrgn.pregn);
|
|
}
|
|
}
|
|
|
|
if (pvNil != _pglmodcx)
|
|
{
|
|
long imodcx;
|
|
MODCX modcx;
|
|
|
|
MarkMemObj(_pglmodcx);
|
|
for (imodcx = _pglmodcx->IvMac(); imodcx-- > 0; )
|
|
{
|
|
_pglmodcx->Get(imodcx, &modcx);
|
|
MarkMemObj(modcx.pcex);
|
|
MarkMemObj(modcx.pusac);
|
|
}
|
|
}
|
|
|
|
GPT::MarkStaticMem();
|
|
CLOK::MarkAllCloks();
|
|
if ((pgob = GOB::PgobScreen()) != pvNil)
|
|
pgob->MarkGobTree();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Assert proc - just calls the app's AssertProc.
|
|
***************************************************************************/
|
|
bool FAssertProc(PSZS pszsFile, long lwLine, PSZS pszsMsg, void *pv, long cb)
|
|
{
|
|
if (vpappb == pvNil)
|
|
return fTrue;
|
|
return vpappb->FAssertProcApp(pszsFile, lwLine, pszsMsg, pv, cb);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Warning reporting proc.
|
|
***************************************************************************/
|
|
void WarnProc(PSZS pszsFile, long lwLine, PSZS pszsMsg)
|
|
{
|
|
if (vpappb == pvNil)
|
|
Debugger();
|
|
else
|
|
vpappb->WarnProcApp(pszsFile, lwLine, pszsMsg);
|
|
}
|
|
|
|
|
|
static MUTX _mutxWarn;
|
|
|
|
/***************************************************************************
|
|
Default framework warning proc.
|
|
***************************************************************************/
|
|
void APPB::WarnProcApp(PSZS pszsFile, long lwLine, PSZS pszsMsg)
|
|
{
|
|
static PFIL _pfilWarn;
|
|
static bool _fInWarn;
|
|
static FP _fpCur;
|
|
STN stn;
|
|
STN stnFile;
|
|
STN stnMsg;
|
|
|
|
if (_fInWarn)
|
|
return;
|
|
|
|
_mutxWarn.Enter();
|
|
|
|
_fInWarn = fTrue;
|
|
if (pvNil == _pfilWarn)
|
|
{
|
|
FNI fni;
|
|
FTG ftg;
|
|
|
|
//put the warning file at the root of the drive that temp files go on
|
|
if (!fni.FGetTemp() || !fni.FSetLeaf(pvNil, kftgDir))
|
|
goto LDone;
|
|
while (fni.FUpDir(pvNil, ffniMoveToDir))
|
|
;
|
|
|
|
stn = PszLit("_Frame_W");
|
|
if (!fni.FSetLeaf(&stn, kftgText))
|
|
goto LDone;
|
|
|
|
ftg = FIL::vftgCreator;
|
|
FIL::vftgCreator = 'ttxt';
|
|
_pfilWarn = FIL::PfilCreate(&fni);
|
|
FIL::vftgCreator = ftg;
|
|
if (pvNil == _pfilWarn)
|
|
goto LDone;
|
|
}
|
|
stnFile.SetSzs(pszsFile);
|
|
stnMsg.SetSzs(pszsMsg);
|
|
|
|
stn.FFormatSz(PszLit("%s(%d): %s\xD") Win(PszLit("\xA")), &stnFile,
|
|
lwLine, &stnMsg);
|
|
Win( OutputDebugString(stn.Psz()); )
|
|
_pfilWarn->FWriteRgbSeq(stn.Prgch(), stn.Cch(), &_fpCur);
|
|
|
|
LDone:
|
|
_fInWarn = fFalse;
|
|
_mutxWarn.Leave();
|
|
}
|
|
#endif //DEBUG
|
|
|