Files
scummvm-cursorfix/engines/bagel/hodjnpodj/hnplibs/rules.cpp
2026-02-02 04:50:13 +01:00

1445 lines
42 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bagel/hodjnpodj/hnplibs/stdafx.h"
#include "bagel/hodjnpodj/hnplibs/rules.h"
#include "bagel/hodjnpodj/hnplibs/button.h"
#include "bagel/hodjnpodj/globals.h"
#include "bagel/boflib/sound.h"
//
namespace Bagel {
namespace HodjNPodj {
#define BUILD_FOR_DLL false
#define COLOR_BUTTONS true
#define SCROLL_PIECES 6 // number of mid-scroll segments
#define SCROLL_SPEC ".\\art\\lscroll.bmp" // path for scroll DIB on disk
#define SCROLL_TOP_SPEC ".\\art\\lscrollt.bmp" // path for scroll's top section DIB on disk
#define SCROLL_BOT_SPEC ".\\art\\lscrollb.bmp" // path for scroll's bottom section DIB on disk
#define SCROLL_MID_SPEC ".\\art\\lscrollm.bmp" // path for scroll's middle section DIB on disk
#define BUTTON_DY 15 // offset for Okay button from scroll base
#define SCROLL_STRIP_WIDTH 10 // width of scroll middle to reveal per interval
#define SCROLL_STRIP_DELAY 1000 // delay to wait after each partial scroll unfurling
#define TEXT_BUFFER_SIZE 1024 // # characters in the text input buffer
#define TEXT_LEFT_MARGIN 55 // left margin offset for display of text
#define TEXT_TOP_MARGIN 5 // top margin offset for display of text
#define TEXT_BOTTOM_MARGIN 20 // bottom margin offset for display of text
#define TEXT_WIDTH 435 // width of text display area
#define TEXT_MORE_DX 120 // offset of "more" indicator from right margin
#define TEXT_MORE_DY 10 // offset of "more" indicator bottom of scroll
#define MORE_TEXT_BLURB "[ More ]" // actual text to display for "more" indicator
#define MORE_TEXT_LENGTH 8 // # characters in "more" indicator string
#define TEXT_NEWLINE '\\' // character that indicates enforced line break
LRESULT PrefHookProc(int, WPARAM, LPARAM); // keyboard hook procedure definition
static FARPROC pKbdHook = nullptr; // pointer to hook procedure
static HHOOK hKbdHook = nullptr; // handle for hook procedure
static CRules *pRulesDialog = nullptr; // pointer to our rules dialog box
static CWnd *pParentWnd = nullptr; // parent window pointer
static CColorButton *pOKButton = nullptr; // OKAY button on scroll
static CDibDoc *pScrollTopDIB = nullptr, // DIB for scroll top section
*pScrollMidDIB = nullptr, // DIB for scroll mid section
*pScrollBotDIB = nullptr; // DIB for scroll bottom section
static CBitmap *pScrollTopBitmap = nullptr, // bitmap for scroll top section
*pScrollTopBitmapOld = nullptr, // bitmap previously mapped to top section context
*pScrollMidBitmap = nullptr, // bitmap for scroll mid section
*pScrollMidBitmapOld = nullptr, // bitmap previously mapped to mid section context
*pScrollBotBitmap = nullptr, // bitmap for scroll bottom section
*pScrollBotBitmapOld = nullptr; // bitmap previously mapped to bottom section context
static CBitmap *pScrollTopMask = nullptr, // mask for scroll top section bitmap
*pScrollTopMaskOld = nullptr, // bitmap previously mapped to top mask context
*pScrollMidMask = nullptr, // mask for scroll mid section bitmap
*pScrollMidMaskOld = nullptr, // bitmap previously mapped to mid mask context
*pScrollBotMask = nullptr, // mask for scroll bottom section bitmap
*pScrollBotMaskOld = nullptr; // bitmap previously mapped to bottom mask context
static CBitmap *pScrollBitmap = nullptr, // bitmap for an entirely blank scroll
*pScrollBitmapOld = nullptr, // bitmap previously mapped to the scroll context
*pBackgroundBitmap = nullptr, // bitmap containing the background for the scroll
*pBackgroundBitmapOld = nullptr, // bitmap previously mapped to the background context
*pWorkBitmap = nullptr, // bitmap containing the work area for the scroll
*pWorkBitmapOld = nullptr; // bitmap previously mapped to the work area context
static CPalette *pScrollPalette = nullptr, // palette used for the scroll
*pScrollPalOld = nullptr, // previous palette mapped to scroll context
*pBackgroundPalOld = nullptr, // previous palette mapped to background context
*pScrollTopPalOld = nullptr, // previous palette mapped to top context
*pScrollMidPalOld = nullptr, // previous palette mapped to middle context
*pScrollBotPalOld = nullptr, // previous palette mapped to bottom context
*pScrollTopMaskPalOld = nullptr, // previous palette mapped to top mask context
*pScrollMidMaskPalOld = nullptr, // previous palette mapped to middle mask context
*pScrollBotMaskPalOld = nullptr, // previous palette mapped to bottom mask context
*pWorkPalOld = nullptr; // previous palette mapped to work area context
static CDC *pScrollDC = nullptr, // device context for the scroll bitmap
*pBackgroundDC = nullptr, // device context for the background bitmap
*pScrollTopDC = nullptr, // device context for the top section bitmap
*pScrollMidDC = nullptr, // device context for the middle section bitmap
*pScrollBotDC = nullptr, // device context for the bottom section bitmap
*pScrollTopMaskDC = nullptr, // device context for the top mask bitmap
*pScrollMidMaskDC = nullptr, // device context for the middle section bitmap
*pScrollBotMaskDC = nullptr, // device context for the bottom section bitmap
*pWorkDC = nullptr; // device context for the work area bitmap
static CFont *pFont = nullptr; // font to use for displaying rules text
static char chPathName[128]; // buffer to hold path name of the rules file
static CFile *pHelpFile = nullptr; // the rules file
static uint32 nHelpFileSize = 0; // size of rules file
static int nHelpPage = 0; // current page of rules text
static uint32 dwHelpPagePosition[100]; // position of each page (# chars from file start)
static bool dwHelpPageEOL[100]; // whether page starts with enforced line break
static bool bHelpEOF = false; // whether end-of-file has been reached
static int tabstop = 20 + TEXT_LEFT_MARGIN;// pixels per tab stop
static const char *pSoundPath = nullptr; // path spec for rules narration
static CSound *pNarrative = nullptr; // sound object
static bool first_time = true;
static bool bActiveWindow = false; // whether our window is active
static bool bBruteForce = false; // whether we can be clever
/////////////////////////////////////////////////////////////////////////////
// CRules dialog
BEGIN_MESSAGE_MAP(CRules, CDialog)
//{{AFX_MSG_MAP(CRules)
ON_WM_CREATE()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_SHOWWINDOW()
ON_WM_SIZE()
ON_WM_MOUSEMOVE()
ON_WM_SETCURSOR()
ON_WM_NCMOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_DESTROY()
ON_WM_ACTIVATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
bool CRules::SetupKeyboardHook() {
HINSTANCE hInst;
pRulesDialog = this; // retain pointer to our dialog box
hInst = AfxGetInstanceHandle(); // get our application instance
hKbdHook = SetWindowsHookEx(WH_KEYBOARD, PrefHookProc, hInst, GetCurrentTask());
if (hKbdHook == nullptr) // plug in our keyboard hook
return false;
return true;
}
void CRules::RemoveKeyboardHook() {
if (m_bKeyboardHook) {
UnhookWindowsHookEx(hKbdHook); // unhook our keyboard procedure
FreeProcInstance(pKbdHook); // release our procedure pointer
}
pKbdHook = nullptr;
pRulesDialog = nullptr;
hKbdHook = nullptr;
m_bKeyboardHook = false;
}
LRESULT PrefHookProc(int code, WPARAM wParam, LPARAM lParam) {
if (code < 0) // required to punt to next hook
return (CallNextHookEx((HHOOK) pKbdHook, code, wParam, lParam));
if (lParam & 0xA0000000) // ignore ALT and key release
return false;
if (bActiveWindow)
switch (wParam) { // process only the keys we are looking for
case VK_UP: // ... letting the default dialog procedure
case VK_NUMPAD8: // ... deal with all the rest
case VK_PRIOR: // go to previous page of text
if (nHelpPage > 0)
(*pRulesDialog).UpdateScroll(nHelpPage - 1);
return true;
case VK_DOWN: // go to next page of text
case VK_NUMPAD2:
case VK_NEXT:
if (!bHelpEOF)
(*pRulesDialog).UpdateScroll(nHelpPage + 1);
return true;
case VK_HOME: // go to first page of text
if (nHelpPage > 0)
(*pRulesDialog).UpdateScroll(0);
return true;
case VK_END: // go to last page of text
while (!bHelpEOF) {
(*pRulesDialog).UpdateScroll(nHelpPage + 1);
}
return true;
}
return false;
}
CRules::CRules(CWnd *pParent, const char *pszPathName,
CPalette *pPalette, const char *pszSoundPath)
: CDialog(CRules::IDD, pParent) {
Common::strcpy_s(chPathName, pszPathName); // retain path to rules file on disk
pScrollPalette = pPalette; // retain palette to be used
pParentWnd = nullptr; // retain parent window pointer
pSoundPath = pszSoundPath; // retain path to sound file on disk
//{{AFX_DATA_INIT(CRules)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
void CRules::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CRules)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
/////////////////////////////////////////////////////////////////////////////
// CRules message handlers
void CRules::OnOK() {
ClearDialogImage();
CDialog::EndDialog(IDC_RULES_OKAY);
}
void CRules::OnCancel() {
ClearDialogImage();
CDialog::OnCancel();
}
void CRules::OnDestroy() {
bool bUpdateNeeded;
HCURSOR hNewCursor = nullptr;
CWinApp *pMyApp;
pMyApp = AfxGetApp();
hNewCursor = (*pMyApp).LoadStandardCursor(IDC_ARROW);
//if (hNewCursor != nullptr);
SetCursor(hNewCursor);
// Free the objects
delete pNarrative;
pNarrative = nullptr;
delete pOKButton;
pOKButton = nullptr;
delete pFont;
pFont = nullptr;
(*pHelpFile).Close(); // close and release the rules file
delete pHelpFile;
pHelpFile = nullptr;
if (pBackgroundBitmap != nullptr) {
bUpdateNeeded = (*pParentWnd).GetUpdateRect(nullptr, false);
if (bUpdateNeeded)
(*pParentWnd).ValidateRect(nullptr);
}
if (!bBruteForce) {
ReleaseCompatibleContext(pWorkDC, pWorkBitmap, pWorkBitmapOld, pWorkPalOld);
ReleaseCompatibleContext(pScrollDC, pScrollBitmap, pScrollBitmapOld, pScrollPalOld);
ReleaseCompatibleContext(pBackgroundDC, pBackgroundBitmap, pBackgroundBitmapOld, pBackgroundPalOld);
ReleaseCompatibleContext(pScrollMidDC, pScrollMidBitmap, pScrollMidBitmapOld, pScrollMidPalOld);
ReleaseCompatibleContext(pScrollMidMaskDC, pScrollMidMask, pScrollMidMaskOld, pScrollMidMaskPalOld);
}
if (m_bKeyboardHook) // remove keyboard hook, if present
RemoveKeyboardHook();
CDialog::OnDestroy();
}
bool CRules::OnInitDialog() {
CWnd *pButton; // pointer to the OKAY button
CRect myRect; // rectangle that holds the button location
int x, y, dx, dy; // used for calculating positioning info
bool bSuccess;
CDialog::OnInitDialog(); // do basic dialog initialization
if (pParentWnd == nullptr) // get our parent window
pParentWnd = ((CWnd *) this)->GetParent(); // ... as passed to us or inquired about
(*pParentWnd).GetWindowRect(&myRect);
x = myRect.left + (((myRect.right - myRect.left) - ScrollRect.right) >> 1);
y = myRect.top + (((myRect.bottom - myRect.top) - ScrollRect.bottom) >> 1);
MoveWindow(x, y, ScrollRect.right, ScrollRect.bottom); // center the dialog box on the parent
pButton = GetDlgItem((int) GetDefID()); // get the window for the okay button
ASSERT(pButton != nullptr); // ... and verify we have it
(*pButton).GetWindowRect(&myRect); // get the button's position and size
dx = myRect.right - myRect.left; // calculate where to place the button
x = (ScrollRect.right - dx) >> 1; // ... centered at the bottom edge
dy = myRect.bottom - myRect.top;
y = ScrollRect.bottom - dy - BUTTON_DY;
(*pButton).MoveWindow(x, y, dx, dy); // reposition the button
OkayRect.SetRect(x, y, x + dx, y + dy);
#if COLOR_BUTTONS
pOKButton = new CColorButton(); // build a color QUIT button to let us exit
ASSERT(pOKButton != nullptr);
(*pOKButton).SetPalette(pScrollPalette); // set the palette to use
bSuccess = (*pOKButton).SetControl((int) GetDefID(), this); // tie to the dialog control
ASSERT(bSuccess);
#endif
if (pSoundPath != nullptr)
pNarrative = new CSound(this, pSoundPath, SOUND_WAVE | SOUND_ASYNCH | SOUND_AUTODELETE);
m_bKeyboardHook = SetupKeyboardHook(); // establish keyboard hook
first_time = true;
return true; // return true unless focused on a control
}
void CRules::OnActivate(unsigned int nState, CWnd *pWndOther, bool bMinimized) {
bool bUpdateNeeded;
switch (nState) {
case WA_INACTIVE:
bActiveWindow = false;
break;
case WA_ACTIVE:
case WA_CLICKACTIVE:
bActiveWindow = true;
bUpdateNeeded = GetUpdateRect(nullptr, false);
if (bUpdateNeeded)
InvalidateRect(nullptr, false);
}
}
void CRules::OnPaint() {
CPaintDC dc(this); // device context for painting
CPalette *pPalOld = nullptr;
CDibDoc *pDibDoc;
ShowWaitCursor(); // put up the hourglass cursor
if (pScrollPalette != nullptr) { // map in our palette
pPalOld = dc.SelectPalette(pScrollPalette, false);
dc.RealizePalette(); // .. and make the system use it
}
if (first_time) { // unfurl scroll visually
first_time = false; // ... but only if this is the first time
UnfurlScroll(&dc); // ... we are updating the screen
ReleaseCompatibleContext(pScrollTopDC, pScrollTopBitmap, pScrollTopBitmapOld, pScrollTopPalOld);
ReleaseCompatibleContext(pScrollBotDC, pScrollBotBitmap, pScrollBotBitmapOld, pScrollBotPalOld);
ReleaseCompatibleContext(pScrollTopMaskDC, pScrollTopMask, pScrollTopMaskOld, pScrollTopMaskPalOld);
ReleaseCompatibleContext(pScrollBotMaskDC, pScrollBotMask, pScrollBotMaskOld, pScrollBotMaskPalOld);
if (pNarrative != nullptr)
// Play the narration
(*pNarrative).play();
} else if (bBruteForce) { // need to paint directly to screen
pDibDoc = LoadScrollDIB(SCROLL_SPEC, nullptr);
if (pDibDoc != nullptr) {
PaintScrollDIB(&dc, pDibDoc);
delete pDibDoc;
}
WritePage(&dc, nHelpPage);
UpdateMore(&dc); // update the "more" indicator
} else
dc.BitBlt( // update the screen with just the
0, 0, // ... current page of text
ScrollRect.right,
ScrollRect.bottom,
pScrollDC,
0, 0,
SRCCOPY);
if (pScrollPalette != nullptr) // map out our palette
dc.SelectPalette(pPalOld, false);
DoArrowCursor(); // return to an arrow cursor
}
bool CRules::OnEraseBkgnd(CDC *pDC) {
return true; // do not automatically erase background to white
}
void CRules::ClearDialogImage() {
delete pOKButton;
pOKButton = nullptr;
if (pBackgroundBitmap != nullptr)
ValidateRect(nullptr);
RefreshBackground();
}
void CRules::RefreshBackground() {
CDC *pDC;
CPalette *pPalOld = nullptr;
if (pBackgroundBitmap == nullptr)
return;
pDC = GetDC(); // get a context for our window
if (pScrollPalette != nullptr) { // map in our palette
pPalOld = (*pDC).SelectPalette(pScrollPalette, false);
(*pDC).RealizePalette(); // .. and make the system use it
}
(*pDC).BitBlt( // repaint the background as it was
0,
0,
ScrollRect.right,
ScrollRect.bottom,
pBackgroundDC,
0,
0,
SRCCOPY);
if (pScrollPalette != nullptr) // map out our palette
(*pDC).SelectPalette(pPalOld, false);
ReleaseDC(pDC); // release the context
}
void CRules::UnfurlScroll(CDC *pDC) {
int i, j, k, h;
int scroll_delta;
CRect dstRect, dst2Rect;
CDibDoc *pDibDoc;
if (!bBruteForce)
bBruteForce = !CreateWorkAreas(pDC); // create our work areas
if (bBruteForce) {
ReleaseCompatibleContext(pWorkDC, pWorkBitmap, pWorkBitmapOld, pWorkPalOld);
ReleaseCompatibleContext(pScrollDC, pScrollBitmap, pScrollBitmapOld, pScrollPalOld);
ReleaseCompatibleContext(pBackgroundDC, pBackgroundBitmap, pBackgroundBitmapOld, pBackgroundPalOld);
ReleaseCompatibleContext(pScrollMidDC, pScrollMidBitmap, pScrollMidBitmapOld, pScrollMidPalOld);
ReleaseCompatibleContext(pScrollMidMaskDC, pScrollMidMask, pScrollMidMaskOld, pScrollMidMaskPalOld);
pDibDoc = LoadScrollDIB(SCROLL_SPEC, nullptr);
if (pDibDoc != nullptr) {
PaintScrollDIB(pDC, pDibDoc);
delete pDibDoc;
}
WritePage(pDC, 0);
UpdateMore(pDC);
ScrollTopCurlRect = ScrollTopRect;
ScrollBotCurlRect = ScrollBotRect;
ScrollBotCurlRect.top += ScrollTopCurlRect.bottom + ScrollMidRect.bottom * SCROLL_PIECES;
ScrollBotCurlRect.bottom += ScrollTopCurlRect.bottom + ScrollMidRect.bottom * SCROLL_PIECES;
return;
}
(*pBackgroundDC).BitBlt( // save the entire background
0, 0,
ScrollRect.right,
ScrollRect.bottom,
pDC,
0, 0,
SRCCOPY);
(*pScrollDC).BitBlt( // copy background to scroll context
0, 0, // ... so we can construct the scroll
ScrollRect.right, // ... on top of it in a masked manner
ScrollRect.bottom,
pDC,
0, 0,
SRCCOPY);
// setup the top part of the scroll
ScrollTopCurlRect = ScrollTopRect; // record where we're putting the top curl
(*pScrollDC).BitBlt( // mask out where the top curl goes
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
pScrollTopMaskDC,
0, 0,
SRCAND);
(*pScrollTopMaskDC).BitBlt( // invert the top curl mask
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
nullptr,
0, 0,
DSTINVERT);
(*pScrollTopDC).BitBlt( // remove transparent area from top curl
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
pScrollTopMaskDC,
0, 0,
SRCAND);
(*pScrollTopMaskDC).BitBlt( // restore top curl mask
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
nullptr,
0, 0,
DSTINVERT);
(*pScrollDC).BitBlt( // paint top curl into the scroll
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
pScrollTopDC,
0, 0,
SRCPAINT);
// setup the middle portion of the scroll
dstRect = ScrollMidRect; // setup initial destination for a
dstRect.top += ScrollTopRect.bottom; // ... mid section strip of the scroll
dstRect.bottom += ScrollTopRect.bottom;
(*pScrollMidMaskDC).BitBlt( // invert the mid section mask
0, 0,
ScrollMidRect.right,
ScrollMidRect.bottom,
nullptr,
0, 0,
DSTINVERT);
(*pScrollMidDC).BitBlt( // mask out transparent part of mid section
0, 0,
ScrollMidRect.right,
ScrollMidRect.bottom,
pScrollMidMaskDC,
0, 0,
SRCAND);
(*pScrollMidMaskDC).BitBlt( // reset the mid section mask
0, 0,
ScrollMidRect.right,
ScrollMidRect.bottom,
nullptr,
0, 0,
DSTINVERT);
for (i = 0; i < SCROLL_PIECES; i++) { // build the scroll's mid section srip-wise
(*pScrollDC).BitBlt(
dstRect.left, // mask out where the image will go
dstRect.top + ScrollMidRect.bottom * i,
ScrollMidRect.right,
ScrollMidRect.bottom,
pScrollMidMaskDC,
0, 0,
SRCAND);
(*pScrollDC).BitBlt( // paint in the mid section image
dstRect.left,
dstRect.top + ScrollMidRect.bottom * i,
ScrollMidRect.right,
ScrollMidRect.bottom,
pScrollMidDC,
0, 0,
SRCPAINT);
}
// setup the bottom part of the scroll
dstRect.top += ScrollMidRect.bottom * SCROLL_PIECES; // record where the bottom curl goes
dstRect.bottom = dstRect.top + ScrollBotRect.bottom;
ScrollBotCurlRect = dstRect;
(*pScrollDC).BitBlt( // mask out where the bottom curl image goes
dstRect.left,
dstRect.top,
ScrollBotRect.right,
ScrollBotRect.bottom,
pScrollBotMaskDC,
0, 0,
SRCAND);
(*pScrollBotMaskDC).BitBlt( // invert the bottom curl mask
0, 0,
ScrollBotRect.right,
ScrollBotRect.bottom,
nullptr,
0, 0,
DSTINVERT);
(*pScrollBotDC).BitBlt( // mask out transparent part of curl
0, 0,
ScrollBotRect.right,
ScrollBotRect.bottom,
pScrollBotMaskDC,
0, 0,
SRCAND);
(*pScrollBotMaskDC).BitBlt( // reset the mask
0, 0,
ScrollBotRect.right,
ScrollBotRect.bottom,
nullptr,
0, 0,
DSTINVERT);
(*pScrollDC).BitBlt( // paint in the bottom curl
dstRect.left,
dstRect.top,
ScrollBotRect.right,
ScrollBotRect.bottom,
pScrollBotDC,
0, 0,
SRCPAINT);
// write the first page of text
WritePage(pScrollDC, 0); // construct first page of text
// now display what we built, making the scroll look like it is unfurlinf from top
// ... and bottom, by increasing the display of the middle section and moving the
// ... top and bottom curls further away
j = (ScrollMidRect.bottom * SCROLL_PIECES) / SCROLL_STRIP_WIDTH; // number of times to update display
k = (ScrollMidRect.bottom * SCROLL_PIECES) % SCROLL_STRIP_WIDTH; // residual not displayed after main loop
h = j & 0x1; // whether there is a dangling odd pixel row
j = (j >> 1) + h + ((k > 0) ? 1 : 0); // revise loop count based on residual & dangle
scroll_delta = SCROLL_STRIP_WIDTH; // current width of the strip to display
dstRect.left = 0;
dstRect.right = ScrollRect.right;
dstRect.top = (ScrollRect.bottom - (ScrollTopRect.bottom + ScrollBotRect.bottom)) >> 1;
dstRect.bottom = dstRect.top + ScrollTopRect.bottom + ScrollBotRect.bottom;
for (i = 0; i <= j; i++) { // do each segment in turn
dst2Rect = dstRect;
(*pWorkDC).BitBlt( // splat in the background for top curl
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
pBackgroundDC,
dst2Rect.left,
dst2Rect.top,
SRCCOPY);
(*pWorkDC).BitBlt( // mask out where the top curl goes
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
pScrollTopMaskDC,
0, 0,
SRCAND);
(*pWorkDC).BitBlt( // insert the top curl background
0, 0,
ScrollTopRect.right,
ScrollTopRect.bottom,
pScrollTopDC,
0, 0,
SRCPAINT);
if (i == 0) { // if first time, just paint curl to display
(*pDC).BitBlt(
dst2Rect.left,
dst2Rect.top,
ScrollTopRect.right,
ScrollTopRect.bottom,
pWorkDC,
0, 0,
SRCCOPY);
} else {
(*pWorkDC).BitBlt( // ... otherwise paint in a new piece of
dst2Rect.left, // ... the middle section
ScrollTopRect.bottom,
dst2Rect.right,
scroll_delta,
pScrollDC,
dst2Rect.left,
dst2Rect.top + ScrollTopRect.bottom,
SRCCOPY);
(*pDC).BitBlt( // ... then paint it and the curl, thus
dst2Rect.left, // ... causing the top to seemingly unfurl
dst2Rect.top, // ... by one more strip
ScrollTopRect.right,
ScrollTopRect.bottom + scroll_delta,
pWorkDC,
0, 0,
SRCCOPY);
}
dst2Rect.top = dstRect.bottom - ScrollBotRect.bottom - scroll_delta;
dst2Rect.bottom = dstRect.bottom;
if ((i == 0) || ((i == 1) && (h == 1))) { // if first time or doing last iteration
(*pWorkDC).BitBlt( // ... just paint in the bottom curl
0, 0,
dst2Rect.right,
scroll_delta,
pDC,
dst2Rect.left,
dst2Rect.top,
SRCCOPY);
} else { //
(*pWorkDC).BitBlt(
0, 0,
dst2Rect.right,
scroll_delta,
pScrollDC,
dst2Rect.left,
dst2Rect.top,
SRCCOPY);
}
(*pWorkDC).BitBlt(
0,
scroll_delta,
ScrollBotRect.right,
ScrollBotRect.bottom,
pBackgroundDC,
ScrollBotRect.left,
dst2Rect.top + scroll_delta,
SRCCOPY);
(*pWorkDC).BitBlt(
0,
scroll_delta,
ScrollBotRect.right,
ScrollBotRect.bottom,
pScrollBotMaskDC,
0, 0,
SRCAND);
(*pWorkDC).BitBlt(
0,
scroll_delta,
ScrollBotRect.right,
ScrollBotRect.bottom,
pScrollBotDC,
0, 0,
SRCPAINT);
(*pDC).BitBlt(
ScrollBotRect.left,
dst2Rect.top,
ScrollBotRect.right,
ScrollBotRect.bottom + scroll_delta,
pWorkDC,
0, 0,
SRCCOPY);
if ((j == 0) && (h == 1)) {
dstRect.top -= scroll_delta >> 1;
dstRect.bottom += scroll_delta >> 1;
} else {
dstRect.top -= scroll_delta;
dstRect.bottom += scroll_delta;
}
if ((i == j - 1) && (k != 0)) {
scroll_delta = k >> 1;
dstRect.top += SCROLL_STRIP_WIDTH - scroll_delta;
dstRect.bottom -= SCROLL_STRIP_WIDTH - scroll_delta;
}
}
UpdateMore(pDC);
}
void CRules::UpdateScroll(int nPage) {
int i;
CRect dstRect;
CDC *pDC;
CPalette *pPalOld = nullptr;
if (bBruteForce) {
nHelpPage = nPage;
InvalidateRect(nullptr, false);
return;
}
pDC = GetDC();
if (pScrollPalette != nullptr) { // map in our palette
pPalOld = (*pDC).SelectPalette(pScrollPalette, false);
(*pDC).RealizePalette(); // .. and make the system use it
}
dstRect = ScrollMidRect; // setup initial destination for blts
dstRect.top += ScrollTopRect.bottom;
dstRect.bottom += ScrollTopRect.bottom;
for (i = 0; i < SCROLL_PIECES; i++) { // repaint the scroll midsection artwork
(*pScrollDC).BitBlt( // ... mask out where artwork goes
dstRect.left,
dstRect.top + ScrollMidRect.bottom * i,
ScrollMidRect.right,
ScrollMidRect.bottom,
pScrollMidMaskDC,
0, 0,
SRCAND);
(*pScrollDC).BitBlt( // ... paint in the scroll artwork
dstRect.left,
dstRect.top + ScrollMidRect.bottom * i,
ScrollMidRect.right,
ScrollMidRect.bottom,
pScrollMidDC,
0, 0,
SRCPAINT);
}
WritePage(pScrollDC, nPage); // construct the new page of text
(*pDC).BitBlt( // paint the result to the sceen
TEXT_LEFT_MARGIN,
ScrollTopRect.bottom + TEXT_TOP_MARGIN,
TEXT_WIDTH,
(SCROLL_PIECES * ScrollMidRect.bottom) - TEXT_TOP_MARGIN - 1,
pScrollDC,
TEXT_LEFT_MARGIN,
ScrollTopRect.bottom + TEXT_TOP_MARGIN,
SRCCOPY);
UpdateMore(pDC); // update the "more" indicator
if (pScrollPalette != nullptr) // map out our palette
(*pDC).SelectPalette(pPalOld, false);
ReleaseDC(pDC);
}
void CRules::WritePage(CDC *pDC, int nPage) {
int i, n, x, y;
unsigned int nCropped = 0;
unsigned int nCount = 0;
char chInBuf[TEXT_BUFFER_SIZE];
CFont *pFontOld = nullptr;
CSize textInfo;
bool bEOL, bEOF;
TEXTMETRIC fontMetrics;
nHelpPage = nPage;
bEOF = bHelpEOF = false;
(*pHelpFile).SeekToBegin();
if ((nHelpPage == 0) && (nPage == 0)) {
dwHelpPagePosition[nHelpPage] = (*pHelpFile).GetPosition();
bEOL = dwHelpPageEOL[nHelpPage] = true;
} else {
(*pHelpFile).Seek(dwHelpPagePosition[nPage], CFile::begin);
bEOL = dwHelpPageEOL[nHelpPage];
}
dwHelpPagePosition[nHelpPage + 1] = dwHelpPagePosition[nHelpPage];
pFontOld = (*pDC).SelectObject(pFont);
(*pDC).GetTextMetrics(&fontMetrics);
(*pDC).SetBkMode(TRANSPARENT);
i = 0;
n = 1;
x = TEXT_LEFT_MARGIN;
y = ScrollTopRect.bottom + TEXT_TOP_MARGIN;
while (true) {
try_again:
if (i >= (int) nCount) {
if (bEOF)
break;
dwHelpPagePosition[nHelpPage + 1] += i + nCropped;
nCount = (*pHelpFile).Read(&chInBuf, TEXT_BUFFER_SIZE);
if (nCount < TEXT_BUFFER_SIZE)
bEOF = true;
i = 0;
n = 1;
nCropped = 0;
}
if (bEOL &&
((chInBuf[i] == '\r') ||
(chInBuf[i] == '\n'))) {
i += 1;
goto try_again;
}
if (!bEOL &&
((chInBuf[i] == ' ') ||
(chInBuf[i] == '\t'))) {
i += 1;
goto try_again;
}
bEOL = false;
while (true) {
if (i + n > (int) nCount) {
n = nCount - i;
if (bEOF)
break;
dwHelpPagePosition[nHelpPage + 1] += i + nCropped;
strncpy(&chInBuf[0], &chInBuf[i], n);
nCount = (*pHelpFile).Read(&chInBuf[n], TEXT_BUFFER_SIZE - n);
nCount += n;
if (nCount < TEXT_BUFFER_SIZE)
bEOF = true;
i = 0;
n = 1;
nCropped = 0;
}
if (chInBuf[i + n - 1] == '\r') {
crop_byte:
Common::copy(&chInBuf[i + n], chInBuf + nCount, &chInBuf[i + n - 1]);
nCount -= 1;
nCropped += 1;
continue;
}
if (chInBuf[i + n - 1] == '\n') {
if ((n > 1) &&
((chInBuf[i + n - 2] == ' ') ||
(chInBuf[i + n - 2] == '-')))
goto crop_byte;
else
chInBuf[i + n - 1] = ' ';
}
if (chInBuf[i + n - 1] == TEXT_NEWLINE) {
bEOL = true;
n -= 1;
break;
}
textInfo = (*pDC).GetTextExtent(&chInBuf[i], n);
if (textInfo.cx > TEXT_WIDTH - TEXT_LEFT_MARGIN) {
if ((chInBuf[i + n - 1] != ' ') &&
(chInBuf[i + n - 1] != '\t'))
while (n > 0) {
n -= 1;
if (strchr(" \t,;:-", chInBuf[i + n - 1]) != nullptr)
break;
}
break;
}
n += 1;
}
// (*pDC).TextOut(x,y,(const char *) &chInBuf[i],n);
(*pDC).TabbedTextOut(x, y, (const char *) &chInBuf[i], n, 1, &tabstop, 0);
if (chInBuf[i + n] == '\r')
i += 2;
else if (chInBuf[i + n] == TEXT_NEWLINE)
i += 1;
i += n;
n = 1;
y += fontMetrics.tmHeight;
if (y - ScrollTopRect.bottom > (SCROLL_PIECES * ScrollMidRect.bottom) - TEXT_BOTTOM_MARGIN)
break;
}
(*pDC).SelectObject(pFontOld);
dwHelpPagePosition[nPage + 1] += i + nCropped;
dwHelpPageEOL[nPage + 1] = bEOL;
while (true) {
if (i >= (int) nCount) {
if (bEOF)
break;
nCount = (*pHelpFile).Read(&chInBuf, TEXT_BUFFER_SIZE);
if (nCount < TEXT_BUFFER_SIZE)
bEOF = true;
i = 0;
n = 1;
}
if (bEOL &&
((chInBuf[i] == '\r') ||
(chInBuf[i] == '\n') ||
(chInBuf[i] == TEXT_NEWLINE))) {
i += 1;
dwHelpPagePosition[nHelpPage + 1] += 1;
} else
break;
}
if (bEOF && (i >= (int) nCount))
bHelpEOF = true;
(*pDC).SelectObject(pFontOld);
}
void CRules::UpdateMore(CDC *pDC) {
int x, y, dx, dy;
CFont *pFontOld = nullptr;
TEXTMETRIC fontMetrics;
CSize textInfo;
pFontOld = (*pDC).SelectObject(pFont);
(*pDC).GetTextMetrics(&fontMetrics);
(*pDC).SetBkMode(TRANSPARENT);
x = ScrollRect.right - TEXT_MORE_DX;
y = ((ScrollTopRect.bottom - fontMetrics.tmHeight) >> 1) +
TEXT_MORE_DY - 3;
textInfo = (*pDC).GetTextExtent(MORE_TEXT_BLURB, MORE_TEXT_LENGTH);
dx = textInfo.cx;
dy = fontMetrics.tmHeight;
if (nHelpPage == 0)
(*pDC).BitBlt(
x,
y,
dx,
dy,
pScrollDC,
x,
y,
SRCCOPY);
else
(*pDC).TextOut(x, y, MORE_TEXT_BLURB, MORE_TEXT_LENGTH);
y = ScrollRect.bottom -
ScrollBotRect.bottom +
((ScrollBotRect.bottom - fontMetrics.tmHeight) >> 1) -
TEXT_MORE_DY;
if (bHelpEOF)
(*pDC).BitBlt(
x,
y,
dx,
dy,
pScrollDC,
x,
y,
SRCCOPY);
else
(*pDC).TextOut(x, y, MORE_TEXT_BLURB, MORE_TEXT_LENGTH);
(*pDC).SelectObject(pFontOld);
}
void CRules::OnShowWindow(bool bShow, unsigned int nStatus) {
CDialog::OnShowWindow(bShow, nStatus);
// TODO: Add your message handler code here
}
void CRules::OnSize(unsigned int nType, int cx, int cy) {
CDialog::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
}
int CRules::OnCreate(LPCREATESTRUCT lpCreateStruct) {
bool bSuccess;
int dxDIB, dyDIB;
pHelpFile = new CFile();
ASSERT(pHelpFile != nullptr);
bSuccess = (*pHelpFile).Open(chPathName, CFile::modeRead | CFile::typeBinary, nullptr);
ASSERT(bSuccess);
if (!bSuccess)
return -1;
nHelpFileSize = (*pHelpFile).GetLength();
AddFontResource("msserif.fon");
pFont = new CFont();
ASSERT(pFont != nullptr);
bSuccess = (*pFont).CreateFont(16, 0, 0, 0, FW_BOLD, 0, 0, 0, 0, OUT_RASTER_PRECIS, 0, PROOF_QUALITY, FF_ROMAN, "MS Sans Serif");
ASSERT(bSuccess);
if ((GetFreeSpace(0) >= (unsigned long) 1000000) &&
(GlobalCompact((unsigned long) 1000000) >= (unsigned long) 750000)) {
pScrollTopDIB = LoadScrollDIB(SCROLL_TOP_SPEC, &ScrollTopRect);
pScrollMidDIB = LoadScrollDIB(SCROLL_MID_SPEC, &ScrollMidRect);
pScrollBotDIB = LoadScrollDIB(SCROLL_BOT_SPEC, &ScrollBotRect);
if ((pScrollTopDIB == nullptr) ||
(pScrollMidDIB == nullptr) ||
(pScrollBotDIB == nullptr))
bBruteForce = true;
else
bBruteForce = false;
if (bBruteForce) {
if (pScrollTopDIB != nullptr)
delete pScrollTopDIB;
if (pScrollMidDIB != nullptr)
delete pScrollMidDIB;
if (pScrollBotDIB != nullptr)
delete pScrollBotDIB;
pScrollTopDIB = nullptr;
pScrollMidDIB = nullptr;
pScrollBotDIB = nullptr;
}
} else
bBruteForce = true;
if (bBruteForce) {
dxDIB = 501;
dyDIB = 395;
ScrollTopRect.SetRect(0, 0, 501, 48);
ScrollMidRect.SetRect(0, 0, 501, 50);
ScrollBotRect.SetRect(0, 0, 501, 47);
} else {
dxDIB = ScrollTopRect.right;
dyDIB = ScrollTopRect.bottom +
(ScrollMidRect.bottom * SCROLL_PIECES) +
ScrollBotRect.bottom;
}
ScrollRect.SetRect(0, 0, dxDIB, dyDIB);
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
bool CRules::CreateWorkAreas(CDC *pDC) {
bool bSuccess;
CRect WorkRect;
pScrollTopBitmap = CreateScrollBitmap(pDC, pScrollTopDIB, pScrollPalette);
delete pScrollTopDIB;
pScrollMidBitmap = CreateScrollBitmap(pDC, pScrollMidDIB, pScrollPalette);
delete pScrollMidDIB;
pScrollBotBitmap = CreateScrollBitmap(pDC, pScrollBotDIB, pScrollPalette);
delete pScrollBotDIB;
if ((pScrollTopBitmap == nullptr) ||
(pScrollMidBitmap == nullptr) ||
(pScrollBotBitmap == nullptr))
return false;
pScrollTopDC = SetupCompatibleContext(pDC, pScrollTopBitmap, pScrollTopBitmapOld, pScrollPalette, pScrollTopPalOld);
pScrollMidDC = SetupCompatibleContext(pDC, pScrollMidBitmap, pScrollMidBitmapOld, pScrollPalette, pScrollMidPalOld);
pScrollBotDC = SetupCompatibleContext(pDC, pScrollBotBitmap, pScrollBotBitmapOld, pScrollPalette, pScrollBotPalOld);
if ((pScrollTopDC == nullptr) ||
(pScrollMidDC == nullptr) ||
(pScrollBotDC == nullptr))
return false;
pScrollBitmap = new CBitmap();
if (pScrollBitmap == nullptr)
return false;
bSuccess = (*pScrollBitmap).CreateCompatibleBitmap(pDC, ScrollRect.right, ScrollRect.bottom);
if (!bSuccess)
return false;
pScrollDC = SetupCompatibleContext(pDC, pScrollBitmap, pScrollBitmapOld, pScrollPalette, pScrollPalOld);
if (pScrollDC == nullptr)
return false;
pBackgroundBitmap = new CBitmap();
if (pBackgroundBitmap == nullptr)
return false;
bSuccess = (*pBackgroundBitmap).CreateCompatibleBitmap(pDC, ScrollRect.right, ScrollRect.bottom);
if (!bSuccess)
return false;
pBackgroundDC = SetupCompatibleContext(pDC, pBackgroundBitmap, pBackgroundBitmapOld, pScrollPalette, pBackgroundPalOld);
if (pBackgroundDC == nullptr)
return false;
WorkRect.UnionRect(&ScrollTopRect, &ScrollMidRect);
WorkRect.UnionRect(&WorkRect, &ScrollBotRect);
WorkRect.bottom += SCROLL_STRIP_WIDTH;
pWorkBitmap = new CBitmap();
if (pWorkBitmap == nullptr)
return false;
bSuccess = (*pWorkBitmap).CreateCompatibleBitmap(pDC, WorkRect.right, WorkRect.bottom);
if (!bSuccess)
return false;
pWorkDC = SetupCompatibleContext(pDC, pWorkBitmap, pWorkBitmapOld, pScrollPalette, pWorkPalOld);
if (pWorkDC == nullptr)
return false;
pScrollTopMask = new CBitmap();
pScrollMidMask = new CBitmap();
pScrollBotMask = new CBitmap();
if ((pScrollTopMask == nullptr) ||
(pScrollMidMask == nullptr) ||
(pScrollBotMask == nullptr))
return false;
pScrollTopMaskDC = SetupMask(pDC, pScrollTopDC, pScrollTopMask, pScrollTopMaskOld, &ScrollTopRect);
pScrollMidMaskDC = SetupMask(pDC, pScrollMidDC, pScrollMidMask, pScrollMidMaskOld, &ScrollMidRect);
pScrollBotMaskDC = SetupMask(pDC, pScrollBotDC, pScrollBotMask, pScrollBotMaskOld, &ScrollBotRect);
if ((pScrollTopMaskDC == nullptr) ||
(pScrollMidMaskDC == nullptr) ||
(pScrollBotMaskDC == nullptr))
return false;
return true;
}
CDC *CRules::SetupMask(CDC *pDC, CDC *pBitmapDC, CBitmap *pMask, CBitmap * &pMaskOld, CRect *pRect) {
CDC *pNewDC = nullptr;
(*pMask).CreateBitmap((*pRect).right, (*pRect).bottom, 1, 1, nullptr);
pNewDC = new CDC();
if ((pNewDC != nullptr) &&
(*pNewDC).CreateCompatibleDC(pDC)) {
pMaskOld = (*pNewDC).SelectObject(pMask);
if (pMaskOld != nullptr) {
(*pNewDC).BitBlt(
0, 0,
(*pRect).right,
(*pRect).bottom,
pBitmapDC,
0, 0,
SRCCOPY);
return (pNewDC);
}
}
return nullptr;
}
CDC *CRules::SetupCompatibleContext(CDC *pDC, CBitmap *pBitmap,
CBitmap * &pBitmapOld, CPalette *pPalette, CPalette * &pPalOld) {
CDC *pNewDC = nullptr;
pNewDC = new CDC();
if ((pDC != nullptr) &&
(pNewDC != nullptr) &&
(*pNewDC).CreateCompatibleDC(pDC)) {
if (pPalette)
pPalOld = (*pNewDC).SelectPalette(pPalette, false);
else
pPalOld = nullptr;
(*pNewDC).RealizePalette();
pBitmapOld = (*pNewDC).SelectObject(pBitmap);
if (pBitmapOld != nullptr)
return (pNewDC);
}
delete pNewDC;
return nullptr;
}
void CRules::ReleaseCompatibleContext(CDC *&pDC, CBitmap * &pBitmap, CBitmap *pBitmapOld, CPalette *pPalOld) {
if (pBitmapOld != nullptr) {
(*pDC).SelectObject(pBitmapOld);
pBitmapOld = nullptr;
}
if (pPalOld != nullptr) {
(*pDC).SelectPalette(pPalOld, false);
pPalOld = nullptr;
}
if (pBitmap != nullptr) {
(*pBitmap).DeleteObject();
delete pBitmap;
pBitmap = nullptr;
}
if (pDC != nullptr) {
(*pDC).DeleteDC();
delete pDC;
pDC = nullptr;
}
}
CDibDoc *CRules::LoadScrollDIB(const char *pszPathName, CRect *pRect) {
bool bSuccess;
HDIB hDIB;
int dxDIB, dyDIB;
CDibDoc *pDibDoc = nullptr;
// CPalette *pPalette = nullptr;
// CBitmap *pBitmap = nullptr;
pDibDoc = new CDibDoc();
ASSERT(pDibDoc != nullptr);
bSuccess = (*pDibDoc).OpenDocument(pszPathName);
ASSERT(bSuccess);
hDIB = (*pDibDoc).GetHDIB();
ASSERT(hDIB != nullptr);
dxDIB = (int) DIBWidth(hDIB);
dyDIB = (int) DIBHeight(hDIB);
if (pRect != nullptr)
(*pRect).SetRect(0, 0, dxDIB, dyDIB);
return (pDibDoc);
}
bool CRules::PaintScrollDIB(CDC *pDC, CDibDoc *pDibDoc) {
bool bSuccess;
HDIB hDIB;
int dxDIB, dyDIB;
CRect myRect;
hDIB = (*pDibDoc).GetHDIB();
ASSERT(hDIB != nullptr);
dxDIB = (int) DIBWidth(hDIB);
dyDIB = (int) DIBHeight(hDIB);
myRect.SetRect(0, 0, dxDIB, dyDIB);
bSuccess = PaintDIB((*pDC).m_hDC, myRect, hDIB, myRect, pScrollPalette);
return bSuccess;
}
CBitmap *CRules::CreateScrollBitmap(CDC *pDC, CDibDoc *pDibDoc, CPalette *pPalette) {
HDIB hDIB;
CBitmap *pBitmap = nullptr;
hDIB = (*pDibDoc).GetHDIB();
ASSERT(hDIB != nullptr);
pBitmap = ConvertDIB(pDC, hDIB, pPalette);
return pBitmap;
}
void CRules::OnLButtonDown(unsigned int nFlags, CPoint point) {
if (!bActiveWindow)
return;
if (ScrollTopCurlRect.PtInRect(point) && (nHelpPage != 0))
UpdateScroll(nHelpPage - 1);
else if (ScrollBotCurlRect.PtInRect(point) && !bHelpEOF)
UpdateScroll(nHelpPage + 1);
OnMouseMove(nFlags, point);
CDialog::OnLButtonDown(nFlags, point);
}
void CRules::OnMouseMove(unsigned int nFlags, CPoint point) {
HCURSOR hNewCursor = nullptr;
CWinApp *pMyApp;
if (!bActiveWindow)
return;
pMyApp = AfxGetApp();
if (OkayRect.PtInRect(point))
hNewCursor = (*pMyApp).LoadStandardCursor(IDC_ARROW);
else if (ScrollTopCurlRect.PtInRect(point)) {
if (nHelpPage == 0)
hNewCursor = (*pMyApp).LoadCursor(IDC_RULES_INVALID);
else
hNewCursor = (*pMyApp).LoadCursor(IDC_RULES_ARROWUP);
} else if (ScrollBotCurlRect.PtInRect(point)) {
if (bHelpEOF)
hNewCursor = (*pMyApp).LoadCursor(IDC_RULES_INVALID);
else
hNewCursor = (*pMyApp).LoadCursor(IDC_RULES_ARROWDN);
} else
hNewCursor = (*pMyApp).LoadStandardCursor(IDC_ARROW);
//if (hNewCursor != nullptr);
SetCursor(hNewCursor);
CDialog::OnMouseMove(nFlags, point);
}
bool CRules::OnSetCursor(CWnd *pWnd, unsigned int nHitTest, unsigned int message) {
if ((*pWnd).m_hWnd == (*this).m_hWnd)
return true;
else
return false;
}
void CRules::ShowWaitCursor() {
CWinApp *pMyApp;
pMyApp = AfxGetApp();
(*pMyApp).BeginWaitCursor();
}
void CRules::DoArrowCursor() {
CWinApp *pMyApp;
pMyApp = AfxGetApp();
(*pMyApp).EndWaitCursor();
}
} // namespace HodjNPodj
} // namespace Bagel