|
|
#include "sol.h"
#include <shellapi.h> // To pick up ShellAbout()
#include <htmlhelp.h>
#include <commctrl.h> // for fusion classes.
VSZASSERT
#define rgbGreen RGB(0x00,0x80,0x00)
#define rgbWhite RGB(0xff,0xff,0xff)
PT ptNil = {0x7fff, 0x7fff}; TCHAR szAppName[10]; // name of this app: 'solitaire'
TCHAR szScore[50]; // 'score:' for internationalization
/* Instance info */ static HANDLE hAccel; // accelerators handle
HWND hwndApp; // window handle to this app
HANDLE hinstApp; // instance handle to this app
BOOL fBW=FALSE; // true if on true monochrome video! (never true on NT)
HBRUSH hbrTable; // brush for background of table top
LONG rgbTable; // RGB value of table top
BOOL fIconic = fFalse; // true if app is 'iconic'
INT dyChar; // tmHeight of font in hdc
INT dxChar; // tmMaxCharWidth of font in hdc
#define modeNil -1
INT modeFaceDown = modeNil; // back of cards ID
GM *pgmCur = NULL; // current game
/* card extent info */ DEL delCrd; DEL delScreen;
RC rcClient; // client rectangle
INT igmCur; /* the current game #, srand seeded with this */ #ifdef DEBUG
BOOL fScreenShots = fFalse; #endif
/* window messages for external app drawing */ static UINT wmCardDraw;
HDC hdcCur = NULL; // current hdc to draw on
INT usehdcCur = 0; // hdcCur use count
X xOrgCur = 0; Y yOrgCur = 0;
static TCHAR szClass[] = TEXT("Solitaire");
TCHAR szOOM[50];
// BUG: some of these should go in gm struct
//
BOOL fStatusBar = fTrue; BOOL fTimedGame = fTrue; BOOL fKeepScore = fFalse; SMD smd = smdStandard; /* Score MoDe */ INT ccrdDeal = 3; BOOL fOutlineDrag = fFalse;
BOOL fHalfCards = fFalse;
INT xCardMargin; #define MIN_MARGIN (dxCrd / 8 + 3)
/******************** Internal Functions ****************/ BOOL FSolInit( HANDLE, HANDLE, LPTSTR, INT ); VOID GetIniFlags( BOOL * ); VOID APIENTRY cdtTerm( VOID ); VOID DoHelp( INT );
LRESULT APIENTRY SolWndProc(HWND, UINT, WPARAM, LPARAM);
// International stuff
//
INT iCurrency; TCHAR szCurrency[5];
/******************************************************************************
* WINMAIN/ENTRY POINT * This is the main entry-point for the application. It uses the porting * macro MMain() since it was ported from 16bit Windows. * * The accelerator-table was added from demo-purposes. * * *****************************************************************************/ MMain( hinst, hinstPrev, lpstrCmdLine, sw )
MSG msg; LPTSTR lpszCmdLine = GetCommandLine();
// Initialize the application.
//
if (!FSolInit(hinst, hinstPrev, lpszCmdLine, sw)) return(0);
// Message-Polling loop.
//
msg.wParam = 1; while (GetMessage((LPMSG)&msg, NULL, 0, 0)) { if( !TranslateAccelerator( hwndApp, hAccel, &msg )) { TranslateMessage((LPMSG)&msg); DispatchMessage((LPMSG)&msg); } }
return ((int)(msg.wParam ? 1 : 0));
// Eliminate unreferenced-variable warnings from
// porting macro.
//
(void)_argv; (void)_argc; }
/******************************************************************************
* FSolInit * * Main program initialization. * * Arguments: * hinst - instance of this task * hinstPrev - previous instance, or NULL if this is the * first instance * lpszCmdLine - command line argument string * sw - show window command * * Returns: * fFalse on failure. * *****************************************************************************/ BOOL FSolInit(HANDLE hinst, HANDLE hinstPrev, LPTSTR lpszCmdLine, INT sw) { WNDCLASSEX cls; HDC hdc; TEXTMETRIC tm; HANDLE hcrsArrow; BOOL fStartIconic; TCHAR FAR *lpch; BOOL fOutline; TCHAR szT[20]; RECT rect; INITCOMMONCONTROLSEX icc; // common control registration.
WORD APIENTRY TimerProc(HWND, UINT, UINT_PTR, DWORD);
hinstApp = hinst;
/* create stock objects */
CchString(szOOM, idsOOM, ARRAYSIZE(szOOM)); if(!cdtInit((INT FAR *)&dxCrd, (INT FAR *)&dyCrd)) { goto OOMError; } hcrsArrow = LoadCursor(NULL, IDC_ARROW); hdc = GetDC(NULL); if(hdc == NULL) { OOMError: OOM(); return fFalse; }
GetTextMetrics(hdc, (LPTEXTMETRIC)&tm); dyChar = tm.tmHeight; dxChar = tm.tmMaxCharWidth; if (GetDeviceCaps(hdc, NUMCOLORS) == 2) fBW = fTrue;
/* BUG: if HORZRES not big enough, have to call cdtDrawExt & shrink dxCrd */ /* BUG: Need to check VERTRES and divide dxCrd by 2 (esp w/ lores ega) */ dxScreen = GetDeviceCaps(hdc, HORZRES); dyScreen = GetDeviceCaps(hdc, VERTRES); if(fHalfCards = dyScreen < 300) dyCrd /= 2; ReleaseDC(NULL, hdc); rgbTable = fBW ? rgbWhite : rgbGreen; hbrTable = CreateSolidBrush(rgbTable);
srand((WORD) time(NULL));
/* load strings */ CchString(szAppName, idsAppName, ARRAYSIZE(szAppName)); CchString(szScore, idsScore, ARRAYSIZE(szScore));
CchString(szT, idsCardDraw, ARRAYSIZE(szT)); wmCardDraw = RegisterWindowMessage(szT);
/* scan cmd line to see if should come up iconic */ /* this may be unnecessary with win3.0 (function may be provided to */ /* do it automatically */
fStartIconic = fFalse; for(lpch = lpszCmdLine; *lpch != TEXT('\000'); lpch++) { if(*lpch == TEXT('/') && *(lpch+1) == TEXT('I')) { fStartIconic = fTrue; break; } }
// Register the common controls.
icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES | ICC_HOTKEY_CLASS | ICC_LISTVIEW_CLASSES | ICC_PAGESCROLLER_CLASS | ICC_PROGRESS_CLASS | ICC_TAB_CLASSES | ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES; InitCommonControlsEx(&icc);
/* Load the solitaire icon */
hIconMain = LoadIcon(hinstApp, MAKEINTRESOURCE(ID_ICON_MAIN));
/* Load the solitaire icon image */
hImageMain = LoadImage(hinstApp, MAKEINTRESOURCE(ID_ICON_MAIN), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
/* register window classes */
if (hinstPrev == NULL) { ZeroMemory( &cls, sizeof(cls) ); cls.cbSize= sizeof(cls); cls.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS, cls.lpfnWndProc = SolWndProc; cls.hInstance = hinstApp; cls.hIcon = hIconMain; cls.hIconSm= hImageMain; cls.hCursor = hcrsArrow; cls.hbrBackground = hbrTable; cls.lpszMenuName = MAKEINTRESOURCE(idmSol); cls.lpszClassName = (LPTSTR)szClass; if (!RegisterClassEx(&cls)) { goto OOMError; } }
/* Determine the proper starting size for the window */
/* Card margin is just a little bigger than 1/8 of a card */ xCardMargin = MIN_MARGIN; /* We need 7 card widths and 8 margins */ rect.right = dxCrd * 7 + 8 * xCardMargin;
/* Compute the window size we need for a client area this big */ rect.bottom = dyCrd * 4; rect.left = rect.top = 0; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, TRUE); rect.right -= rect.left; rect.bottom -= rect.top;
/* Make sure it's not too big */ if (rect.bottom > dyScreen) rect.bottom = dyScreen;
/* create our windows */ if (! (hwndApp = CreateWindow( (LPTSTR)szClass, (LPTSTR)szAppName, fStartIconic ? WS_OVERLAPPEDWINDOW | WS_MINIMIZE | WS_CLIPCHILDREN: WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, 0, rect.right, rect.bottom, (HWND)NULL, (HMENU)NULL, hinstApp, (LPTSTR)NULL))) { goto OOMError; }
GetIniFlags(&fOutline);
if(SetTimer(hwndApp, 666, 250, TimerProc) == 0) { goto OOMError; }
FInitGm(); FSetDrag(fOutline);
ShowWindow(hwndApp, sw); UpdateWindow(hwndApp);
hAccel = LoadAccelerators( hinst, TEXT("HiddenAccel") );
FRegisterStat(hinstPrev == NULL); if(fStatusBar) FCreateStat();
Assert(pgmCur != NULL); if(sw != SW_SHOWMINNOACTIVE && sw != SW_MINIMIZE) PostMessage(hwndApp, WM_COMMAND, idsInitiate, 0L);
return(fTrue); }
VOID DoPaint(HWND hwnd) { PAINTSTRUCT paint;
BeginPaint(hwnd, (LPPAINTSTRUCT) &paint); if(pgmCur) SendGmMsg(pgmCur, msggPaint, (INT_PTR) &paint, 0); EndPaint(hwnd, (LPPAINTSTRUCT) &paint); }
/* SolWndProc
* * Window procedure for main Sol window. * * Arguments: * hwnd - window handle receiving the message - should * be hwndSol * wm - window message * wParam, lParam - more info as required by wm * * Returns: * depends on the message */ LRESULT APIENTRY SolWndProc(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam) { HMENU hmenu; PT pt; INT msgg; VOID NewGame(); VOID StatString();
switch (wm) { default: if(wm == wmCardDraw) { switch(wParam) { case drwInit: return MAKELONG(dxCrd, dyCrd);
case drwDrawCard: #define lpcddr ((CDDR FAR *)lParam)
return cdtDraw(lpcddr->hdc, lpcddr->x, lpcddr->y, lpcddr->cd, lpcddr->mode, lpcddr->rgbBgnd); #undef lpcddr
case drwClose: PostMessage(hwndApp, WM_SYSCOMMAND, SC_CLOSE, 0L); return fTrue; } } break;
case WM_HELP: DoHelp( idsHelpIndex ); break;
case WM_DESTROY: KillTimer(hwndApp, 666); SendGmMsg(pgmCur, msggEnd, 0, 0); FSetDrag(fTrue); /* Free up screen bitmaps if we made em */ cdtTerm(); DeleteObject(hbrTable); PostQuitMessage(0); break;
case WM_ACTIVATE: if( GET_WM_ACTIVATE_STATE(wParam, lParam) && !GET_WM_ACTIVATE_FMINIMIZED(wParam, lParam) ) DoPaint(hwnd); break;
case WM_KILLFOCUS: if(pgmCur->fButtonDown) SendGmMsg(pgmCur, msggMouseUp, 0, fTrue); /* Fall through. */ case WM_SETFOCUS: ShowCursor(wm == WM_SETFOCUS); break;
case WM_SIZE: { int nNewMargin; int nMinMargin;
fIconic = IsIconic(hwnd); GetClientRect(hwnd, (LPRECT) &rcClient);
/* Compute the new margin size if any and if necessary, redraw */ nNewMargin = ((short)lParam - 7 * (short)dxCrd) / 8; nMinMargin = MIN_MARGIN; if (nNewMargin < nMinMargin && xCardMargin != nMinMargin) nNewMargin = nMinMargin; if (nNewMargin >= nMinMargin) { xCardMargin = nNewMargin; PositionCols(); InvalidateRect(hwnd, NULL, TRUE); }
/* Code always falls through here */ }
case WM_MOVE: StatMove(); break;
case WM_MENUSELECT: // Don't send in garbage if not a menu item
if( GET_WM_MENUSELECT_FLAGS( wParam, lParam ) & MF_POPUP || GET_WM_MENUSELECT_FLAGS( wParam, lParam ) & MF_SYSMENU || GET_WM_MENUSELECT_FLAGS( wParam, lParam ) & MF_SEPARATOR ) {
StatString(idsNil); } else { StatString( GET_WM_MENUSELECT_CMD( wParam, lParam )); } break;
case WM_KEYDOWN: Assert(pgmCur); SendGmMsg(pgmCur, msggKeyHit, wParam, 0); break;
case WM_LBUTTONDOWN: /* ProfStart(); */ SetCapture(hwnd); if(pgmCur->fButtonDown) break; msgg = msggMouseDown; goto DoMouse;
case WM_LBUTTONDBLCLK: msgg = msggMouseDblClk; if(pgmCur->fButtonDown) break; goto DoMouse;
case WM_RBUTTONDOWN: // If the left mousebutton is down, ignore the right click.
if (GetCapture()) break; msgg = msggMouseRightClk; goto DoMouse;
case WM_LBUTTONUP: /* ProfStop(); */ ReleaseCapture(); msgg = msggMouseUp; if(!pgmCur->fButtonDown) break; goto DoMouse;
case WM_MOUSEMOVE: msgg = msggMouseMove; if(!pgmCur->fButtonDown) break; DoMouse: Assert(pgmCur != NULL); LONG2POINT( lParam, pt ); Assert(pgmCur); SendGmMsg(pgmCur, msgg, (INT_PTR) &pt, 0); break;
case WM_COMMAND: switch( GET_WM_COMMAND_ID( wParam, lParam )) { /* Game menu */ case idsInitiate: NewGame(fTrue, fFalse); break; case idsUndo: Assert(pgmCur); SendGmMsg(pgmCur, msggUndo, 0, 0); break; case idsBacks: DoBacks(); break; case idsOptions: DoOptions(); break; case idsExit: PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L); break; /* Help Menu */ case (WORD)idsHelpIndex: case (WORD)idsHelpSearch: case (WORD)idsHelpUsing: DoHelp( (INT)(SHORT)GET_WM_COMMAND_ID( wParam, lParam )); break; case idsAbout: { TCHAR szExtraInfo[100]; CchString(szExtraInfo, idsExtraInfo, ARRAYSIZE(szExtraInfo)); #ifndef _GAMBIT_
ShellAbout(hwnd, szAppName, szExtraInfo, hIconMain); #endif
break; } case idsForceWin: SendGmMsg(pgmCur, msggForceWin, 0, 0); break; #ifdef DEBUG
case idsGameNo: if(FSetGameNo()) NewGame(fFalse, fFalse); break;
case idsCardMacs: PrintCardMacs(pgmCur); break; case idsAssertFail: Assert(fFalse); break; case idsMarquee: break;
case idsScreenShots: fScreenShots ^= 1; CheckMenuItem(GetMenu(hwnd), idsScreenShots, fScreenShots ? MF_CHECKED|MF_BYCOMMAND : MF_UNCHECKED|MF_BYCOMMAND); InvalidateRect(hwndStat, NULL, fTrue); if(fScreenShots) InvalidateRect(hwnd, NULL, fTrue); break; #endif
default: break; } break;
case WM_INITMENU: hmenu = GetMenu(hwnd); Assert(pgmCur); EnableMenuItem(hmenu, idsUndo, pgmCur->udr.fAvail && !FSelOfGm(pgmCur) ? MF_ENABLED : MF_DISABLED|MF_GRAYED); EnableMenuItem(hmenu, idsInitiate, FSelOfGm(pgmCur) ? MF_DISABLED|MF_GRAYED : MF_ENABLED); EnableMenuItem(hmenu, idsBacks, FSelOfGm(pgmCur) ? MF_DISABLED|MF_GRAYED : MF_ENABLED); EnableMenuItem(hmenu, idsAbout, FSelOfGm(pgmCur) ? MF_DISABLED|MF_GRAYED : MF_ENABLED); break;
case WM_PAINT: if(!fIconic) { DoPaint(hwnd); return(0L); } break; }
return(DefWindowProc(hwnd, wm, wParam, lParam)); }
HDC HdcSet(HDC hdc, X xOrg, Y yOrg) { HDC hdcT = hdcCur; hdcCur = hdc; xOrgCur = xOrg; yOrgCur = yOrg; return hdcT; }
BOOL FGetHdc() { HDC hdc;
Assert(hwndApp); if(hdcCur != NULL) { usehdcCur++; return fTrue; }
hdc = GetDC(hwndApp); if(hdc == NULL) return fFalse; HdcSet(hdc, 0, 0); usehdcCur = 1; return fTrue; }
VOID ReleaseHdc() { if(hdcCur == NULL) return; if(--usehdcCur == 0) { ReleaseDC(hwndApp, hdcCur); hdcCur = NULL; } }
WORD APIENTRY TimerProc(HWND hwnd, UINT wm, UINT_PTR id, DWORD dwTime) {
if(pgmCur != NULL) SendGmMsg(pgmCur, msggTimer, 0, 0); return fTrue; }
VOID ChangeBack(INT mode) {
if(mode == modeFaceDown) return; modeFaceDown = mode; InvalidateRect(hwndApp, NULL, fTrue); }
VOID NewGame(BOOL fNewSeed, BOOL fZeroScore) {
#ifdef DEBUG
InitDebug(); #endif
if(fNewSeed) { static INT lastrnd= -1; // previous rand() value
INT rnd1; // trial rand() value
INT Param;
// It was reported that games never changed.
// We could not repro it so see if it happens
// and output a message to the debugger.
//
Param= (INT) time(NULL); srand( igmCur = ((WORD) Param) & 0x7fff);
#ifdef DEBUG
rnd1= rand();
if( lastrnd == rnd1 ) { TCHAR szText[100]; wsprintf(szText,TEXT("Games repeat: time= %d GetLastError= %d\n"), Param, GetLastError()); OutputDebugString(szText); }
lastrnd= rnd1; #endif
}
#ifdef DEBUG
SendGmMsg(pgmCur, msggChangeScore, 0, 0); #endif
SendGmMsg(pgmCur, msggDeal, fZeroScore, 0); }
INT_PTR APIENTRY About(HWND hdlg, UINT iMessage, WPARAM wParam, LPARAM lParam) { if (iMessage == WM_COMMAND) { EndDialog(hdlg,fTrue); return fTrue; } else if (iMessage == WM_INITDIALOG) return fTrue; else return fFalse; }
VOID DoHelp(INT idContext) { CHAR sz[100]; HWND hwndResult;
LoadStringA(hinstApp, (WORD)idsHelpFile, (LPSTR)sz, 100);
#ifndef _GAMBIT_
switch(idContext) { case idsHelpUsing: hwndResult = HtmlHelpA(GetDesktopWindow(), "NTHelp.chm", HH_DISPLAY_TOPIC, 0); break; case idsHelpIndex: hwndResult = HtmlHelpA(GetDesktopWindow(), sz, HH_DISPLAY_TOPIC, 0); break; case idsHelpSearch: hwndResult = HtmlHelpA(GetDesktopWindow(), sz, HH_DISPLAY_INDEX, 0); break; } if(!hwndResult) ErrorIds(idsNoHelp); #endif
}
VOID GetIniFlags(BOOL *pfOutline) { INI ini; INT mode; TCHAR szDefCurrency[5]; INT iDefCurrency;
ini.w = 0; ini.grbit.fStatusBar = fStatusBar; ini.grbit.fTimedGame = fTimedGame; ini.grbit.fOutlineDrag = fOutlineDrag; ini.grbit.fDrawThree = ccrdDeal == 3; ini.grbit.fKeepScore = fKeepScore; ini.grbit.fSMD = 0;
ini.w = GetIniInt(idsAppName, idsOpts, ini.w);
fStatusBar = ini.grbit.fStatusBar ? 1 : 0; fTimedGame = ini.grbit.fTimedGame ? 1 : 0; *pfOutline = ini.grbit.fOutlineDrag ? 1 : 0; ccrdDeal = ini.grbit.fDrawThree ? 3 : 1; fKeepScore = ini.grbit.fKeepScore ? 1 : 0; switch(ini.grbit.fSMD) { default: smd = smdStandard; break; case 1: smd = smdVegas; break; case 2: smd = smdNone; break; }
mode = GetIniInt(idsAppName, idsBack, rand() % cIDFACEDOWN) + IDFACEDOWNFIRST-1; ChangeBack(PegRange(mode, IDFACEDOWNFIRST, IDFACEDOWN12));
// get the default user currency.
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY, szDefCurrency, sizeof(szDefCurrency)/sizeof(TCHAR)) == 0) lstrcpy(szDefCurrency, TEXT("$"));
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICURRENCY, (LPTSTR) &iDefCurrency, sizeof(iDefCurrency)) == 0) iDefCurrency = 0;
iCurrency = GetIniInt(idsIntl, idsiCurrency, iDefCurrency); FGetIniString(idsIntl, idssCurrency, szCurrency, szDefCurrency, sizeof(szCurrency));
}
VOID WriteIniFlags(INT wif) { INI ini;
if(wif & wifOpts) { ini.w = 0; ini.grbit.fStatusBar = fStatusBar; ini.grbit.fTimedGame = fTimedGame; ini.grbit.fOutlineDrag = fOutlineDrag; ini.grbit.fDrawThree = ccrdDeal == 3; ini.grbit.fKeepScore = fKeepScore; switch(smd) { default: Assert(fFalse); break; case smdStandard: ini.grbit.fSMD = 0; break; case smdVegas: ini.grbit.fSMD = 1; break; case smdNone: ini.grbit.fSMD = 2; break; }
FWriteIniInt(idsAppName, idsOpts, ini.w); } if(wif & wifBack) FWriteIniInt(idsAppName, idsBack, modeFaceDown-IDFACEDOWNFIRST+1); }
|