/** \file mswmisc.c * Basic windows functions and main entry point for application. */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2005 Dave Bullis, 2009 Martin Fischer * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define _WIN32_WINNT 0x0500 #include #include #include #include #include #include #include #include #include #include "mswint.h" #include "i18n.h" #if _MSC_VER > 1300 #define stricmp _stricmp #define strnicmp _strnicmp #define strdup _strdup #endif #define OFN_LONGFILENAMES 0x00200000L char * mswStrdup( const char * ); #define PAUSE_TIMER (901) #define ALARM_TIMER (902) #define BALLOONHELP_TIMER (903) #define TRIGGER_TIMER (904) #define WANT_LITTLE_LABEL_FONT #ifndef WANT_LITTLE_LABEL_FONT #define LABELFONTDECL #define LABELFONTSELECT #define LABELFONTRESET #else #define LABELFONTDECL HFONT hFont; #define LABELFONTRESET if (!mswThickFont) {SelectObject( hDc, hFont );} #define LABELFONTSELECT if (!mswThickFont) {hFont = SelectObject( hDc, mswLabelFont );} #endif /* * EXPORTED VARIABLES */ long debugWindow = 0; HINSTANCE mswHInst; HWND mswHWnd = (HWND)0; const char *mswDrawWindowClassName = "DRAWWINDOW"; char mswTmpBuff[1024]; int mswEditHeight; int mswAllowBalloonHelp = TRUE; int mswGroupStyle; HFONT mswOldTextFont; HFONT mswLabelFont; long mswThickFont = 1; double mswScale = 1.0; callBacks_t *mswCallBacks[CALLBACK_CNT]; void closeBalloonHelp( void ); static wControl_p getControlFromCursor( HWND, wWin_p * ); /* * LOCAL VARIABLES */ struct wWin_t { WOBJ_COMMON wPos_t lastX, lastY; wPos_t padX, padY; wControl_p first, last; wWinCallBack_p winProc; BOOL_T busy; #ifdef OWNERICON HBITMAP wicon_bm; wPos_t wicon_w, wicon_h; #endif DWORD baseStyle; wControl_p focusChainFirst; wControl_p focusChainLast; char * nameStr; wBool_t centerWin; DWORD style; int isBusy; int pendingShow; int modalLevel; }; static needToDoPendingShow = FALSE; /* System metrics: */ static int mTitleH; static int mFixBorderW; static int mFixBorderH; static int mResizeBorderW; static int mResizeBorderH; static int mMenuH; static int screenWidth = 0, screenHeight = 0; wWin_p mswWin = NULL; wWin_p winFirst, winLast; static long count51 = 0; static UINT alarmTimer; static UINT pauseTimer; static UINT balloonHelpTimer = (UINT)0; static UINT triggerTimer; static UINT balloonHelpTimeOut = 500; static wControl_p balloonHelpButton = NULL; static enum { balloonHelpIdle , balloonHelpWait, balloonHelpShow } balloonHelpState = balloonHelpIdle; static HWND balloonHelpHWnd = (HWND)0; static int balloonHelpFontSize = 8; static char balloonHelpFaceName[] = "MS Sans Serif"; static HFONT balloonHelpOldFont; static HFONT balloonHelpNewFont; static int balloonHelpEnable = TRUE; static wControl_p balloonControlButton = NULL; static BOOL_T helpInitted = FALSE; static DWORD dwCookie; #define CONTROL_BASE (1) typedef struct { wControl_p b; } controlMap_t; dynArr_t controlMap_da; #define controlMap(N) DYNARR_N(controlMap_t,controlMap_da,N) static char * appName; static char * helpFile; char *mswProfileFile; static wBalloonHelp_t * balloonHelpStrings; static wCursor_t curCursor = wCursorNormal; #ifdef HELPSTR static FILE * helpStrF; #endif static int inMainWndProc = FALSE; int newHelp = 1; static wBool_t mswWinBlockEnabled = TRUE; static FILE * dumpControlsF; static int dumpControls; extern char *userLocale; /* ***************************************************************************** * * Internal Utility functions * ***************************************************************************** */ DWORD GetTextExtent( HDC hDc, CHAR * str, UINT len ) { SIZE size; GetTextExtentPoint( hDc, str, len, &size ); return size.cx + (size.cy<<16); } static char * controlNames[] = { "MAIN", "POPUP", "BUTTON", "STRING", "INTEGER", "FLOAT", "LIST", "DROPLIST", "COMBOLIST", "RADIO", "TOGGLE", "DRAW", "TEXT", "MESSAGE", "LINES", "MENUITEM", "CHOICEITEM", "BOX" }; static void doDumpControls(void) { wControl_p b; int inx; if ( !dumpControls ) return; if ( !dumpControlsF ) { dumpControlsF = fopen( "controls.lst", "w" ); if ( !dumpControlsF ) abort(); } for ( inx=0; inxhWnd, (b->type>=0&&b->type<=B_BOX?controlNames[b->type]:"NOTYPE"), (b->labelStr?b->labelStr:""), (b->helpStr?b->helpStr:"") ); } else { fprintf( dumpControlsF, "[%0.3d] \n", inx ); } } fflush( dumpControlsF ); fclose( dumpControlsF ); dumpControls = 0; } void mswFail( const char * where ) { sprintf( mswTmpBuff, "%s\n# Controls %d", where, controlMap_da.cnt ); MessageBox( NULL, mswTmpBuff, "FAIL", MB_TASKMODAL|MB_OK ); doDumpControls(); } /* static UINT curSysRes = 100; static UINT curGdiRes = 100; static UINT curUsrRes = 100; static UINT curMinRes = 100; */ wControl_p mswMapIndex( INDEX_T inx ) { if (inx < CONTROL_BASE || inx > controlMap_da.cnt) { mswFail("mswMapIndex- bad index"); exit(1); } return controlMap(inx-CONTROL_BASE).b; } void mswRepaintLabel( HWND hWnd, wControl_p b ) { HDC hDc; HBRUSH oldBrush, newBrush; RECT rect; DWORD dw; LABELFONTDECL if (b->labelStr) { hDc = GetDC( hWnd ); LABELFONTSELECT newBrush = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ) ); oldBrush = SelectObject( hDc, newBrush ); dw = GetTextExtent( hDc, CAST_AWAY_CONST b->labelStr, strlen(b->labelStr) ); rect.left = b->labelX; rect.top = b->labelY; rect.right = b->labelX + LOWORD(dw); rect.bottom = b->labelY + HIWORD(dw); FillRect( hDc, &rect, newBrush ); DeleteObject( SelectObject( hDc, oldBrush ) ); /*SetBkMode( hDc, OPAQUE );*/ SetBkColor( hDc, GetSysColor( COLOR_BTNFACE ) ); if (!TextOut( hDc, b->labelX, b->labelY, b->labelStr, strlen(b->labelStr) ) ) mswFail( "Repainting text label" ); LABELFONTRESET ReleaseDC( hWnd, hDc ); } } int mswRegister( wControl_p w ) { int index; DYNARR_APPEND( controlMap_t, controlMap_da, 25 ); index = controlMap_da.cnt-1+CONTROL_BASE; controlMap(controlMap_da.cnt-1).b = (wControl_p)w; return index; } void mswUnregister( int index ) { if (index < 0 || index > controlMap_da.cnt) { mswFail("mswMapIndex- bad index"); exit(1); } controlMap(index-CONTROL_BASE).b = NULL; } void * mswAlloc( wWin_p parent, wType_e type, const char * labelStr, int size, void * data, int * index ) { wControl_p w = (wControl_p)calloc( 1, size ); if (w == NULL) abort(); *index = mswRegister( w ); w->type = type; w->next = NULL; w->parent = parent; w->x = 0; w->y = 0; w->w = 0; w->h = 0; w->option = 0; w->labelX = w->labelY = 0; w->labelStr = labelStr; w->helpStr = NULL; w->hWnd = (HWND)0; w->data = data; w->focusChainNext = NULL; w->shown = TRUE; return w; } void mswComputePos( wControl_p b, wPos_t origX, wPos_t origY ) { wWin_p w = b->parent; if (origX >= 0) b->x = origX; else b->x = w->lastX + (-origX) - 1; if (origY >= 0) b->y = origY; else b->y = w->lastY + (-origY) - 1; b->labelX = b->x; b->labelY = b->y+2; if (b->labelStr) { int lab_l; HDC hDc; DWORD dw; LABELFONTDECL hDc = GetDC( w->hWnd ); LABELFONTSELECT lab_l = strlen(b->labelStr); dw = GetTextExtent( hDc, CAST_AWAY_CONST b->labelStr, lab_l ); b->labelX -= LOWORD(dw) + 5; LABELFONTRESET ReleaseDC( w->hWnd, hDc ); } } void mswAddButton( wControl_p b, BOOL_T paintLabel, const char * helpStr ) { wWin_p w = b->parent; BOOL_T resize = FALSE; RECT rect; if (w->first == NULL) { w->first = b; } else { w->last->next = b; } w->last = b; b->next = NULL; b->parent = w; w->lastX = b->x + b->w; w->lastY = b->y + b->h; if ((w->option&F_AUTOSIZE)!=0 && w->lastX > w->w) { w->w = w->lastX; resize = TRUE; } if ((w->option&F_AUTOSIZE)!=0 && w->lastY > w->h) { w->h = w->lastY; resize = TRUE; } if (resize) { w->busy = TRUE; rect.left = 0; rect.top = 0; rect.right = w->w+w->padX; rect.bottom = w->h+w->padY; AdjustWindowRect( &rect, w->style, (w->option&F_MENUBAR)?1:0 ); rect.bottom += mFixBorderH; if (!SetWindowPos( w->hWnd, HWND_TOP, CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE)) mswFail("SetWindowPos"); w->busy = FALSE; } if (paintLabel) mswRepaintLabel( w->hWnd, (wControl_p)b ); if (helpStr == NULL) return; b->helpStr = mswStrdup( helpStr ); #ifdef HELPSTR if (helpStrF) fprintf( helpStrF, "HELPSTR - %s\n", helpStr?helpStr:"<>" ); #endif } void mswResize( wWin_p w ) { wControl_p b; RECT rect; w->lastX = 0; w->lastY = 0; for (b=w->first; b; b=b->next) { if (w->lastX < (b->x + b->w)) w->lastX = b->x + b->w; if (w->lastY < (b->y + b->h)) w->lastY = b->y + b->h; } if (w->option&F_AUTOSIZE) { w->w = w->lastX; w->h = w->lastY; w->busy = TRUE; rect.left = 0; rect.top = 0; rect.right = w->w + w->padX; rect.bottom = w->h + w->padY; AdjustWindowRect( &rect, w->style, (w->option&F_MENUBAR)?1:0 ); rect.bottom += mFixBorderH; if (!SetWindowPos( w->hWnd, HWND_TOP, CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER)) mswFail("SetWindowPos"); w->busy = FALSE; } } void mswChainFocus( wControl_p b ) { wWin_p w; w = b->parent; if (w->option&F_NOTAB) return; if (b->option&BO_NOTAB) return; if (w->focusChainFirst == NULL) { w->focusChainFirst = w->focusChainLast = w->focusChainNext = b; b->focusChainNext = b; } else { w->focusChainLast->focusChainNext = b; w->focusChainLast = b; b->focusChainNext = w->focusChainFirst; } } void mswSetFocus( wControl_p b ) { if (b && b->type != B_MENUITEM && b->focusChainNext) b->parent->focusChainNext = b; } /* ****************************************************************************** * * Main and Popup Windows * ****************************************************************************** */ static void getSavedSizeAndPos( long option, const char * nameStr, wPos_t *rw, wPos_t *rh, wPos_t *rx, wPos_t *ry, int *showCmd ) { int x, y, w, h; const char *cp; char *cq; int state; *showCmd = SW_SHOWNORMAL; if ( (option&F_RECALLPOS) && nameStr ) { if ( (option & F_RESIZE) && (cp = wPrefGetString( "msw window size", nameStr)) && (state = (int)strtol( cp, &cq, 10 ), cp != cq) && (cp = cq, w = (wPos_t)strtod( cp, &cq ), cp != cq ) && (cp = cq, h = (int)strtod( cp, &cq ), cp != cq) ) { if (state == 1) *showCmd = SW_SHOWMINIMIZED; else if (state == 2) *showCmd = SW_SHOWMAXIMIZED; if (w < 10) w = 10; if (h < 10) h = 10; if (w > screenWidth) w = screenWidth; if (h > screenHeight) h = screenHeight; *rw = w; *rh = h; } if ((cp = wPrefGetString( "msw window pos", nameStr)) && (x = (wPos_t)strtod( cp, &cq ), cp != cq) && (cp = cq, y = (wPos_t)strtod( cp, &cq ), cp != cq) ) { if (y < 0) y = 0; if (x < 0) x = 0; if ( y > screenHeight-40 ) y = screenHeight-40; if ( x > screenWidth-40 ) x = screenWidth-40; *rx = x; *ry = y; } } } static wWin_p winCommonCreate( HWND hWnd, int typ, long option, const char * className, long style, const char * labelStr, wWinCallBack_p winProc, wPos_t w, wPos_t h, void * data, const char * nameStr, int * showCmd ) { wWin_p win; int index; wPos_t ww, hh, xx, yy; RECT rect; win = (wWin_p)mswAlloc( NULL, typ, mswStrdup(labelStr), sizeof *win, data, &index ); win->option = option; win->first = win->last = NULL; win->lastX = 0; win->lastY = 0; win->winProc = winProc; win->centerWin = TRUE; win->modalLevel = 0; #ifdef OWNERICON win->wicon_bm = (HBITMAP)0; #endif win->busy = TRUE; ww = hh = xx = yy = CW_USEDEFAULT; getSavedSizeAndPos( option, nameStr, &ww, &hh, &xx, &yy, showCmd ); if (xx != CW_USEDEFAULT) win->centerWin = FALSE; if (option & F_RESIZE) { style |= WS_THICKFRAME; if ( ww != CW_USEDEFAULT ) { w = ww; h = hh; option &= ~F_AUTOSIZE; win->option = option; } } if ( option & F_AUTOSIZE ) { win->padX = w; win->padY = h; } else { win->padX = 0; win->padY = 0; win->w = w; win->h = h; } win->style = style; rect.left = 0; rect.top = 0; rect.right = win->w + win->padX; rect.bottom = win->h + win->padY; AdjustWindowRect( &rect, win->style, (win->option&F_MENUBAR)?1:0 ); rect.bottom += mFixBorderH; win->hWnd = CreateWindow( className, labelStr, style, xx, yy, rect.right-rect.left, rect.bottom-rect.top, hWnd, NULL, mswHInst, NULL ); if (win->hWnd == (HWND)0) { mswFail( "CreateWindow(POPUP)" ); } else { SetWindowWord( win->hWnd, 0, (WORD)index ); } win->baseStyle = WS_GROUP; win->focusChainFirst = win->focusChainLast = win->focusChainNext = NULL; if (winFirst == NULL) { winFirst = winLast = win; } else { winLast->next = (wControl_p)win; winLast = win; } #ifdef HELPSTR if (helpStrF) fprintf( helpStrF, "WINDOW - %s\n", labelStr ); #endif win->nameStr = mswStrdup( nameStr ); if (typ == W_MAIN) mswInitColorPalette(); #ifdef LATER hDc = GetDC( win->hWnd ); oldHPal = SelectPalette( hDc, mswPalette, 0 ); ReleaseDC( win->hWnd, hDc ); #endif return win; } void wInitAppName(char *_appName) { appName = (char *)malloc( strlen(_appName) + 1 ); strcpy(appName, _appName); } /** * Initialize the application's main window. This function does the necessary initialization * of the application including creation of the main window. * * \param name IN internal name of the application. Used for filenames etc. * \param x IN size * \param y IN size * \param helpStr IN ?? * \param labelStr IN window title * \param nameStr IN ?? * \param option IN options for window creation * \param winProc IN pointer to main window procedure * \param data IN ?? * \return window handle or NULL on error */ wWin_p wWinMainCreate( const char * name, POS_T x, POS_T y, const char * helpStr, const char * labelStr, const char * nameStr, long option, wWinCallBack_p winProc, void * data ) { wWin_p w; RECT rect; const char * appDir; const char * libDir; int showCmd; int error; HDC hDc; TEXTMETRIC tm; char *pos; char * configName; /* check for configuration name */ if( pos = strchr( name, ';' )) { /* if found, split application name and configuration name */ configName = (char *)malloc( strlen( name ) + 1 ); strcpy( configName, pos + 1 ); } else { /* if not found, application name and configuration name are same */ configName = (char*)malloc( strlen(name)+1 ); strcpy( configName, name ); } appDir = wGetAppWorkDir(); if ( appDir == NULL ) { free( configName ); return NULL; } mswProfileFile = (char*)malloc( strlen(appDir)+1+strlen(configName)+1+3+1 ); wsprintf( mswProfileFile, "%s\\%s.ini", appDir, configName ); free( configName ); error = WritePrivateProfileString( "mswtest", "test", "ok", mswProfileFile ); if ( error <= 0 ) { sprintf( mswTmpBuff, "Can not write to %s.\nPlease make sure the directory exists and is writable", mswProfileFile ); wNoticeEx( NT_ERROR, mswTmpBuff, "Ok", NULL ); return NULL; } libDir = wGetAppLibDir(); /* length of path + \ + length of filename + . + length of extension + \0 */ helpFile = (char*)malloc( strlen(libDir) + 1 + strlen(appName) + 1 + 3 + 1 ); wsprintf( helpFile, "%s\\%s.chm", libDir, appName ); wPrefGetInteger( "msw tweak", "ThickFont", &mswThickFont, 0 ); showCmd = SW_SHOW; w = winCommonCreate( NULL, W_MAIN, option|F_RESIZE, "MswMainWindow", WS_OVERLAPPEDWINDOW, labelStr, winProc, x, y, data, nameStr, &showCmd ); mswHWnd = w->hWnd; if ( !mswThickFont ) { DWORD dw; SendMessage( w->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L ); hDc = GetDC( w->hWnd ); GetTextMetrics( hDc, &tm ); mswEditHeight = tm.tmHeight+2; dw = GetTextExtent( hDc, "AXqypj", 6 ); mswEditHeight = HIWORD(dw)+2; ReleaseDC( w->hWnd, hDc ); } ShowWindow( w->hWnd, showCmd ); UpdateWindow( w->hWnd ); GetWindowRect( w->hWnd, &rect ); GetClientRect( w->hWnd, &rect ); w->busy = FALSE; return w; } wWin_p wWinPopupCreate( wWin_p parent, POS_T x, POS_T y, const char * helpStr, const char * labelStr, const char * nameStr, long option, wWinCallBack_p winProc, void * data ) { wWin_p w; DWORD style; HMENU sysMenu; int showCmd; static DWORD overlapped = WS_OVERLAPPED; static DWORD popup = WS_POPUP; style = popup; style |= WS_BORDER | WS_CAPTION | WS_SYSMENU; w = winCommonCreate( parent?parent->hWnd:mswHWnd, W_POPUP, option, "MswPopUpWindow", style, labelStr, winProc, x, y, data, nameStr, &showCmd ); w->helpStr = mswStrdup( helpStr ); sysMenu = GetSystemMenu( w->hWnd, FALSE ); if (sysMenu) { DeleteMenu( sysMenu, SC_RESTORE, MF_BYCOMMAND ); /*DeleteMenu( sysMenu, SC_MOVE, MF_BYCOMMAND );*/ /*DeleteMenu( sysMenu, SC_SIZE, MF_BYCOMMAND );*/ DeleteMenu( sysMenu, SC_MINIMIZE, MF_BYCOMMAND ); DeleteMenu( sysMenu, SC_MAXIMIZE, MF_BYCOMMAND ); DeleteMenu( sysMenu, SC_TASKLIST, MF_BYCOMMAND ); DeleteMenu( sysMenu, 4, MF_BYPOSITION ); } w->busy = FALSE; return w; } void wWinSetBigIcon( wWin_p win, wIcon_p bm ) { #ifdef OWNERICON win->wicon_w = bm->w; win->wicon_h = bm->h; win->wicon_bm = mswCreateBitMap( GetSysColor(COLOR_BTNTEXT), RGB( 255, 255, 255 ), RGB( 255, 255, 255 ), bm->w, bm->h, bm->bits ); #endif } void wWinSetSmallIcon( wWin_p win, wIcon_p bm ) { #ifdef OWNERICON win->wicon_w = bm->w; win->wicon_h = bm->h; win->wicon_bm = mswCreateBitMap( GetSysColor(COLOR_BTNTEXT), RGB( 255, 255, 255 ), RGB( 255, 255, 255 ), bm->w, bm->h, bm->bits ); #endif } void wWinTop( wWin_p win ) { /*BringWindowToTop( win->hWnd );*/ SetWindowPos( win->hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE ); SetFocus( win->hWnd ); } DWORD mswGetBaseStyle( wWin_p win ) { DWORD style; style = win->baseStyle; win->baseStyle = 0; return style; } static wAccelKey_e translateExtKey( UINT wParam ) { wAccelKey_e extChar; extChar = wAccelKey_None; switch( wParam ) { case VK_DELETE: extChar = wAccelKey_Del; break; case VK_INSERT: extChar = wAccelKey_Ins; break; case VK_HOME: extChar = wAccelKey_Home; break; case VK_END: extChar = wAccelKey_End; break; case VK_PRIOR: extChar = wAccelKey_Pgup; break; case VK_NEXT: extChar = wAccelKey_Pgdn; break; case VK_UP: extChar = wAccelKey_Up; break; case VK_DOWN: extChar = wAccelKey_Down; break; case VK_RIGHT: extChar = wAccelKey_Right; break; case VK_LEFT: extChar = wAccelKey_Left; break; case VK_BACK: extChar = wAccelKey_Back; break; /*case VK_F1: extChar = wAccelKey_F1; break;*/ case VK_F2: extChar = wAccelKey_F2; break; case VK_F3: extChar = wAccelKey_F3; break; case VK_F4: extChar = wAccelKey_F4; break; case VK_F5: extChar = wAccelKey_F5; break; case VK_F6: extChar = wAccelKey_F6; break; case VK_F7: extChar = wAccelKey_F7; break; case VK_F8: extChar = wAccelKey_F8; break; case VK_F9: extChar = wAccelKey_F9; break; case VK_F10: extChar = wAccelKey_F10; break; case VK_F11: extChar = wAccelKey_F11; break; case VK_F12: extChar = wAccelKey_F12; break; } return extChar; } long notModKey; int mswTranslateAccelerator( HWND hWnd, LPMSG pMsg ) { long acclKey; long state; wWin_p win; wControl_p b; if ( pMsg->message != WM_KEYDOWN ) return FALSE; acclKey = pMsg->wParam; b = getControlFromCursor( pMsg->hwnd, &win ); if ( win == NULL ) return 0; if ( b != NULL ) { switch (b->type) { case B_STRING: case B_INTEGER: case B_FLOAT: case B_LIST: case B_DROPLIST: case B_COMBOLIST: case B_TEXT: return 0; } } if ( acclKey == (long)VK_F1 ) { closeBalloonHelp(); if (!b && win) { wHelp( win->helpStr ); } else { if (b->helpStr) wHelp( b->helpStr ); else if (b->parent) wHelp( b->parent->nameStr ); } return 1; } /*acclKey = translateExtKey( (WORD)acclKey );*/ state = 0; if ( GetKeyState(VK_CONTROL) & 0x1000 ) state |= WKEY_CTRL; if ( GetKeyState(VK_MENU) & 0x1000 ) state |= WKEY_ALT; if ( GetKeyState(VK_SHIFT) & 0x1000 ) state |= WKEY_SHIFT; state <<= 8; acclKey |= state; if (pMsg->wParam > 0x12) notModKey = TRUE; return mswMenuAccelerator( win, acclKey ); } /* ****************************************************************************** * * Window Utilities * ****************************************************************************** */ void wGetDisplaySize( POS_T * width, POS_T * height ) { *width = screenWidth; *height = screenHeight; } void wWinGetSize( wWin_p w, POS_T * width, POS_T * height ) { RECT rect; GetWindowRect( w->hWnd, &rect ); GetClientRect( w->hWnd, &rect ); w->w = rect.right - rect.left; w->h = rect.bottom - rect.top; *width = w->w; *height = w->h; } void wWinSetSize( wWin_p w, POS_T width, POS_T height ) { RECT rect; w->w = width; w->h = height; rect.left = 0; rect.top = 0; rect.right = w->w /*+w->padX*/; rect.bottom = w->h /*+w->padY*/; AdjustWindowRect( &rect, w->style, (w->option&F_MENUBAR)?1:0 ); rect.bottom += mFixBorderH; if (!SetWindowPos( w->hWnd, HWND_TOP, CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top, SWP_NOMOVE|SWP_NOZORDER)) mswFail("wWinSetSize"); InvalidateRect( w->hWnd, NULL, TRUE ); } static int blocking; static void blockingLoop( void ) { MSG msg; int myBlocking=blocking; while (blocking>=myBlocking && GetMessage( &msg, NULL, 0, 0 )) { if ( #ifdef DOTRANSACCEL (!TranslateAccelerator( mswWin->hWnd, hMswAccel, &msg )) && #endif (!mswTranslateAccelerator( mswWin->hWnd, &msg )) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } } static void savePos( wWin_p win ) { char posStr[20]; WINDOWPLACEMENT windowPlace; wPos_t w, h; RECT rect; if ( win->nameStr && IsWindowVisible( win->hWnd) /*&& !IsIconic( win->hWnd )*/ ) { windowPlace.length = sizeof windowPlace; GetWindowPlacement( win->hWnd, &windowPlace ); if (win->option&F_RECALLPOS) { wsprintf( posStr, "%d %d", windowPlace.rcNormalPosition.left, windowPlace.rcNormalPosition.top ); wPrefSetString( "msw window pos", win->nameStr, posStr ); if (win->option&F_RESIZE) { GetClientRect( win->hWnd, &rect ); w = rect.right - rect.left; h = rect.bottom - rect.top; w = windowPlace.rcNormalPosition.right - windowPlace.rcNormalPosition.left; h = windowPlace.rcNormalPosition.bottom - windowPlace.rcNormalPosition.top; w -= mResizeBorderW*2; h -= mResizeBorderH*2 + mTitleH; if (win->option&F_MENUBAR) h -= mMenuH; wsprintf( posStr, "%d %d %d", ( windowPlace.showCmd == SW_SHOWMINIMIZED ? 1 : (windowPlace.showCmd == SW_SHOWMAXIMIZED ? 2 : 0 ) ), w, h ); wPrefSetString( "msw window size", win->nameStr, posStr ); } } } } void wWinShow( wWin_p win, BOOL_T show ) { wPos_t x, y; wWin_p win1; win->busy = TRUE; if (show) { if (win->centerWin) { x = (screenWidth-win->w)/2; y = (screenHeight-win->h)/2; if (x<0) y = 0; if (x<0) y = 0; if (!SetWindowPos( win->hWnd, HWND_TOP, x, y, CW_USEDEFAULT, CW_USEDEFAULT, SWP_NOSIZE|SWP_NOZORDER)) mswFail( "wWinShow:SetWindowPos()" ); } win->centerWin = FALSE; win->shown = TRUE; if (mswHWnd == (HWND)0 || !IsIconic(mswHWnd) ) { ShowWindow( win->hWnd, SW_SHOW ); if (win->focusChainFirst) { SetFocus( win->focusChainFirst->hWnd ); } win->pendingShow = FALSE; if ( mswWinBlockEnabled && (win->option & F_BLOCK) ) { blocking++; inMainWndProc = FALSE; for ( win1 = winFirst; win1; win1=(wWin_p)win1->next ) { if ( win1->shown && win1 != win ) { if (win1->modalLevel == 0 ) EnableWindow( win1->hWnd, FALSE ); win1->modalLevel++; } } win->busy = FALSE; blockingLoop(); } } else { win->pendingShow = TRUE; needToDoPendingShow = TRUE; } } else { savePos( win ); ShowWindow( win->hWnd, SW_HIDE ); /*SetWindowPos( win->hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_HIDEWINDOW );*/ if ( mswWinBlockEnabled && (win->option & F_BLOCK) ) { blocking--; for ( win1 = winFirst; win1; win1=(wWin_p)win1->next ) { if ( win1->shown && win1 != win ) { if ( win1->modalLevel > 0 ) win1->modalLevel--; if (win1->modalLevel == 0 ) EnableWindow( win1->hWnd, TRUE ); } } } savePos( win ); win->pendingShow = FALSE; win->shown = FALSE; } win->busy = FALSE; } void wWinBlockEnable( wBool_t enabled ) { mswWinBlockEnabled = enabled; } wBool_t wWinIsVisible( wWin_p w ) { return IsWindowVisible(w->hWnd); } void wWinSetTitle( wWin_p w, const char * title ) { SetWindowText( w->hWnd, title ); } void wWinSetBusy( wWin_p w, BOOL_T busy ) { HMENU menuH; UINT uEnable; int cnt, inx; wControl_p b; w->isBusy = busy; menuH = GetMenu( w->hWnd ); if (menuH) { uEnable = MF_BYPOSITION|(busy?MF_DISABLED:MF_ENABLED); cnt = GetMenuItemCount(menuH); for (inx=0; inxfirst; b; b=b->next) { if ( (b->option&BO_DISABLED)==0) { if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->setBusyProc) { mswCallBacks[b->type]->setBusyProc( b, busy ); } else { if (b->hWnd) EnableWindow( b->hWnd, (BOOL)!busy ); } } } } const char * wWinGetTitle( wWin_p w ) { return w->labelStr; } void wWinClear( wWin_p win, wPos_t x, wPos_t y, wPos_t width, wPos_t height ) { } void wSetCursor( wCursor_t cursor ) { switch ( cursor ) { case wCursorNormal: case wCursorQuestion: default: SetCursor( LoadCursor( NULL, IDC_ARROW ) ); break; case wCursorWait: SetCursor( LoadCursor( NULL, IDC_WAIT ) ); break; case wCursorCross: SetCursor( LoadCursor( NULL, IDC_CROSS ) ); break; case wCursorIBeam: SetCursor( LoadCursor( NULL, IDC_IBEAM ) ); break; } curCursor = cursor; } void wWinDoCancel( wWin_p win ) { wControl_p b; for (b=win->first; b; b=b->next) { if ((b->type == B_BUTTON) && (b->option & BB_CANCEL) ) { mswButtPush( b ); } } } unsigned long wGetTimer( void ) { return (unsigned long)GetTickCount(); } /* ****************************************************************************** * * Control Utilities * ****************************************************************************** */ void wControlSetHelp( wControl_p b, const char * help ) { if (b->helpStr) free(CAST_AWAY_CONST b->helpStr); if (help) b->helpStr = mswStrdup( help ); else b->helpStr = NULL; } /** * Add control to circular list of synonymous controls. Synonymous controls are kept in sync by * calling wControlLinkedActive for one member of the list * * \param IN first control * \param IN second control * \return none */ void wControlLinkedSet( wControl_p b1, wControl_p b2 ) { b2->synonym = b1->synonym; if( b2->synonym == NULL ) b2->synonym = b1; b1->synonym = b2; } /** * Activate/deactivate a group of synonymous controls. * * \param IN control * \param IN state * \return none */ void wControlLinkedActive( wControl_p b, int active ) { wControl_p savePtr = b; if( savePtr->type == B_MENUITEM ) wMenuPushEnable( (wMenuPush_p)savePtr, active ); else wControlActive( savePtr, active ); savePtr = savePtr->synonym; while( savePtr && savePtr != b ) { if( savePtr->type == B_MENUITEM ) wMenuPushEnable( (wMenuPush_p)savePtr, active ); else wControlActive( savePtr, active ); savePtr = savePtr->synonym; } } void wControlShow( wControl_p b, BOOL_T show ) { RECT rc; if (show) { if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->repaintProc) mswCallBacks[b->type]->repaintProc( b->parent->hWnd, b ); } else { if( b->labelStr ) { rc.left = b->labelX; rc.right = b->x; rc.top = b->labelY; rc.bottom = b->labelY+b->h; InvalidateRect( ((wControl_p)b->parent)->hWnd, &rc, TRUE ); } } if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->showProc) { mswCallBacks[b->type]->showProc( b, show ); } else { ShowWindow( b->hWnd, show?SW_SHOW:SW_HIDE ); #ifdef SHOW_DOES_SETFOCUS if (show && (b->option&BO_READONLY)==0 && b->hWnd != GetFocus() ) { hWnd = SetFocus( b->hWnd ); } #endif } b->shown = show; } void wControlSetFocus( wControl_p b ) { if ( b->hWnd ) SetFocus( b->hWnd ); } void wControlActive( wControl_p b, int active ) { if (active) b->option &= ~BO_DISABLED; else b->option |= BO_DISABLED; if (b->parent->isBusy) return; if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->setBusyProc) { mswCallBacks[b->type]->setBusyProc( b, !active ); } else { EnableWindow( b->hWnd, (BOOL)active ); InvalidateRect( b->hWnd, NULL, TRUE ); } } const char * wControlGetHelp( wControl_p b ) { return b->helpStr; } wPos_t wLabelWidth( const char * labelStr ) { int lab_l; HDC hDc; DWORD dw; LABELFONTDECL hDc = GetDC( mswHWnd ); lab_l = strlen(labelStr); LABELFONTSELECT dw = GetTextExtent( hDc, CAST_AWAY_CONST labelStr, lab_l ); LABELFONTRESET ReleaseDC( mswHWnd, hDc ); return LOWORD(dw) + 5; } wPos_t wControlGetWidth( wControl_p b) /* Control */ { return b->w; } wPos_t wControlGetHeight( wControl_p b) /* Control */ { return b->h; } wPos_t wControlGetPosX( wControl_p b) /* Control */ { return b->x; } wPos_t wControlGetPosY( wControl_p b) /* Control */ { return b->y; } void wControlSetPos( wControl_p b, wPos_t x, wPos_t y ) { b->labelX = x; b->labelY = y+2; if (b->labelStr) { int lab_l; HDC hDc; DWORD dw; LABELFONTDECL hDc = GetDC( b->parent->hWnd ); LABELFONTSELECT lab_l = strlen(b->labelStr); dw = GetTextExtent( hDc, CAST_AWAY_CONST b->labelStr, lab_l ); b->labelX -= LOWORD(dw) + 5; LABELFONTRESET ReleaseDC( b->parent->hWnd, hDc ); } if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->setPosProc) { mswCallBacks[b->type]->setPosProc( b, x, y ); } else { b->x = x; b->y = y; if (b->hWnd) if (!SetWindowPos( b->hWnd, HWND_TOP, x, y, CW_USEDEFAULT, CW_USEDEFAULT, SWP_NOSIZE|SWP_NOZORDER)) mswFail("wControlSetPos"); } } void wControlSetLabel( wControl_p b, const char * labelStr ) { if ( b->type == B_RADIO || b->type == B_TOGGLE ) { ; } else { int lab_l; HDC hDc; DWORD dw; LABELFONTDECL hDc = GetDC( b->parent->hWnd ); lab_l = strlen(labelStr); LABELFONTSELECT dw = GetTextExtent( hDc, CAST_AWAY_CONST labelStr, lab_l ); LABELFONTRESET b->labelX = b->x - LOWORD(dw) - 5; ReleaseDC( b->parent->hWnd, hDc ); b->labelStr = mswStrdup( labelStr ); if (b->type == B_BUTTON) SetWindowText( b->hWnd, labelStr ); } } void wControlSetContext( wControl_p b, void * context ) { b->data = context; } static int controlHiliteWidth = 5; static int controlHiliteWidth2 = 3; void wControlHilite( wControl_p b, wBool_t hilite ) { HDC hDc; HPEN oldPen, newPen; int oldMode; if ( b == NULL ) return; if ( !IsWindowVisible(b->parent->hWnd) ) return; if ( !IsWindowVisible(b->hWnd) ) return; hDc = GetDC( b->parent->hWnd ); newPen = CreatePen( PS_SOLID, controlHiliteWidth, RGB(0,0,0) ); oldPen = SelectObject( hDc, newPen ); oldMode = SetROP2( hDc, R2_NOTXORPEN ); MoveTo( hDc, b->x-controlHiliteWidth2, b->y-controlHiliteWidth2 ); LineTo( hDc, b->x+b->w+controlHiliteWidth2, b->y-controlHiliteWidth2 ); LineTo( hDc, b->x+b->w+controlHiliteWidth2, b->y+b->h+controlHiliteWidth2 ); LineTo( hDc, b->x-controlHiliteWidth2, b->y+b->h+controlHiliteWidth2 ); LineTo( hDc, b->x-controlHiliteWidth2, b->y-controlHiliteWidth2 ); SetROP2( hDc, oldMode ); SelectObject( hDc, oldPen ); DeleteObject( newPen ); ReleaseDC( b->parent->hWnd, hDc ); } /* ***************************************************************************** * * Exported Utility Functions * ***************************************************************************** */ void wMessage( wWin_p w, const char * msg, int beep ) { HDC hDc; int oldRop; POS_T h; RECT rect; LABELFONTDECL if (beep) MessageBeep(0); GetClientRect( w->hWnd, &rect ); hDc = GetDC( w->hWnd ); oldRop = SetROP2( hDc, R2_WHITE ); h = w->h+2; Rectangle( hDc, 0, h, w->w, h ); SetROP2( hDc, oldRop ); LABELFONTSELECT TextOut( hDc, 0, h, msg, strlen(msg) ); LABELFONTRESET ReleaseDC( w->hWnd, hDc ); } void wExit( int rc ) { INDEX_T inx; wControl_p b; mswPutCustomColors(); wPrefFlush(); for ( inx=controlMap_da.cnt-1; inx>=0; inx-- ) { b = controlMap(inx).b; if (b != NULL) { if (b->type == W_MAIN || b->type == W_POPUP) { wWin_p w = (wWin_p)b; savePos( w ); if (w->winProc != NULL) w->winProc( w, wQuit_e, w->data ); } } } for ( inx=controlMap_da.cnt-1; inx>=0; inx-- ) { b = controlMap(inx).b; if (b != NULL) { if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->doneProc != NULL) mswCallBacks[b->type]->doneProc( b ); } controlMap(inx).b = NULL; } deleteBitmaps(); if (mswOldTextFont != (HFONT)0) DeleteObject( mswOldTextFont ); if (helpInitted) { WinHelp(mswHWnd, helpFile, HELP_QUIT, 0L ); helpInitted = FALSE; } if (balloonHelpHWnd) { HDC hDc; hDc = GetDC( balloonHelpHWnd ); SelectObject( hDc, balloonHelpOldFont ); DeleteObject( balloonHelpNewFont ); ReleaseDC( balloonHelpHWnd, hDc ); } #ifdef HELPSTR fclose( helpStrF ); #endif DestroyWindow( mswHWnd ); if (mswPalette) { DeleteObject( mswPalette ); /*DeleteObject( mswPrintPalette );*/ } } void wFlush( void ) { wWin_p win; inMainWndProc = FALSE; mswRepaintAll(); for (win=winFirst; win; win=(wWin_p)win->next) UpdateWindow( win->hWnd ); } void wUpdate( wWin_p win ) { UpdateWindow( win->hWnd ); } static wBool_t paused; static wAlarmCallBack_p alarmFunc; static setTriggerCallback_p triggerFunc; static wControl_p triggerControl; /** * Wait until the pause timer expires. During that time, the message loop is * handled and queued messages are processed */ static void pausedLoop( void ) { MSG msg; while (paused && GetMessage( &msg, NULL, 0, 0 )) { if ( (mswWin) && (!mswTranslateAccelerator( mswWin->hWnd, &msg )) ) { TranslateMessage( &msg ); } DispatchMessage( &msg ); } } /** * Timer callback function for the pause timer. The only purpose of this * timer proc is to clear the waiting flag and kill the timer itself. */ void CALLBACK TimerProc( HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime ) { if (idEvent == PAUSE_TIMER) { paused = FALSE; KillTimer( hWnd, PAUSE_TIMER ); } } /** * Pause the application for a specified time. */ void wPause( long msec ) { paused = TRUE; if (msec > 65000L) msec = 65000L; pauseTimer = SetTimer( mswHWnd, PAUSE_TIMER, (UINT)msec, TimerProc ); if (pauseTimer == 0) mswFail("wPause: No timers"); else pausedLoop(); } void wAlarm( long msec, wAlarmCallBack_p func ) { alarmFunc = func; if (msec > 65000L) msec = 65000L; alarmTimer = SetTimer( mswHWnd, ALARM_TIMER, (UINT)msec, NULL ); if (alarmTimer == 0) mswFail("wAlarm: No timers"); } void mswSetTrigger( wControl_p control, setTriggerCallback_p func ) { UINT msec = (UINT)500; triggerControl = control; triggerFunc = func; if (func == NULL && triggerTimer != 0) { KillTimer( mswHWnd, triggerTimer ); triggerTimer = 0; return; } if (msec > 65000L) msec = 65000L; triggerTimer = SetTimer( mswHWnd, TRIGGER_TIMER, (UINT)msec, NULL ); if (triggerTimer == 0) mswFail("wAlarm: No timers"); } void wBeep( void ) { MessageBeep( MB_OK ); } /** * Show a notification window with a yes/no reply and an icon. * * \param type IN type of message: Information, Warning, Error * \param msg IN message to display * \param yes IN text for accept button * \param no IN text for cancel button * \return True when accept was selected, false otherwise */ int wNoticeEx( int type, const char * msg, const char * yes, const char * no ) { int res; UINT flag; char *headline; switch( type ) { case NT_INFORMATION: flag = MB_ICONINFORMATION; headline = _("Information"); break; case NT_WARNING: flag = MB_ICONWARNING; headline = _("Warning"); break; case NT_ERROR: flag = MB_ICONERROR; headline = _("Error"); break; } res = MessageBox( mswHWnd, msg, headline, flag | MB_TASKMODAL|((no==NULL)?MB_OK:MB_YESNO) ); return res == IDOK || res == IDYES; } int wNotice( const char * msg, const char * yes, const char * no ) { int res; res = MessageBox( mswHWnd, msg, "Notice", MB_TASKMODAL|((no==NULL)?MB_OK:MB_YESNO) ); return res == IDOK || res == IDYES; } /** * Show a notification window with three choices and an icon. * * \param msg IN message to display * \param yes IN text for yes button * \param no IN text for no button * \param cancel IN text for cancel button * \return 1 for yes, -1 for no, 0 for cancel */ int wNotice3( const char * msg, const char * yes, const char * no, const char * cancel ) { int res; res = MessageBox( mswHWnd, msg, _("Warning"), MB_ICONWARNING | MB_TASKMODAL|MB_YESNOCANCEL ); if ( res == IDOK || res == IDYES ) return 1; else if ( res == IDNO ) return -1; else return 0; } void wHelp( const char * topic ) { char *pszHelpTopic; HWND hwndHelp; if (!helpInitted) { HtmlHelp( NULL, NULL, HH_INITIALIZE, (DWORD)&dwCookie) ; helpInitted = TRUE; } /* "c:\\help.chm::/intro.htm>mainwin", */ /* attention: always adapt constant value (10) to needed number of formatting characters */ pszHelpTopic = malloc( strlen( helpFile ) + strlen( topic ) + 10 ); assert( pszHelpTopic != NULL ); sprintf( pszHelpTopic, "/%s.html", topic ); hwndHelp = HtmlHelp(mswHWnd, helpFile, HH_DISPLAY_TOPIC, (DWORD_PTR)pszHelpTopic); if( !hwndHelp ) wNoticeEx( NT_ERROR, pszHelpTopic, "Ok", NULL ); free( pszHelpTopic ); } void doHelpMenu( void * context ) { HH_FTS_QUERY ftsQuery; if( !helpInitted ) { HtmlHelp( NULL, NULL, HH_INITIALIZE, (DWORD)&dwCookie) ; helpInitted = TRUE; } switch ((int)(long)context) { case 1: /* Contents */ HtmlHelp( mswHWnd, helpFile, HH_DISPLAY_TOC, (DWORD_PTR)NULL ); break; case 2: /* Search */ ftsQuery.cbStruct = sizeof( ftsQuery ); ftsQuery.fExecute = FALSE; ftsQuery.fStemmedSearch = FALSE; ftsQuery.fTitleOnly = FALSE; ftsQuery.pszSearchQuery = NULL; ftsQuery.pszWindow = NULL; HtmlHelp( mswHWnd, helpFile, HH_DISPLAY_SEARCH,(DWORD)&ftsQuery ); break; default: return; } helpInitted = TRUE; } void wMenuAddHelp( wMenu_p m ) { wMenuPushCreate( m, NULL, "&Contents", 0, doHelpMenu, (void*)1 ); wMenuPushCreate( m, NULL, "&Search for Help on...", 0, doHelpMenu, (void*)2 ); } void wSetBalloonHelp( wBalloonHelp_t * bh ) { balloonHelpStrings = bh; } void wEnableBalloonHelp( int enable ) { balloonHelpEnable = enable; } void wBalloonHelpUpdate ( void ) { } void wControlSetBalloonText( wControl_p b, const char * text ) { b->tipStr = mswStrdup( text ); } void startBalloonHelp( void ) { HDC hDc; DWORD extent; int w, h; RECT rect; POINT pt; wBalloonHelp_t * bh; const char * hs; HFONT hFont; if (!balloonHelpStrings) return; if (!balloonHelpEnable) return; if (balloonHelpHWnd) { if ( balloonHelpButton->tipStr ) { hs = balloonHelpButton->tipStr; } else { hs = balloonHelpButton->helpStr; if (!hs) return; for ( bh = balloonHelpStrings; bh->name && strcmp(bh->name,hs) != 0; bh++ ); if (!bh->name || !bh->value) return; balloonHelpButton->tipStr = hs = bh->value; } if (newHelp) { wControlSetBalloon( balloonHelpButton, 0, 0, hs ); } else { hDc = GetDC( balloonHelpHWnd ); hFont = SelectObject( hDc, mswLabelFont ); extent = GetTextExtent( hDc, CAST_AWAY_CONST hs, strlen(hs) ); w = LOWORD( extent ); h = HIWORD( extent ); pt.x = 0; if ( balloonHelpButton->type == B_RADIO || balloonHelpButton->type == B_TOGGLE ) { pt.y = balloonHelpButton->h; } else { GetClientRect( balloonHelpButton->hWnd, &rect ); pt.y = rect.bottom; } ClientToScreen( balloonHelpButton->hWnd, &pt ); if (pt.x + w+2 > screenWidth) pt.x = screenWidth-(w+2); if (pt.x < 0) pt.x = 0; SetWindowPos( balloonHelpHWnd, HWND_TOPMOST, pt.x, pt.y, w+6, h+4, SWP_SHOWWINDOW|SWP_NOACTIVATE ); SetBkColor( hDc, GetSysColor( COLOR_INFOBK )); TextOut( hDc, 2, 1, hs, strlen(hs) ); SelectObject( hDc, hFont ); ReleaseDC( balloonHelpHWnd, hDc ); } } } void closeBalloonHelp( void ) { if (balloonHelpTimer) { KillTimer( mswHWnd, balloonHelpTimer ); balloonHelpTimer = 0; } if (balloonHelpState == balloonHelpShow) if (balloonHelpHWnd) ShowWindow( balloonHelpHWnd, SW_HIDE ); balloonHelpState = balloonHelpIdle; } void wControlSetBalloon( wControl_p b, wPos_t dx, wPos_t dy, const char * msg ) { HDC hDc; DWORD extent; int w, h; RECT rect; POINT pt; HFONT hFont; if ( msg ) { hDc = GetDC( balloonHelpHWnd ); hFont = SelectObject( hDc, mswLabelFont ); extent = GetTextExtent( hDc, CAST_AWAY_CONST msg, strlen(msg) ); w = LOWORD( extent ); h = HIWORD( extent ); if ( b->type == B_RADIO || b->type == B_TOGGLE ) { pt.y = b->h; } else { GetClientRect( b->hWnd, &rect ); pt.y = rect.bottom; } pt.x = dx; pt.y -= dy; ClientToScreen( b->hWnd, &pt ); if (pt.x + w+2 > screenWidth) pt.x = screenWidth-(w+2); if (pt.x < 0) pt.x = 0; SetWindowPos( balloonHelpHWnd, HWND_TOPMOST, pt.x, pt.y, w+6, h+4, SWP_SHOWWINDOW|SWP_NOACTIVATE ); SetBkColor( hDc, GetSysColor( COLOR_INFOBK ) ); TextOut( hDc, 2, 1, msg, strlen(msg) ); SelectObject( hDc, hFont ); ReleaseDC( balloonHelpHWnd, hDc ); balloonHelpState = balloonHelpShow; balloonControlButton = b; } else { closeBalloonHelp(); } } int wGetKeyState( void ) { int rc, keyState; rc = 0; keyState = GetAsyncKeyState( VK_SHIFT ); if (keyState & 0x8000) rc |= WKEY_SHIFT; keyState = GetAsyncKeyState( VK_CONTROL ); if (keyState & 0x8000) rc |= WKEY_CTRL; keyState = GetAsyncKeyState( VK_MENU ); if (keyState & 0x8000) rc |= WKEY_ALT; return rc; } /* ****************************************************************************** * * File Selection * ****************************************************************************** */ FILE * wFileOpen( const char * fileName, const char * mode ) { return fopen( fileName, mode ); } struct wFilSel_t { wWin_p parent; wFilSelMode_e mode; int option; const char * title; char * extList; wFilSelCallBack_p action; void * data; }; static char selFileName[1024]; static char selFileTitle[1024]; static char sysDirName[1024]; /** * Run the file selector. After the selector is finished an array of filenames is * created. Each filename will be fully qualified. The array and the number of * filenames are passed to a callback function. This is similar to the argc argv c * convention in C. Once the callback returns, the allocated strings are free'd. * * \param fs IN the file selector dialog * \param dirName IN the initial directory presented * \return FALSE on error, TRUE if ok */ int wFilSelect( struct wFilSel_t * fs, const char * dirName ) { int rc; OPENFILENAME ofn; char **fileName; char *nextFileName; int cntFiles; const char * ext; char defExt[4]; int i; if (dirName == NULL || dirName[0] == '\0' || strcmp(dirName, ".") == 0 ) { GetSystemDirectory( CAST_AWAY_CONST (dirName = sysDirName), sizeof sysDirName ); } memset( &ofn, 0, sizeof ofn ); ofn.lStructSize = sizeof ofn; ofn.hwndOwner = mswHWnd; ofn.lpstrFilter = fs->extList; ofn.nFilterIndex = 0; memset( selFileName, '\0', sizeof(selFileName)); ofn.lpstrFile = selFileName; ofn.nMaxFile = sizeof selFileName; selFileTitle[0] = '\0'; ofn.lpstrFileTitle = selFileTitle; ofn.nMaxFileTitle = sizeof selFileTitle; ofn.lpstrInitialDir = dirName; ofn.lpstrTitle = fs->title; ext = fs->extList + strlen(fs->extList)+1; if (*ext++ == '*' && *ext++ == '.') { for ( i=0; i<3 && ext[i] && ext[i]!=';'; i++ ) defExt[i] = ext[i]; defExt[i] = '\0'; } else { defExt[0] = '\0'; } ofn.lpstrDefExt = defExt; if ( fs->option & FS_MULTIPLEFILES ) { ofn.Flags = OFN_ALLOWMULTISELECT | OFN_LONGFILENAMES | OFN_EXPLORER; } else { ofn.Flags = OFN_LONGFILENAMES; } if (fs->mode == FS_LOAD) { ofn.Flags |= OFN_FILEMUSTEXIST; rc = GetOpenFileName( &ofn ); } else if (fs->mode == FS_SAVE) { ofn.Flags |= OFN_OVERWRITEPROMPT; rc = GetSaveFileName( &ofn ); } else if (fs->mode == FS_UPDATE) { rc = GetSaveFileName( &ofn ); } else return FALSE; if (!rc) return FALSE; nextFileName = selFileName; selFileName[ofn.nFileOffset - 1] = '\0'; cntFiles = 0; while (*nextFileName) { cntFiles++; nextFileName = nextFileName + strlen( nextFileName ) + 1; } // strings were counted including the path on its own so reduce the count cntFiles--; // build up the array of filenames fileName = malloc(sizeof(nextFileName) * cntFiles); nextFileName = selFileName + ofn.nFileOffset; for ( i=0; i < cntFiles; i++) { fileName[ i ] = malloc(strlen(selFileName) + strlen(nextFileName) + 2 ); strcpy(fileName[ i ], selFileName); strcat(fileName[ i ], FILE_SEP_CHAR); strcat(fileName[ i ], nextFileName); nextFileName = nextFileName + strlen( nextFileName ) + 1; } fs->action( cntFiles, fileName, fs->data ); for (i=0; i < cntFiles; i++) { free( fileName[ i ] ); } free( fileName ); return TRUE; } struct wFilSel_t * wFilSelCreate( wWin_p parent, wFilSelMode_e mode, int option, const char * title, const char * extList, wFilSelCallBack_p action, void * data ) { char * cp; struct wFilSel_t * ret; int len; ret = (struct wFilSel_t*)malloc(sizeof *ret); ret->parent = parent; ret->mode = mode; ret->option = option; ret->title = mswStrdup(title); len = strlen(extList); ret->extList = (char*)malloc(len+2); strcpy(ret->extList,extList); for ( cp=ret->extList; *cp; cp++ ) { if (*cp == '|') *cp = '\0'; } *++cp = '\0'; ret->action = action; ret->data = data; return ret; } const char * wMemStats( void ) { int rc; static char msg[80]; long usedSize = 0; long usedCnt = 0; long freeSize = 0; long freeCnt = 0; _HEAPINFO heapinfo; heapinfo._pentry = NULL; while ( (rc=_heapwalk( &heapinfo )) == _HEAPOK ) { switch (heapinfo._useflag) { case _FREEENTRY: freeSize += (long)heapinfo._size; freeCnt++; break; case _USEDENTRY: usedSize += (long)heapinfo._size; usedCnt++; break; } } sprintf( msg, "Used: %ld(%ld), Free %ld(%ld)%s", usedSize, usedCnt, freeSize, freeCnt, (rc==_HEAPOK)?"": (rc==_HEAPEMPTY)?"": (rc==_HEAPBADBEGIN)?", BADBEGIN": (rc==_HEAPEND)?"": (rc==_HEAPBADPTR)?", BADPTR": ", Unknown Heap Status" ); return msg; } /* ***************************************************************************** * * Main * ***************************************************************************** */ static wControl_p getControlFromCursor( HWND hWnd, wWin_p * winR ) { POINT pt; wWin_p w; wControl_p b; wIndex_t inx; HWND hTopWnd; if (winR) *winR = NULL; GetCursorPos( &pt ); hTopWnd = GetActiveWindow(); inx = GetWindowWord( hWnd, 0 ); if ( inx < CONTROL_BASE || inx > controlMap_da.cnt ) { /* Unknown control */ /*MessageBeep( MB_ICONEXCLAMATION );*/ return NULL; } w=(wWin_p)controlMap(inx-CONTROL_BASE).b; if (!w) return NULL; if (w->type != W_MAIN && w->type != W_POPUP) return NULL; if ( winR ) *winR = w; ScreenToClient( hWnd, &pt ); for (b = w->first; b; b=b->next) { if (b->type == B_BOX || b->type == B_LINES) continue; if (b->hWnd == NULL) continue; if (IsWindowVisible( b->hWnd ) == FALSE) continue; if (pt.x > b->x && pt.x < b->x+b->w && pt.y > b->y && pt.y < b->y+b->h ) return b; } return b; } /** * Window function for the main window and all popup windows. * */ LRESULT FAR PASCAL MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { int inx; wWin_p w; wControl_p b, oldW; int child = ((GetWindowLong( hWnd, GWL_STYLE) & WS_CHILD) != 0); POS_T newW, newH; RECT rect; PAINTSTRUCT ps; HWND hWnd2; LRESULT ret; HDC hDc; wAccelKey_e extChar; switch (message) { case WM_MOUSEWHEEL: inx = GetWindowWord( hWnd, 0 ); b = getControlFromCursor( hWnd, NULL ); if( b && b->type == B_DRAW ) if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->messageProc) return mswCallBacks[b->type]->messageProc( (wControl_p)b, hWnd, message, wParam, lParam ); return( 0 ); case WM_DRAWITEM: case WM_COMMAND: case WM_MEASUREITEM: case WM_NOTVALID: if (WCMD_PARAM_ID == IDM_DOHELP) { b = getControlFromCursor( hWnd, NULL ); closeBalloonHelp(); if (!b) return 0L; if (b->helpStr) wHelp( b->helpStr ); return 0L; } closeBalloonHelp(); if (WCMD_PARAM_ID < CONTROL_BASE || WCMD_PARAM_ID > (WPARAM)controlMap_da.cnt) break; b = controlMap(WCMD_PARAM_ID-CONTROL_BASE).b; if (!b) break; if( b->type == B_BITMAP ) { // draw the bitmap mswDrawIcon(((LPDRAWITEMSTRUCT)lParam)->hDC, 0, 0, (wIcon_p)(b->data), FALSE, (COLORREF)0, (COLORREF)0 ); return( TRUE ); } else { mswSetFocus( b ); ret = 0L; if (!inMainWndProc) { inMainWndProc = TRUE; if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->messageProc) { ret = mswCallBacks[b->type]->messageProc( b, hWnd, message, wParam, lParam ); } inMainWndProc = FALSE; } return ret; } case WM_PAINT: inx = GetWindowWord( hWnd, 0 ); if (inx >= CONTROL_BASE && inx <= controlMap_da.cnt && (w = (wWin_p)controlMap(inx-CONTROL_BASE).b) && (w->type == W_MAIN || w->type == W_POPUP) && (!IsIconic(mswHWnd)) && (GetUpdateRect( hWnd, &rect, FALSE ) ) ) { BeginPaint( hWnd, &ps ); for (b=w->first; b; b=b->next ) { if (b->shown && mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->repaintProc) mswCallBacks[b->type]->repaintProc( hWnd, b ); } EndPaint( hWnd, &ps ); return 1L; } break; case WM_SIZE: inx = GetWindowWord( hWnd, 0 ); if (inx < CONTROL_BASE || inx > controlMap_da.cnt) break; w = (wWin_p)controlMap(inx-CONTROL_BASE).b; if (!w) break; if (w->type != W_MAIN && w->type != W_POPUP) break; if (w->busy) break; switch( wParam ) { case SIZE_MAXIMIZED: case SIZE_MINIMIZED: case SIZE_RESTORED: newW = LOWORD( lParam ); /* WIN32?? */ newH = HIWORD( lParam ); /* WIN32?? */ if (newW <= 0 || newH <= 0) break; if (newW == w->w && newH == w->h) break; GetWindowRect( w->hWnd, &rect ); GetClientRect( w->hWnd, &rect ); InvalidateRect( w->hWnd, NULL, TRUE ); w->w = newW; w->h = newH; if (w->winProc) w->winProc( w, wResize_e, w->data ); break; default: break; } break; case WM_CHAR: case WM_KEYUP: inx = GetWindowWord( hWnd, 0 ); if ( inx < CONTROL_BASE || inx > controlMap_da.cnt ) break; w = (wWin_p)controlMap(inx-CONTROL_BASE).b; if (!w) break; if (w->type != W_MAIN && w->type != W_POPUP) { if (mswCallBacks[w->type] != NULL && mswCallBacks[w->type]->messageProc) return mswCallBacks[w->type]->messageProc( (wControl_p)w, hWnd, message, wParam, lParam ); break; } extChar = translateExtKey( WCMD_PARAM_ID ); if (message == WM_KEYUP ) { if (extChar == wAccelKey_None) break; if (extChar == wAccelKey_Back) break; } b = getControlFromCursor( hWnd, NULL ); closeBalloonHelp(); if (b && b->type == B_DRAW) { return SendMessage( b->hWnd, WM_CHAR, wParam, lParam ); } switch (WCMD_PARAM_ID) { case 0x0D: /* CR - push default button */ for (b=w->first; b; b=b->next) { if (b->type == B_BUTTON && (b->option & BB_DEFAULT) != 0) { inMainWndProc = TRUE; if (mswCallBacks[B_BUTTON] != NULL && mswCallBacks[B_BUTTON]->messageProc) { ret = mswCallBacks[B_BUTTON]->messageProc( b, b->hWnd, WM_COMMAND, wParam, lParam ); } inMainWndProc = FALSE; break; } } return 0L; case 0x1B: /* ESC - push cancel button */ for (b=w->first; b; b=b->next) { if (b->type == B_BUTTON && (b->option & BB_CANCEL) != 0) { inMainWndProc = TRUE; if (mswCallBacks[B_BUTTON] != NULL && mswCallBacks[B_BUTTON]->messageProc) { ret = mswCallBacks[B_BUTTON]->messageProc( b, b->hWnd, WM_COMMAND, wParam, lParam ); } inMainWndProc = FALSE; break; } } mswSetTrigger( (wControl_p)TRIGGER_TIMER, NULL ); return 0L; case 0x20: /* SPC - push current button with focus */ if ( (b=w->focusChainNext) != NULL ) { switch (b->type) { case B_BUTTON: case B_CHOICEITEM: inMainWndProc = TRUE; if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->messageProc) { ret = mswCallBacks[b->type]->messageProc( b, b->hWnd, WM_COMMAND, MAKELPARAM( LOWORD(wParam), BN_CLICKED), (LPARAM)(b->hWnd) ); } inMainWndProc = FALSE; break; } } return 0L; case 0x09: /* TAB - jump to next control */ if ( w->focusChainNext ) { for ( b = w->focusChainNext->focusChainNext; b!=w->focusChainNext; b=b->focusChainNext ) { if( IsWindowVisible(b->hWnd) && IsWindowEnabled(b->hWnd)) break; } oldW = w->focusChainNext; w->focusChainNext = b; if (!inMainWndProc) { inMainWndProc = TRUE; SetFocus( b->hWnd ); /* if( b->type == B_BUTTON) InvalidateRect( b->hWnd, NULL, TRUE ); */ if( oldW->type == B_BUTTON) InvalidateRect( oldW->hWnd, NULL, TRUE ); inMainWndProc = FALSE; } } return 0L; } /* Not a Draw control */ MessageBeep( MB_ICONHAND ); return 0L; break; case WM_ENABLE: if (wParam == 1) { /* WIN32??? */ hWnd2 = SetFocus( hWnd ); } break; case WM_F1DOWN: if ((hWnd2 = GetActiveWindow()) == hWnd || (inx=GetWindowWord(hWnd2,0)) < CONTROL_BASE || inx > controlMap_da.cnt ) return DefWindowProc( hWnd, message, wParam, lParam ); b=controlMap(inx-CONTROL_BASE).b; if (!b) break; closeBalloonHelp(); wHelp( b->helpStr ); return 0L; case WM_SETCURSOR: /*if (any buttons down) break;*/ wSetCursor( curCursor ); if (!mswAllowBalloonHelp) break; if (IsIconic(mswHWnd)) break; b = getControlFromCursor(hWnd, NULL); if ( b == balloonControlButton ) break; if ( /*(!IsWindowEnabled(hWnd))*/ GetActiveWindow() != hWnd || (!b) || b->type == B_DRAW || b->helpStr == NULL ) { closeBalloonHelp(); break; } if ( b != balloonHelpButton ) closeBalloonHelp(); if (balloonHelpState != balloonHelpIdle) { break; } balloonHelpTimer = SetTimer( mswHWnd, BALLOONHELP_TIMER, balloonHelpTimeOut, NULL ); if (balloonHelpTimer == (UINT)0) break; balloonHelpState = balloonHelpWait; balloonHelpButton = b; break; case WM_SYSCOMMAND: inx = GetWindowWord( hWnd, 0 ); if (inx < CONTROL_BASE || inx > controlMap_da.cnt) break; w = (wWin_p)controlMap(inx-CONTROL_BASE).b; if (!w) break; if (w->type != W_POPUP) break; if (w->busy) break; if ( (wParam&0xFFF0) != SC_CLOSE ) break; if (w->winProc) w->winProc( w, wClose_e, w->data ); wWinShow( w, FALSE ); return 0L; case WM_CLOSE: inx = GetWindowWord( hWnd, 0 ); if (inx < CONTROL_BASE || inx > controlMap_da.cnt) break; w = (wWin_p)controlMap(inx-CONTROL_BASE).b; if (!w) break; if (w->type == W_MAIN) { /* It's the big one! */ /* call main window procedure for processing of shutdown */ if( w->winProc ) (w->winProc( w, wClose_e, NULL )); return 0L; } case WM_DESTROY: if ( hWnd == mswHWnd ) { PostQuitMessage(0L); return 0L; } break; case WM_TIMER: if (wParam == ALARM_TIMER) { KillTimer( mswHWnd, alarmTimer ); alarmFunc(); } else if (wParam == TRIGGER_TIMER) { KillTimer( mswHWnd, triggerTimer ); triggerTimer = 0; if (triggerFunc) triggerFunc( triggerControl ); } else if (wParam == BALLOONHELP_TIMER) { KillTimer( hWnd, balloonHelpTimer ); balloonHelpTimer = (UINT)0; startBalloonHelp(); } return 0L; case WM_MENUSELECT: mswAllowBalloonHelp = TRUE; closeBalloonHelp(); break; case WM_WINDOWPOSCHANGED: if (hWnd == mswHWnd && !IsIconic(hWnd) && needToDoPendingShow) { for (w=winFirst; w; w=(wWin_p)w->next) { if (w->hWnd != mswHWnd && w->pendingShow ) ShowWindow( w->hWnd, SW_SHOW ); w->pendingShow = FALSE; } needToDoPendingShow = FALSE; } break; case 51: count51++; /*return NULL;*/ #ifdef LATER case WM_SETFOCUS: hDc = GetDC( hWnd ); rc = RealizePalette( hDc ); ReleaseDC( hWnd, hDc ); inx = GetWindowWord( hWnd, 0 ); if ( inx < CONTROL_BASE || inx > controlMap_da.cnt ) break; w = (wWin_p)controlMap(inx-CONTROL_BASE).b; if (!w) break; if (w->type != W_MAIN && w->type != W_POPUP) break; for (b=w->first; b; b=b->next) { if (b->hWnd && (b->type == B_BUTTON || b->type==B_DRAW)) { hDc = GetDC( b->hWnd ); rc = RealizePalette( hDc ); ReleaseDC( b->hWnd, hDc ); } } break; #endif case WM_PALETTECHANGED: if (wParam == (WPARAM)hWnd) return 0L; case WM_QUERYNEWPALETTE: if (mswPalette) { hDc = GetDC( hWnd ); SelectPalette( hDc, mswPalette, 0 ); inx = RealizePalette( hDc ); ReleaseDC( hWnd, hDc ); if (inx>0) InvalidateRect( hWnd, NULL, TRUE ); return inx; } case WM_ACTIVATE: if ( LOWORD(wParam) == WA_INACTIVE ) closeBalloonHelp(); break; case WM_HSCROLL: case WM_VSCROLL: b = getControlFromCursor( hWnd, NULL ); if (!b) break; /*mswSetFocus( b );*/ ret = 0L; if (!inMainWndProc) { inMainWndProc = TRUE; if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->messageProc) { ret = mswCallBacks[b->type]->messageProc( b, hWnd, message, wParam, lParam ); } inMainWndProc = FALSE; } return ret; case WM_LBUTTONDOWN: case WM_MOUSEMOVE: case WM_LBUTTONUP: b = getControlFromCursor( hWnd, NULL ); if (!b) break; /*mswSetFocus( b );*/ ret = 0L; if (!inMainWndProc) { inMainWndProc = TRUE; if (mswCallBacks[b->type] != NULL && mswCallBacks[b->type]->messageProc) { ret = mswCallBacks[b->type]->messageProc( b, hWnd, message, wParam, lParam ); } inMainWndProc = FALSE; } return ret; default: ; } return DefWindowProc( hWnd, message, wParam, lParam ); } /* ***************************************************************************** * * INIT * ***************************************************************************** */ /** * Register window classes used by the application. These are the main window, * the popup windows, the tooltip window and the drawing area. * * \param hinstCurrent IN application instance * \return FALSE in case of error, else TRUE */ static BOOL InitApplication( HINSTANCE hinstCurrent ) { WNDCLASS wc; wc.style = 0L; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 4; wc.hInstance = hinstCurrent; wc.hIcon = LoadIcon( hinstCurrent, "MSWAPPICON" ); wc.hCursor = NULL; wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = "MswMainWindow"; if (!RegisterClass(&wc)) { mswFail("RegisterClass(MainWindow)"); return FALSE; } wc.style = CS_SAVEBITS; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 8; wc.hInstance = hinstCurrent; wc.hIcon = LoadIcon( NULL, "wAppIcon" ); wc.hCursor = NULL; wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wc.lpszMenuName = "GenericMenu"; wc.lpszClassName = "MswPopUpWindow"; if (!RegisterClass(&wc)) { mswFail("RegisterClass(PopUpWindow)"); return FALSE; } wc.style = CS_SAVEBITS; wc.lpfnWndProc = DefWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 4; wc.hInstance = hinstCurrent; wc.hIcon = 0; wc.hCursor = 0; wc.hbrBackground = CreateSolidBrush( GetSysColor( COLOR_INFOBK ) ); wc.lpszMenuName = NULL; wc.lpszClassName = "MswBalloonHelp"; if (!RegisterClass(&wc)) { mswFail("RegisterClass(BalloonHelp)"); return FALSE; } wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; wc.lpfnWndProc = mswDrawPush; wc.lpszClassName = mswDrawWindowClassName; wc.cbWndExtra = 4; if (!RegisterClass(&wc)) { mswFail("RegisterClass(drawClass)"); return FALSE; } return TRUE; } /** * Standard entry point for the app. Nothing special, * create some window classes, initialize some global * variables with system information, call the application main * and finally process the message queue. */ int PASCAL WinMain( HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, LPSTR lpszCmdLine, int nCmdShow ) { MSG msg; HDC hDc; char **argv; int argc; TEXTMETRIC tm; DWORD dw; if (!hinstPrevious) if (!InitApplication(hinstCurrent)) return FALSE; mswHInst = hinstCurrent; mTitleH = GetSystemMetrics( SM_CYCAPTION ) - 1; mFixBorderW = GetSystemMetrics( SM_CXBORDER ); mFixBorderH = GetSystemMetrics( SM_CYBORDER ); mResizeBorderW = GetSystemMetrics( SM_CXFRAME ); mResizeBorderH = GetSystemMetrics( SM_CYFRAME ); mMenuH = GetSystemMetrics( SM_CYMENU ) + 1; screenWidth = GetSystemMetrics( SM_CXSCREEN ); screenHeight = GetSystemMetrics( SM_CYSCREEN ); mswLabelFont = GetStockObject( DEFAULT_GUI_FONT ); hDc = GetDC( 0 ); mswScale = GetDeviceCaps( hDc, LOGPIXELSX ) / 96.0; if ( mswScale < 1.0 ) mswScale = 1.0; GetTextMetrics( hDc, &tm ); mswEditHeight = tm.tmHeight + 8; dw = GetTextExtent( hDc, "AXqypj", 6 ); mswEditHeight = HIWORD(dw)+2; ReleaseDC( 0, hDc ); mswCreateCheckBitmaps(); /* get the command line parameters in standard C style and pass them to the main function. The globals are predefined by Visual C */ argc = __argc; argv = __argv; mswWin = wMain( argc, (char**)argv ); if (mswWin == NULL) return 0; balloonHelpHWnd = CreateWindow( "MswBalloonHelp", "BalloonHelp", WS_POPUP|WS_BORDER, 0, 0, 80, 40, mswHWnd, NULL, mswHInst, NULL ); if (balloonHelpHWnd == (HWND)0) { mswFail( "CreateWindow(BALLOONHELP)" ); } else { hDc = GetDC( balloonHelpHWnd ); /* We need to remember this because the hDc gets changed somehow, /* and we when we select the oldFont back in we don't get newFont */ balloonHelpNewFont = CreateFont( - balloonHelpFontSize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, balloonHelpFaceName ); balloonHelpOldFont = SelectObject( hDc, balloonHelpNewFont ); ReleaseDC( balloonHelpHWnd, hDc ); } SetCursor( LoadCursor( NULL, IDC_ARROW ) ); while (GetMessage( &msg, NULL, 0, 0 )) { if (!mswTranslateAccelerator( mswWin->hWnd, &msg )) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } if( helpInitted == TRUE ) HtmlHelp( NULL, NULL, HH_UNINITIALIZE, (DWORD)dwCookie); return msg.wParam; }