/**************************************************************************** * * config.c * * Copyright (C) Microsoft Corporation 1990-1994. * All Rights reserved. * ***************************************************************************** * * Module Intent * * This module implements author-configurable options such as menus and * buttons. * *****************************************************************************/ #include "help.h" #pragma hdrstop #include "inc\frstuff.h" #include "inc\hwproc.h" #include "inc\sbutton.h" #include "resource.h" #define IBF_NONE 0x0000 #define IBF_STD 0x0001 #define cchBTNTEXT_SIZE 100 // 4.0 raised from 31 #define cchBTNID_SIZE 100 // 4.0 raised from 31 #define cchBTNMCRO_SIZE cchMAXBINDING // 512 #define BS_INITSIZE 22 typedef struct { HWND hwnd; BOOL fAuthorableButton; } DUP_HWND; /***************************************************************************** * * * Prototypes * * * *****************************************************************************/ BOOL CALLBACK BAddBtnDlg(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam); BOOL CALLBACK BExecMacroDlg(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam); INLINE static BOOL STDCALL BAddButton(HWND, UINT, HASH, HBTNS, PSTR, PCSTR); INLINE static int STDCALL CxFromSz(PSTR szButtonText); INLINE static BOOL STDCALL FChangeButtonMacro(HWND hwnd, HASH hash, PCSTR pszMacro); INLINE static BOOL STDCALL FDeleteButton(HWND hwnd, HASH hash); INLINE static PSTR STDCALL FFindMacro(HBTNS, PCSTR); static BOOL STDCALL FAbleButton(HASH, BOOL, int); static HWND STDCALL HwndCreateIconButton(HWND, PSTR); static HWND STDCALL HwndFromHash(HASH hash, HWND hwnd); static void STDCALL ResolveDuplicate(DUP_HWND* aDupHwnd, PSTR* ppsz, int iDup, PBS pbs, int iWindow); static void STDCALL FixDupButtonAccelerator(HBTNS hbtns, PSTR psz); INLINE static HMENU STDCALL GetFloatingMenu(VOID); INLINE static int STDCALL WCmpButtonQch(PCSTR, PCSTR); #ifdef _DEBUG VOID STDCALL VDebugAddButton(VOID); #endif /*************************************************************************** FUNCTION: StbCreate PURPOSE: Creates an array of pointers for strings PARAMETERS: void RETURNS: COMMENTS: MODIFICATION DATES: 05-Aug-1994 [ralphw] ***************************************************************************/ #define STB_INITSIZE 32 // initial number of strings we support PSTB STDCALL StbCreate(void) { PSTB pstb = (PSTB) LhAlloc(LMEM_ZEROINIT | LMEM_FIXED, sizeof(int) + (STB_INITSIZE * sizeof(PSTR))); if (pstb) { pstb->cEntries = STB_INITSIZE; } return pstb; } /*************************************************************************** FUNCTION: StbDelete PURPOSE: Deletes entire array of string pointers and the memory for the pointers themselves PARAMETERS: pstb RETURNS: COMMENTS: MODIFICATION DATES: 05-Aug-1994 [ralphw] ***************************************************************************/ void STDCALL StbDelete(PSTB pstb) { int i; for (i = 0; i < STB_INITSIZE; i++) { if (pstb->ppsz[i]) FreeLh(pstb->ppsz[i]); } FreeLh(pstb); } /*************************************************************************** FUNCTION: StbAddStr PURPOSE: Add a string to our string table PARAMETERS: ppstb pszString RETURNS: COMMENTS: MODIFICATION DATES: 05-Aug-1994 [ralphw] ***************************************************************************/ int STDCALL StbAddStr(PSTB* ppstb, PCSTR pszString) { PSTB pstb = *ppstb; // for notational convenience int i; for (i = 0; i < pstb->cEntries; i++) { if (!pstb->ppsz[i]) { pstb->ppsz[i] = LocalStrDup(pszString); return i; } } pstb->cEntries += STB_INITSIZE; pstb = (PSTB) GhResize(pstb, LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(int) + (pstb->cEntries * sizeof(PSTR))); if (!pstb) OOM(); // doesn't return *ppstb = pstb; pstb->ppsz[i] = LocalStrDup(pszString); return i; } /*************************************************************************** FUNCTION: StbDeleteStr PURPOSE: Delete a string from our string table PARAMETERS: pstb id RETURNS: COMMENTS: MODIFICATION DATES: 05-Aug-1994 [ralphw] ***************************************************************************/ void STDCALL StbDeleteStr(PSTB pstb, int id) { ASSERT(id >= 0 && id < pstb->cEntries); FreeLh(pstb->ppsz[id]); pstb->ppsz[id] = NULL; } void STDCALL StbReplaceStr(PSTB pstb, int id, PCSTR pszNew) { ASSERT(id >= 0 && id < pstb->cEntries); FreeLh(pstb->ppsz[id]); pstb->ppsz[id] = LocalStrDup(pszNew); if (!pstb->ppsz[id]) OOM(); } /***************************************************************************** * * * Defines * * * *****************************************************************************/ #define wSTART_MENUID 10001 #define wENTRIES 5 #define fMNU_SYSTEM 1 #define fMNU_AUTHOR 2 #define fMNU_POPUP 4 #define fMNU_DELETED 8 #define fKEY_SHIFT 1 #define fKEY_CONTROL 2 #define fKEY_ALT 4 #define MAX_POPUP_ADDONS 20 /***************************************************************************** * * * Typedefs * * * *****************************************************************************/ typedef struct { HASH hash; HASH hashOwner; HMENU hmenu; int wId; int wMacro; int wFlags; } MENUS, * PMENUS, * QMENUS; typedef struct { UINT wKey; UINT wShift; int wMacro; } ACC, *PACC, * QACC; typedef struct { int id; PSTR pszText; } POPUP_ADDON; typedef LH HMENUS; // Handle to authorable menu table typedef LH HACC; // Handle to accelerator table /***************************************************************************** * * * Static Variables * * * *****************************************************************************/ static PSTB pstbMenu; // Handle to string table for macros static HMENUS hmenus; // Handle to menu information static int cMnuTbl; // Total used entries in table static int maxMnuTbl; // Max entries in the table static DWORD wMenuId = wSTART_MENUID; // Menu id to use for new menu items static HACC hacc; // Handle to accelerator table static int maxAccTbl; // Current maximum for the ACC table static int cAccTbl; // Current count of ACC table static POPUP_ADDON aAddons[MAX_POPUP_ADDONS]; static int curAddOn = -1; BOOL fMenuChanged = TRUE; // Has the menu been altered? /***************************************************************************** * * * Prototypes * * * *****************************************************************************/ static VOID STDCALL ConfigMenu(VOID); static BOOL STDCALL FAddHmenu(HMENU, HASH, HASH, int, int, int); static PMENUS STDCALL PmenusFromHash(HASH, PMENUS); INLINE static PMENUS STDCALL PmenusFromId(int, PMENUS); /******************* ** ** Name: DisplayFloatingMenu ** ** Purpose: Display the floating menu (if there is one) ** ** Arguments: None. ** ** Returns: Nothing ** *******************/ VOID STDCALL DisplayFloatingMenu(POINT pt) { HMENU hmenu; if (hmenu = GetFloatingMenu()) { if (pt.x == -1) GetCursorPos(&pt); CheckMenuItem(hmenu, IDM_ONTOP_DEFAULT, (cntFlags.fsOnTop == ONTOP_NOTSET) ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, IDM_ONTOP_FORCEON, (cntFlags.fsOnTop == ONTOP_FORCEON) ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, IDM_ONTOP_FORCEOFF, (cntFlags.fsOnTop == ONTOP_FORCEOFF) ? MF_CHECKED : MF_UNCHECKED); if (fHelpAuthor) CheckMenuItem(hmenu, IDM_ASK_FIRST, fDebugState & fDEBUGASKFIRST ? MF_CHECKED : MF_UNCHECKED); TrackPopupMenu(hmenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwndNote ? hwndNote : ahwnd[MAIN_HWND].hwndParent, NULL); } } /*************************************************************************** * - Name: GetFloatingMenu - * Purpose: Gets the current floating menu (if any) * * Arguments: None. * * Returns: The menu handle or NULL if the floating menu is * empty. * * Globals Used: hmenuFloating. * ***************************************************************************/ INLINE static HMENU STDCALL GetFloatingMenu(VOID) { if (!hmenuFloating) { hmenuFloating = CreatePopupMenu(); if (hmenuFloating) { AddOurMenuItems(hmenuFloating, FALSE); #ifdef _DEBUG if (!hwndNote) { AppendMenu(hmenuFloating, MF_ENABLED | MF_STRING, HLPMENUBOOKMARKDEFINE, "&Define Bookmark..."); } #endif } } return hmenuFloating; } void STDCALL AddOurMenuItems(HMENU hmenu, BOOL fTempPopup) { HMENU hmenuPopup; if (!hwndNote) { AppendMenu(hmenu, MF_ENABLED | MF_STRING, HLPMENUEDITANNOTATE, GetStringResource(sidAnnotateMenu)); } AppendMenu(hmenu, MF_ENABLED | MF_STRING, HLPMENUEDITCPYSPL, GetStringResource(sidCopyMenu)); AppendMenu(hmenu, MF_ENABLED | MF_STRING, HLPMENUFILEPRINT, GetStringResource(sidPrintMenu)); if (!hwndNote) { if (fTempPopup) hmenuPopup = CreatePopupMenu(); else { if (!hmenuFont) hmenuPopup = hmenuFont = CreatePopupMenu(); else return; // we've already added these items } if (hmenuPopup) { AppendMenu(hmenu, MF_ENABLED | MF_POPUP, (UINT) hmenuPopup, GetStringResource(sidFont)); AppendMenu(hmenuPopup, MF_ENABLED | MF_STRING, IDM_FONTS_SMALLER, GetStringResource(sidSmall)); AppendMenu(hmenuPopup, MF_ENABLED | MF_STRING, IDM_FONTS_DEFAULT, GetStringResource(sidNormal)); AppendMenu(hmenuPopup, MF_ENABLED | MF_STRING, IDM_FONTS_BIGGER, GetStringResource(sidLarge)); } if (fTempPopup) hmenuPopup = CreatePopupMenu(); else { if (!hmenuOnTop) hmenuPopup = hmenuOnTop = CreatePopupMenu(); else return; // we've already added these items } if (hmenuPopup) { AppendMenu(hmenu, MF_ENABLED | MF_POPUP, (UINT) hmenuPopup, GetStringResource(sidOnTopMenu)); AppendMenu(hmenuPopup, MF_ENABLED | MF_STRING, IDM_ONTOP_DEFAULT, GetStringResource(sidTopDefault)); AppendMenu(hmenuPopup, MF_ENABLED | MF_STRING, IDM_ONTOP_FORCEON, GetStringResource(sidTopForceOn)); AppendMenu(hmenuPopup, MF_ENABLED | MF_STRING, IDM_ONTOP_FORCEOFF, GetStringResource(sidTopForceOff)); } if (!fDisableAuthorColors) AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_OVERRIDE_COLORS, GetStringResource(sidOverride)); if (fHelpAuthor) { AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_TOPIC_INFO, GetStringResource(sidTopicInfo)); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_ASK_FIRST, GetStringResource(sidAskHotspots)); } #ifdef _DEBUG AppendMenu(hmenu, MF_SEPARATOR, (UINT) hmenuPopup, ""); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_GENERATE_FTS, "Generate FTS Index"); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_DO_FIND, "Find..."); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_MEM_USAGE, "Memory Used"); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_ADD_BUTTON, "Add Button..."); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_FRAMES, "Frames"); AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_DISCARD_BITMAPS, "Discard Bitmaps"); AppendMenu(hmenu, MF_ENABLED | MF_STRING, HLPMENUFILEOPEN, "Open..."); #endif } else { GetAuthorFlag(); // popups don't normally read this flag if (fHelpAuthor) { // add Topic Information for popups AppendMenu(hmenu, MF_ENABLED | MF_STRING, IDM_TOPIC_INFO, GetStringResource(sidTopicInfo)); } } if (!hwndNote && curAddOn > -1) { int i; AppendMenu(hmenu, MF_SEPARATOR, (UINT) hmenuPopup, ""); for (i = 0; i <= curAddOn; i++) { AppendMenu(hmenu, MF_ENABLED | MF_STRING, aAddons[i].id, aAddons[i].pszText); } } } /******************* ** ** Name: AddAccelerator ** ** Purpose: Adds an accelerator to the accelerator table. ** ** Arguments: wKey - virtual key code of key ** wShift - shift state needed while the key is active ** 0 - unshifted ** 1 - shift ** 2 - control ** 4 - alt ** nszBinding - macro to execute for accelerator ** ** Returns: Nothing ** *******************/ void STDCALL AddAccelerator(UINT wKey, UINT wShift, PCSTR nszBinding) { int i; HACC haccT; // Temp handle for accelerator table PACC pacc; // Pointer to accelerator table ASSERT(cAccTbl <= maxAccTbl); if (hacc) { pacc = PtrFromGh(hacc); // Check table for previous definition for the key and shift for (i = 0; i < cAccTbl; i++) { if ((pacc[i].wShift == wShift) && (pacc[i].wKey == wKey)) { StbReplaceStr(pstbMenu, pacc[i].wMacro, nszBinding); fMenuChanged = TRUE; return; } } } if (cAccTbl == maxAccTbl) { // The table is full, so grow it if (haccT = (HACC) GhResize(hacc, 0, (maxAccTbl + wENTRIES) * sizeof(ACC))) { maxAccTbl += wENTRIES; hacc = haccT; } else { if (fHelpAuthor) Error(wERRS_NOADDACC, wERRA_RETURN); return; } } if (!hacc) { if (fHelpAuthor) Error(wERRS_NOADDACC, wERRA_RETURN); return; } pacc = PtrFromGh(hacc); pacc += cAccTbl; // Note has not been incremented yet pacc->wKey = wKey; // Insert key data and macro tag pacc->wShift = wShift; pacc->wMacro = StbAddStr(&pstbMenu, nszBinding); cAccTbl++; fMenuChanged = TRUE; } /******************* ** ** Name: AcceleratorExecute ** ** Purpose: Executes a macro if the keyboard is in the state added ** with AddAccelerator. ** ** ** Arguments: wKey - key currently being pressed. ** ** Returns: Nothing ** *******************/ BOOL STDCALL FAcceleratorExecute(UINT wKey) { UINT wShift = 0; // Current shift state. PACC pacc; // Pointer to accelerator table int i; if (hacc == NULL) return FALSE; if (GetKeyState(VK_SHIFT) & 0x8000) // Get the current shift state wShift |= fKEY_SHIFT; if (GetKeyState(VK_CONTROL) & 0x8000) wShift |= fKEY_CONTROL; if (GetKeyState(VK_MENU) & 0x8000) wShift |= fKEY_ALT; pacc = PtrFromGh(hacc); for (i = 0; i < cAccTbl; i++) { // Check table for the key and shift if ((pacc[i].wShift == wShift) && (pacc[i].wKey == wKey)) { Execute(PszFromPstb(pstbMenu, pacc[i].wMacro)); return TRUE; } } return FALSE; } /******************* ** ** Name: DoMenuStuff ** ** Purpose: This function is called as a result of a macro that wants ** to take some sort of menu action. ** ** Arguments: wParam: Indicates the type of modification. ** lParam: Data for this message (may be a local handle). ** ** Returns: nothing. ** *******************/ VOID STDCALL DoMenuStuff(WPARAM wParam, LPARAM lParam) { PMNUINFO pmnuinfo = (PMNUINFO) lParam; // Menu info structure passed. switch (wParam) { case MNU_RESET: ConfigMenu(); return; case MNU_FLOATING: { POINT pt; pt.x = -1; DisplayFloatingMenu(pt); return; } #ifdef OLD_WAY // see button.c case MNU_INSERTPOPUP: case MNU_INSERTITEM: if (wParam == MNU_INSERTPOPUP) InsertPopup(pmnuinfo->hashOwner, pmnuinfo->hashId, pmnuinfo->wPos, pmnuinfo->wFlags, pmnuinfo->Data); else { InsertItem(pmnuinfo->hashOwner, pmnuinfo->hashId, pmnuinfo->wPos, pmnuinfo->wFlags, pmnuinfo->Data, pmnuinfo->Data + strlen(pmnuinfo->Data) + 1); } break; case MNU_CHANGEITEM: ChangeMenuBinding(pmnuinfo->hashId, pmnuinfo->Data); break; case MNU_ABLE: AbleMenuItem(pmnuinfo->hashId, pmnuinfo->wFlags); break; case MNU_ACCELERATOR: AddAccelerator(pmnuinfo->wKey, pmnuinfo->wShift, pmnuinfo->Data); break; #endif } FreeLh((LH) lParam); } /*************************************************************************** * - Name: ConfigMenu - * Purpose: Does the initialization of the menu when the program * starts or when files are changed. * * Arguments: None. * * Returns: Nothing. * * Globals Used: hmenus, * ***************************************************************************/ #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif const char txtMnuHelp[] = "mnu_help"; const char txtMnuFile[] = "mnu_file"; const char txtMnuEdit[] = "mnu_edit"; const char txtMnuOptions[] = "mnu_options"; const char txtMnuFloating[] = "mnu_floating"; #ifndef NO_PRAGMAS #pragma data_seg() #endif static VOID STDCALL ConfigMenu(VOID) { HMENU hmenuT; HMENU hmenuNew; if (hmenuFloating) { DestroyMenu(hmenuFont); hmenuFont = NULL; DestroyMenu(hmenuOnTop); hmenuOnTop = NULL; DestroyMenu(hmenuFloating); hmenuFloating = NULL; } if (!fMenuChanged) return; while (curAddOn > -1) FreeLh(aAddons[curAddOn--].pszText); if (pstbMenu) StbDelete(pstbMenu); // Destroy and recreate string table pstbMenu = StbCreate(); // Recreate the menu info table if (hmenus) FreeLh(hmenus); cMnuTbl = 0; if ((hmenus = LhAlloc(LMEM_FIXED, sizeof(MENUS) * wENTRIES)) != NULL) maxMnuTbl = wENTRIES; else maxMnuTbl = 0; // Reinitialize the main menu if ((hmenuNew = LoadMenu(hInsNow, MAKEINTRESOURCE(RESOURCE_HELPMAIN)))) { hmenuT = GetMenu(ahwnd[MAIN_HWND].hwndParent); if (SetMenu(ahwnd[MAIN_HWND].hwndParent, hmenuNew)) { hmnuHelp = hmenuNew; if (hmenuT) DestroyMenu(hmenuT); } else hmenuNew = hmenuT; } else hmenuNew = GetMenu(ahwnd[MAIN_HWND].hwndParent); FAddHmenu((HMENU) -1, (HASH) -1L, HashFromSz(txtMnuFloating), -1, -1, fMNU_SYSTEM | fMNU_POPUP); if (hmenuNew) { FAddHmenu(hmenuNew, (HASH) -1, HashFromSz(txtMnuMain), -1, -1, fMNU_SYSTEM | fMNU_POPUP); if (hmenuT = GetSubMenu(hmenuNew, 0)) FAddHmenu(hmenuT, (HASH) -1, HashFromSz(txtMnuFile), -1, -1, fMNU_SYSTEM | fMNU_POPUP); if (hmenuT = GetSubMenu(hmenuNew, 1)) FAddHmenu(hmenuT, (HASH) -1, HashFromSz(txtMnuEdit), -1, -1, fMNU_SYSTEM | fMNU_POPUP); if (hmenuT = GetSubMenu(hmenuNew, 3)) FAddHmenu(hmenuT, (HASH) -1, HashFromSz(txtMnuOptions), -1, -1, fMNU_SYSTEM | fMNU_POPUP); #ifdef _DEBUG // There is one more menu under debug*/ if (hmenuT = GetSubMenu(hmenuNew, GetMenuItemCount(hmenuNew)-2)) #else if (hmenuT = GetSubMenu(hmenuNew, GetMenuItemCount(hmenuNew)-1)) #endif { FAddHmenu(hmenuT, (HASH) -1, HashFromSz(txtMnuHelp), -1, -1, fMNU_SYSTEM | fMNU_POPUP); } // save bookmark menu in a global hmenuBookmark = GetSubMenu(hmenuNew, 2); } if (hacc) FreeLh(hacc); // Recreate the accelerator key tbl cAccTbl = 0; if ((hacc = LhAlloc(LMEM_FIXED, sizeof(ACC) * wENTRIES)) != NULL) maxAccTbl = wENTRIES; else maxAccTbl = 0; fMenuChanged = FALSE; } /*************************************************************************** * - Name: FAddMenu - * Purpose: This routine adds a popup or an item to the global * menu table. * * Arguments: hmenu - popup menu handle * hashOwner - hash value for the owner of this item or popup * hash - actual hash for this item/popup. * wId - ID to associate with this item. * wMacro - string table tag for this item. * wFlags - data about the item (passed down). * * * Returns: TRUE iff the insert succeeds. * * Globals Used: hmenus. * ***************************************************************************/ static BOOL STDCALL FAddHmenu(HMENU hmenu, HASH hashOwner, HASH hash, int wId, int wMacro, int wFlags) { HMENUS hmenusT; PMENUS pmenus; ASSERT(cMnuTbl <= maxMnuTbl); if (cMnuTbl == maxMnuTbl) { // The table is full, so grow it if (hmenusT = (HMENUS) GhResize(hmenus, 0, (maxMnuTbl + wENTRIES) * sizeof(MENUS))) { maxMnuTbl += wENTRIES; hmenus = hmenusT; } else return FALSE; } if (!hmenus) return FALSE; pmenus = PtrFromGh(hmenus); if (PmenusFromHash(hash, pmenus) != NULL) return FALSE; pmenus += cMnuTbl; pmenus->hash = hash; pmenus->hashOwner = hashOwner; pmenus->hmenu = hmenu; pmenus->wId = wId; pmenus->wMacro = wMacro; pmenus->wFlags = wFlags; cMnuTbl++; return TRUE; } /*************************************************************************** * - Name: InsertPopup - * Purpose: Inserts a popup menu. * * Arguments: hashOwner - hash value of the menu to insert on * hashId - hash value for this menu * wPos - position on the menu (-1 == end) * wFlags - flags passed to InsertMenu() * nszText - Text on the menu item. * * Returns: Nothing. * * Globals Used: hmenus * **************************************************************************/ void STDCALL InsertPopup(HASH hashOwner, HASH hashId, int wPos, DWORD wFlags, PCSTR nszText) { PMENUS pmenus; HMENU hmenu = NULL; HMENU hmenuNew; if (hmenus == NULL) { // Table was never created! if (fHelpAuthor) Error(wERRS_NOPOPUP, wERRA_RETURN); return; } // Now we look for which menu to to attach the popup to pmenus = PtrFromGh(hmenus); if (((pmenus = PmenusFromHash(hashOwner, pmenus)) && (pmenus->wFlags & fMNU_POPUP))) hmenu = pmenus->hmenu; if (hmenu == NULL) { // We could not find the menu! if (fHelpAuthor) Error(wERRS_NOPOPUP, wERRA_RETURN); return; } if ((hmenuNew = CreateMenu()) == NULL) { if (fHelpAuthor) Error(wERRS_NOPOPUP, wERRA_RETURN); return; } wFlags |= MF_BYPOSITION; if (!InsertMenu(hmenu, wPos, wFlags | MF_POPUP, (UINT) hmenuNew, nszText)) { if (fHelpAuthor) Error(wERRS_NOPOPUP, wERRA_RETURN); DestroyMenu(hmenuNew); return; } if (!FAddHmenu(hmenuNew, hashOwner, hashId, (UINT16) -1, (UINT16) -1, fMNU_AUTHOR | fMNU_POPUP)) { if (fHelpAuthor) Error(wERRS_NOPOPUP, wERRA_RETURN); DeleteMenu(hmenu, wPos, MF_BYPOSITION); return; } fMenuChanged = TRUE; if (hmenu == GetMenu(ahwnd[MAIN_HWND].hwndParent)) DrawMenuBar(ahwnd[MAIN_HWND].hwndParent); } /*************************************************************************** * - Name: PmenusFromHash - * Purpose: gets a menu structure associated with a hash value * * Arguments: hash - hash value associated with the popup/item. * pmenus - pointer to the menu data table. * * Returns: a pointer to a MENUS structure or NULL if the menu is not * found. * ***************************************************************************/ static PMENUS STDCALL PmenusFromHash(HASH hash, PMENUS pmenus) { int i; for (i = 0; i < cMnuTbl; i++) { if ((pmenus->hash == hash) && !(pmenus->wFlags & fMNU_DELETED)) return pmenus; pmenus++; } return NULL; } /*************************************************************************** * - Name: MenuExecute - * Purpose: Will execute the binding assoicate with wId. * * Arguments: wId - id of menu item to execute * * Returns: Nothing. * * Globals Used: hmenus. * ***************************************************************************/ VOID STDCALL MenuExecute(int id) { PMENUS pmenus; PSTR pszMacro; if (id < wSTART_MENUID) return; pmenus = PtrFromGh(hmenus); if ((pmenus = PmenusFromId(id, pmenus)) == NULL) { if (fHelpAuthor) Error(wERRS_NOMENUMACRO, wERRA_RETURN); return; } pszMacro = LocalStrDup(PszFromPstb(pstbMenu, pmenus->wMacro)); if (pszMacro) { Execute(pszMacro); FreeGh(pszMacro); } } /*************************************************************************** * - Name: PmenusFromId - * Purpose: gets a menu structure associated with an id. * * Arguments: id - id (as sent by windows of a popup or item. * pmenus - pointer to the menu data table. * Returns: * a pointer to a MENUS structure or NULL if the menu is not * found. * ***************************************************************************/ INLINE static PMENUS STDCALL PmenusFromId(int id, PMENUS pmenus) { int i; for (i = 0; i < cMnuTbl; i++) { if ((pmenus->wId == id) && !(pmenus->wFlags & fMNU_DELETED)) return pmenus; pmenus++; } return NULL; } /*************************************************************************** * - Name: InsertItem - * Purpose: Inserts an item on a menu. * * Arguments: hashOwner - hash value of the owner popup for this item * hashId - hash value for this item * wPos - position to place on menu (-1 == end) * wFlags - wFlags - Window's flags for InsertMenu() * nszText - text for the menu item * nszBinding - macro associated with the menu item. * * Returns: Nothing * ***************************************************************************/ void STDCALL InsertItem(HASH hashOwner, HASH hashId, int wPos, DWORD wFlags, PCSTR nszText, PCSTR nszBinding) { HMENU hmenu = NULL; int wMacro; PMENUS pmenus; ASSERT(hmenus); if (hmenus == NULL) { // Table was never created! if (fHelpAuthor) Error(wERRS_NOITEM, wERRA_RETURN); return; } // Now we look for which menu to to attach the popup to pmenus = PtrFromGh(hmenus); if ((pmenus = PmenusFromHash(hashOwner, pmenus)) && (pmenus->wFlags & fMNU_POPUP)) hmenu = pmenus->hmenu; if (hmenu == NULL) { // We could not find the menu! if (fHelpAuthor) Error(wERRS_NOITEM, wERRA_RETURN); return; } wMacro = StbAddStr(&pstbMenu, nszBinding); wFlags |= MF_BYPOSITION; wFlags &= ~MF_POPUP; if (hmenu == ((HMENU) -1)) { if (++curAddOn >= MAX_POPUP_ADDONS) { if (fHelpAuthor) Error(wERRS_TOO_MANY_ADDONS, wERRA_RETURN); return; } aAddons[curAddOn].id = wMenuId; aAddons[curAddOn].pszText = LocalStrDup(nszText); } else if (!InsertMenu(hmenu, wPos, wFlags, wMenuId, nszText)) { if (fHelpAuthor) Error(wERRS_NOITEM, wERRA_RETURN); StbDeleteStr(pstbMenu, wMacro); return; } if (!FAddHmenu(hmenu, hashOwner, hashId, wMenuId, wMacro, fMNU_AUTHOR)) { if (fHelpAuthor) Error(wERRS_NOITEM, wERRA_RETURN); StbDeleteStr(pstbMenu, wMacro); DeleteMenu(hmenu, wMenuId, MF_BYCOMMAND); return; } wMenuId++; fMenuChanged = TRUE; if (hmenu == GetMenu(ahwnd[MAIN_HWND].hwndParent)) DrawMenuBar(ahwnd[MAIN_HWND].hwndParent); } /*************************************************************************** * - Name: DeleteMenuItem - * Purpose: Removes a menu item from a menu. * * Arguments: hash - hash value of the item. * * Returns: nothing. * * Globals Used: hmenus, pstbMenu * ***************************************************************************/ void STDCALL DeleteMenuItem(HASH hash) { PMENUS pmenus; PMENUS pmenusT; pmenus = PtrFromGh(hmenus); if ((pmenusT = PmenusFromHash(hash, pmenus)) == NULL) { if (fHelpAuthor) Error(wERRS_NODELITEM, wERRA_RETURN); return; } if (!(pmenusT->wFlags & fMNU_POPUP)) { if (DeleteMenu(pmenusT->hmenu, pmenusT->wId, MF_BYCOMMAND)) pmenusT->wFlags |= fMNU_DELETED; } StbDeleteStr(pstbMenu, pmenusT->wMacro); if (pmenusT->hmenu == GetMenu(ahwnd[MAIN_HWND].hwndParent)) DrawMenuBar(ahwnd[MAIN_HWND].hwndParent); } /*************************************************************************** * - Name: ChangeMenuBinding - * Purpose: Changes the macro associated with a menu item. * * Arguments: hash - hash value of the menu item to change. * nszBinding - new binding to associate with the menu item. * * Returns: nothing. * * Globals Used: hmenus, pstbMenu * ***************************************************************************/ void STDCALL ChangeMenuBinding(HASH hash, PCSTR nszBinding) { PMENUS pmenus = PtrFromGh(hmenus); if (((pmenus = PmenusFromHash(hash, pmenus)) == NULL) || (pmenus->wFlags & fMNU_POPUP) || !(pmenus->wFlags & fMNU_AUTHOR)) { if (fHelpAuthor) Error(wERRS_NOCHGITEM, wERRA_RETURN); return; } StbReplaceStr(pstbMenu, pmenus->wMacro, nszBinding); fMenuChanged = TRUE; } /*************************************************************************** * - Name: AbleMenuItem - * Purpose: To enable or disable a menu item. * * Arguments: hash - hash value of the item. * wFlags - flags directing the routine. * #define MF_ENABLED 0x0000 * #define MF_GRAYED 0x0001 * #define MF_DISABLED 0x0002 * * Returns: Nothing * * Globals Used: hmenus. * ***************************************************************************/ void STDCALL AbleMenuItem(HASH hash, UINT wFlags) { PMENUS pmenus; pmenus = PtrFromGh(hmenus); if ( ((pmenus = PmenusFromHash(hash, pmenus)) == NULL) || (pmenus->wFlags & fMNU_POPUP) || !(pmenus->wFlags & fMNU_AUTHOR) ) { if (fHelpAuthor) Error(wERRS_NOABLE, wERRA_RETURN); return; } wFlags &= ~MF_BYPOSITION; if (wFlags & BF_CHECK) CheckMenuItem(pmenus->hmenu, pmenus->wId, wFlags & ~BF_CHECK); else EnableMenuItem(pmenus->hmenu, pmenus->wId, wFlags); if (pmenus->hmenu == GetMenu(ahwnd[MAIN_HWND].hwndParent)) DrawMenuBar(ahwnd[MAIN_HWND].hwndParent); } /******************* - - Name: HbtnsCreate * * Purpose: To create a BUTTONSTATE structure for the icon window. * * Arguments: None. * * Returns: The local handle to the structure, if created, else NULL. * ******************/ HBTNS STDCALL HbtnsCreate(VOID) { HBTNS hbtns = LhAlloc(LMEM_FIXED, sizeof(BUTTONSTATE) + (BS_INITSIZE - 1) * sizeof(BTNPTR)); if (hbtns) { PBS pbs = (PBS) PtrFromGh(hbtns); pbs->pstb = StbCreate(); if (!pbs->pstb) { FreeLh(hbtns); return 0; } pbs->cbp = 0; pbs->cbpMax = BS_INITSIZE; } return hbtns; } /******************* * - Name: FDestroyBs * * Purpose: Destroys a BUTTONSTATE structure for the icon window. * * Arguments: hbtns A local handle to the structure. * * Returns: TRUE if successful, else FALSE. * ******************/ BOOL STDCALL FDestroyBs(HBTNS hbtns) { PBS pbs; pbs = (PBS) PtrFromGh(hbtns); FreeLh(pbs->pstb); FreeLh(hbtns); return TRUE; } /******************* * - Name: FDeleteButton * * Purpose: Deletes the specified button * * Arguments: hbtns A local handle to the structure. * hash unique identifier for button * * Returns: TRUE if successful, else FALSE. * ******************/ INLINE static BOOL STDCALL FDeleteButton(HWND hwnd, HASH hash) { PBS pbs; BOOL fRet = FALSE; int i; WRECT rc; HBTNS hbtns; if ((hbtns = (HBTNS) GetWindowLong(hwnd, GIWW_BUTTONSTATE)) == NULL) return FALSE; pbs = (PBS) PtrFromGh(hbtns); for (i = 0; i < pbs->cbp; i++) { if ((pbs->rgbp[i].wFlags != IBF_STD) && (pbs->rgbp[i].hash == hash)) { DestroyWindow(pbs->rgbp[i].hwnd); StbDeleteStr(pbs->pstb, pbs->rgbp[i].wMacro); StbDeleteStr(pbs->pstb, pbs->rgbp[i].wText); pbs->cbp--; MoveMemory(&pbs->rgbp[i], &pbs->rgbp[i+1], sizeof(pbs->rgbp[i]) * (pbs->cbp - i)); SetWindowLong(hwnd, GIWW_CBUTTONS, GetWindowLong(hwnd, GIWW_CBUTTONS) - 1); GetWindowWRect(hwnd, &rc); if (YArrangeButtons(hwnd, rc.cx, TRUE) != rc.cy) SendMessage(GetParent(hwnd), HWM_RESIZE, 0, 0L); fRet = TRUE; break; } } return fRet; } /******************* * - Name: FChangeButtonMacro * * Purpose: Changes the macro associated with a button * * Arguments: hbtns A local handle to the structure. * hash unique identifier for button * pszMacro pointer to new macro * * Returns: TRUE if successful, else FALSE. * ******************/ INLINE static BOOL STDCALL FChangeButtonMacro(HWND hwnd, HASH hash, PCSTR pszMacro) { PBS pbs; int i; if ((pbs = (PBS) GetWindowLong(hwnd, GIWW_BUTTONSTATE)) == NULL) return FALSE; for (i = 0; i < pbs->cbp; i++) { if (pbs->rgbp[i].hash == hash) { StbDeleteStr(pbs->pstb, pbs->rgbp[i].wMacro); pbs->rgbp[i].wMacro = StbAddStr(&pbs->pstb, pszMacro); break; } } return TRUE; } /******************* * - Name: FFindMacro * * Purpose: Finds the macro for the given button and copies the macro text * into the buffer. * * Arguments: hbtns The handle to the BUTTONSTATE structure. * pszText The button text of the button in question. * pszMacro A buffer to copy the macro text into. * cbMacro The length of the buffer. * * Returns: TRUE if successful, else FALSE. * ******************/ INLINE static PSTR STDCALL FFindMacro(HBTNS hbtns, PCSTR pszText) { int i; PSTR pszMacro = NULL; PBS pbs = (PBS) PtrFromGh(hbtns); for (i = 0; i < pbs->cbp; i++) { if ((WCmpButtonQch(PszFromPstb(pbs->pstb, pbs->rgbp[i].wText), pszText) == 0)) { pszMacro = LocalStrDup(PszFromPstb(pbs->pstb, pbs->rgbp[i].wMacro)); } if (pszMacro) break; } return pszMacro; } /******************* * - Name: BAddButton * * Purpose: Add new button information to the button structure for the icon * window. * * Arguments: hbtns: The button data structure, or null * lpszName: The button window text * lpszMacro: The macro for this button. * * Returns: TRUE, if successful, else FALSE. * ******************/ INLINE static BOOL STDCALL BAddButton(HWND hwnd, UINT wFlags, HASH hash, HBTNS hbtns, PSTR pszName, PCSTR pszMacro) { PBS pbs; PSTR pszTmp; if (!hbtns) return FALSE; pbs = (PBS) PtrFromGh(hbtns); // Make sure the button array has room for another button. if (pbs->cbp >= pbs->cbpMax) return FALSE; // Copy the information. pbs->rgbp[pbs->cbp].hwnd = hwnd; pbs->rgbp[pbs->cbp].wFlags = wFlags; pbs->rgbp[pbs->cbp].hash = hash; pszTmp = StrChrDBCS(pszName, ACCESS_KEY); if (pszTmp) { if (pszDbcsMenuAccelerator) { PSTR pszOriginal = lcStrDup(pszName); PSTR pszAccel = strstr(pszName, pszDbcsRomanAccelerator); if (pszAccel) { #ifdef _PRIVATE char szBuf[512]; wsprintf(szBuf, "Converting %s\r\n", pszName); SendStringToParent(szBuf); #endif pszAccel++; // skip over the '&' *pszAccel = ACCESS_KEY; strcpy(pszAccel + 1, pszAccel + 3); pbs->rgbp[pbs->cbp].vkKey = VkKeyScan(pszAccel[1]); } else pbs->rgbp[pbs->cbp].vkKey = VkKeyScan(pszTmp[1]); pszAccel = strstr(pszName, pszDbcsMenuAccelerator); if (pszAccel) { #if 0 pbs->rgbp[pbs->cbp].vkKeyAlt = VkKeyScan(pszAccel[strlen(pszDbcsMenuAccelerator)]); #endif strcpy(pszAccel, pszAccel + 4); } #if 0 else pbs->rgbp[pbs->cbp].vkKeyAlt = 0; #endif if (strcmp(pszOriginal, pszName) != 0) // did we change it? SetWindowText(hwnd, pszName); } else pbs->rgbp[pbs->cbp].vkKey = VkKeyScan(pszTmp[1]); } else pbs->rgbp[pbs->cbp].vkKey = 0; // Add the name, now that we've had a change to munge it pbs->rgbp[pbs->cbp].wText = StbAddStr(&pbs->pstb, pszName); pbs->rgbp[pbs->cbp].wMacro = StbAddStr(&pbs->pstb, pszMacro); pbs->cbp++; return TRUE; } /******************* - - Name: VExecuteButtonMcro * * Purpose: Decodes and executes the macro for this author-defined button. * * Arguments: hwndButton The Button that's been pushed. * * Returns: * ******************/ VOID STDCALL VExecuteButtonMacro(HBTNS hbtns, HWND hwndButton) { char rgchText[cchBTNTEXT_SIZE]; PSTR pszMacro; GetWindowText(hwndButton, rgchText, cchBTNTEXT_SIZE); if ((pszMacro = FFindMacro(hbtns, rgchText))) { Execute(pszMacro); FreeGh(pszMacro); } } /*************************************************************************** * - Name VDestroyAuthoredButtons - * Purpose Zaps the author-configured buttons and the internal data * structure used to maintain them. Recalculates the size * size of the largest button and sets this in the icon * window. * * Arguments hwnd The parent of the buttons, usually the icon window * * Returns Nothing. * * +++ * * Notes At the present time, there are 6 default buttons. * ***************************************************************************/ void STDCALL VDestroyAuthoredButtons(HWND hwnd) { HWND hwndButton; HWND hwndNext; HBTNS hbtns; PBS pbs; INT16 ibp; /*-----------------------------------------------------------------*\ * Destroy those authored windows. \*-----------------------------------------------------------------*/ hwndButton = GetWindow( hwnd, GW_CHILD ); while (hwndButton != NULL) { hwndNext = GetNextWindow(hwndButton, GW_HWNDNEXT); /*-----------------------------------------------------------------*\ * This is an authored window. \*-----------------------------------------------------------------*/ DestroyWindow(hwndButton); SetWindowLong(hwnd, GIWW_CBUTTONS, GetWindowLong(hwnd, GIWW_CBUTTONS) - 1); hwndButton = hwndNext; } /*-----------------------------------------------------------------*\ * Remove authored buttons from hbtns * Notes: This assumes that all authored buttons are added after * all standard buttons. * This does not relieve the string table, which will * apparently grow without bound now. \*-----------------------------------------------------------------*/ hbtns = (HBTNS) GetWindowLong(hwnd, GIWW_BUTTONSTATE); if (hbtns) { pbs = (PBS) PtrFromGh(hbtns); for (ibp = pbs->cbp - 1; ibp >= 0; ibp--) { StbDeleteStr(pbs->pstb, pbs->rgbp[ibp].wText); StbDeleteStr(pbs->pstb, pbs->rgbp[ibp].wMacro); } pbs->cbp = 0; } SetWindowLong(hwnd, GIWW_CXBUTTON, 0); SetWindowLong(hwnd, GIWW_CBUTTONS, 0); } /******************* ** ** Name: VModifyButtons ** ** Purpose: This function is called when the icon window wishes to add ** or delete a button. This function is also responsible for ** freeing the string memory used here. ** ** Arguments: wParam: Indicates the type of modification. ** lParam: A local handle to a the strings for this message, or NULL. ** ** Returns: nothing. ** *******************/ VOID STDCALL VModifyButtons(HWND hwnd, WPARAM wParam, LPARAM lParam) { PSTR nszText; PSTR pszMacro; HASH hash; switch (wParam) { case UB_CHGMACRO: if (lParam) { pszMacro = PtrFromGh((LH) lParam); hash = *((HASH *) pszMacro) ++; if (!FChangeButtonMacro(hwnd, hash, pszMacro) && fHelpAuthor) Error(wERRS_NOMODIFY, wERRA_RETURN); FreeLh((LH) lParam); } break; case UB_ADD: if (lParam) { nszText = PtrFromGh((LH) lParam); hash = *((HASH *) nszText) ++; pszMacro = nszText + strlen(nszText) + 1; // Make certain the Print button isn't duplicated if (_stricmp(pszMacro, "Print()") == 0) { FreeLh((LH) lParam); break; } else if (!HwndAddButton(hwnd, IBF_NONE, hash, nszText, pszMacro)) { // if (fHelpAuthor) // Error(wERRS_NOBUTTON, wERRA_RETURN); } FreeLh((LH) lParam); } else if (fHelpAuthor) Error(wERRS_NOBUTTON, wERRA_RETURN); break; case UB_DELETE: if (!FDeleteButton(hwnd, (HASH) lParam) && fHelpAuthor) Error(wERRS_NODELETE, wERRA_RETURN); break; case UB_REFRESH: VDestroyAuthoredButtons(hwnd); SendMessage(GetParent(hwnd), HWM_RESIZE, 0, 0L); break; case UB_DISABLE: FAbleButton((HASH) lParam, FALSE, GetWindowIndex(hwnd)); break; case UB_ENABLE: FAbleButton((HASH) lParam, TRUE, GetWindowIndex(hwnd)); break; } } /******************* - - Name: HwndAddButton * * Purpose: This function is called when the icon window wishes to add * or delete a button. This function is also responsible for * freeing the string memory used here. * * Arguments: hwnd - icon window * * * * Returns: Window handle to the newly added button if successful. * ******************/ HWND STDCALL HwndAddButton(HWND hwnd, UINT wFlags, HASH hash, PSTR pszBtnName, PCSTR pszMacro) { HWND hwndButton; WRECT rc; BOOL fError = FALSE; PSTR pszSaveBtnName; // Make sure hash is unique if (HwndFromHash(hash, hwnd) != NULL) return NULL; /* * Fix any problems with duplicate accelerators. The macro is typically * defined right after the button name. If there is no accelerator and * we add one, we'll mess up the macro name. So, we copy the name, * and only change the copy of the name rather then the original name. */ pszSaveBtnName = LhAlloc(LMEM_FIXED,lstrlen(pszBtnName) + 4); if (pszSaveBtnName) lstrcpy(pszSaveBtnName,pszBtnName); else OOM(); FixDupButtonAccelerator((HBTNS) GetWindowLong(hwnd, GIWW_BUTTONSTATE), pszSaveBtnName); if ((hwndButton = HwndCreateIconButton(hwnd, pszSaveBtnName))) { if (!BAddButton(hwndButton, wFlags, hash, (HBTNS) GetWindowLong(hwnd, GIWW_BUTTONSTATE), pszSaveBtnName, pszMacro)) { DestroyWindow(hwndButton); fError = TRUE; hwndButton = NULL; } else { SetWindowLong(hwnd, GIWW_CBUTTONS, GetWindowLong(hwnd, GIWW_CBUTTONS) + 1); GetWindowWRect(hwnd, &rc); if (YArrangeButtons(hwnd, rc.cx, TRUE) != rc.cy && !fButtonsBusy) SendMessage(GetParent(hwnd), HWM_RESIZE, 0, 0L); } } else fError = TRUE; if (fError && fHelpAuthor) Error(wERRS_NOBUTTON, wERRA_RETURN); FreeLh((HLOCAL) pszSaveBtnName); return hwndButton; } /*************************************************************************** * - Name: EnableButton - * Purpose: * Enables or disables a button, and invalidates it's rect appropriately. * * This routine exists because the mere Enabling or disabling of a button * normally causes it to be repainted immediately. We want to delay the * repaint until everything gets repainted all at once. Looks much better * that way. * * Arguments: * hwndButton - window handle for the button in question * fEnable - TRUE => enable it, else disable (May come in as * any non-zero value) * * Returns: * nothing * ***************************************************************************/ VOID STDCALL EnableButton(HWND hwndButton, BOOL fEnable) { BOOL fOld; // TRUE => window was enabled // Turn fEnable into a pure boolean we can compare with. fEnable = (fEnable != FALSE); if (IsValidWindow(hwndButton)) { /* * Turn off repainting in button, so that the EnableWindow call * will NOT draw the buttons immediately. We'll redraw them all * togther, later, by invalidating the rect when we're done. */ fOld = IsWindowEnabled(hwndButton); if (fOld != fEnable) { SendMessage(hwndButton, WM_SETREDRAW, FALSE, 0); EnableWindow(hwndButton, fEnable); /* * Turn on repainting in icon win, and invalidate it's * contents, to make sure that it will eventually get redrawn. */ SendMessage(hwndButton, WM_SETREDRAW, TRUE, 0); InvalidateRect(hwndButton, NULL, FALSE); } } } /*************************************************************************** * - Name HwndCreateIconButton - * Purpose Creates a button in the icon window. * * Arguments hwnd The parent (usually the icon window) * szText The button text * * Returns a window handle to the button * ***************************************************************************/ static HWND STDCALL HwndCreateIconButton(HWND hwnd, PSTR szText) { HWND hwndButton; int xNewButton; int cxNewButton; ASSERT(IsValidWindow(hwnd)); cxNewButton = CxFromSz(szText); if (cxNewButton > GetWindowLong(hwnd, GIWW_CXBUTTON)) SetWindowLong(hwnd, GIWW_CXBUTTON, cxNewButton); else cxNewButton = GetWindowLong(hwnd, GIWW_CXBUTTON); xNewButton = GetWindowLong(hwnd, GIWW_CBUTTONS) * (cxNewButton + ICON_SURROUND); hwndButton = CreateWindow((LPSTR) WC_BUTTON, szText, WS_CHILD | WS_VISIBLE, xNewButton + ICON_SURROUND, ICON_SURROUND, cxNewButton, GetWindowLong(hwnd, GIWW_CYBUTTON), hwnd, (HMENU) ICON_USER, hInsNow, NULL ); ASSERT(IsValidWindow(hwndButton)); if (hwndButton) { // Subclass button. if (lpfnlButtonWndProc == NULL) lpfnlButtonWndProc = (FARPROC) GetWindowLong(hwndButton, GWL_WNDPROC); SendMessage(hwndButton, WM_SETFONT, (WPARAM) HfontGetSmallSysFont(), 0); SetWindowLong(hwndButton, GWL_WNDPROC, (LONG) LSButtonWndProc); } return hwndButton; } /******************* - - Name: HwndFromHash * * Purpose: Finds the child window with the hash * * Arguments: hash hash value of id string. * hwnd The parent window. * * Returns: The window handle of the correct child window, or NULL. * ******************/ static HWND STDCALL HwndFromHash(HASH hash, HWND hwnd) { PBS pbs; int ibp; HWND hwndRet = NULL; pbs = (PBS) GetWindowLong(hwnd, GIWW_BUTTONSTATE); if (pbs) { for (ibp = 0; ibp < pbs->cbp; ibp++) { if (pbs->rgbp[ibp].hash == hash) { hwndRet = pbs->rgbp[ibp].hwnd; break; } } } return hwndRet; } /******************* - - Name: ProcessMnemonic * * Purpose: Finds the child window with the given mnemonic. * * Arguments: c The mnemonic. * hwnd The parent window. * * Returns: The window handle of the correct child window, or NULL. * * Notes: Even works for standard buttons. * ******************/ #define MAX_DUP_WINDOWS 10 #define FLAG_AUTHORABLE -1 BOOL STDCALL ProcessMnemonic(WORD ch, int iWindow, BOOL fReturnOnly) { PBS pbs; HBTNS hbtns; int ibp; DUP_HWND aDupHwnd[MAX_DUP_WINDOWS]; PSTR ppsz[MAX_DUP_WINDOWS]; int iDup = 0; int i; for (i = 0; i < MAX_BUTTONS; i++) { // check for match with key or SHIFT+key if (btndata[i].hwnd && btndata[i].iWindow == iWindow && ((UINT16) btndata[i].vKey == ch || (UINT16) (btndata[i].vKey & 0xFF) == (ch & 0xFF))) { if (fReturnOnly) return TRUE; aDupHwnd[iDup].hwnd = btndata[i].hwnd; aDupHwnd[iDup].fAuthorableButton = TRUE; ppsz[iDup++] = btndata[i].pszText; /* * To avoid get duplicate accelerators on Swiss-German * keyboards, we special case the '<' character (which * is on the same key as '>' in a swiss-german keyboard. */ if (ch == 0xe2 || ch == 0x1e2) // '<' and '>' on Swiss-german keyboards break; if (iDup >= MAX_DUP_WINDOWS) break; } } if (iWindow == -1) { pbs = NULL; goto PopupWindow; } hbtns = (HBTNS) GetWindowLong(ahwnd[iWindow].hwndButtonBar, GIWW_BUTTONSTATE); pbs = (hbtns ? (PBS) PtrFromGh(hbtns) : NULL); // If no button bar, then authorable buttons are the only option if (!ahwnd[iWindow].hwndButtonBar && pbs) { if (!iDup) return FALSE; else if (iDup > 1) ResolveDuplicate(aDupHwnd, ppsz, iDup, pbs, iWindow); else doBtnCmd(aDupHwnd[0].hwnd); return TRUE; } if (hbtns) { // REVIEW: why check for shift separately? for (ibp = 0; ibp < pbs->cbp; ibp++) { if (pbs->rgbp[ibp].vkKey == ch || (pbs->rgbp[ibp].vkKey & 0xFF) == (ch & 0xFF)) { if (fReturnOnly) { return TRUE; } aDupHwnd[iDup].hwnd = pbs->rgbp[ibp].hwnd; aDupHwnd[iDup].fAuthorableButton = FALSE; ppsz[iDup++] = PszFromPstb(pbs->pstb, pbs->rgbp[ibp].wText); /* * To avoid get duplicate accelerators on Swiss-German * keyboards, we special case the '<' character (which * is on the same key as '>' in a swiss-german keyboard. */ if (ch == 0xe2 || ch == 0x1e2) // '<' on Swiss-german keyboards break; } } if (!iDup) { return FALSE; } else if (iDup == 1) { if (IsWindowEnabled(aDupHwnd[0].hwnd)) { if (aDupHwnd[0].fAuthorableButton) doBtnCmd(aDupHwnd[0].hwnd); else PostMessage(ahwnd[iWindow].hwndButtonBar, IWM_COMMAND, (WPARAM) GetWindowLong(aDupHwnd[0].hwnd, GWL_ID), (LPARAM) aDupHwnd[0].hwnd); } } else if (iDup > 1) ResolveDuplicate(aDupHwnd, ppsz, iDup, pbs, iWindow); return TRUE; } // Disabled buttons or a popup, so stick with authorable buttons PopupWindow: if (!iDup) return FALSE; else if (iDup > 1 && pbs) ResolveDuplicate(aDupHwnd, ppsz, iDup, pbs, iWindow); else doBtnCmd(aDupHwnd[0].hwnd); return TRUE; } /*************************************************************************** FUNCTION: ResolveDuplicate PURPOSE: PARAMETERS: aDupHwnd array of duplicate window handles ppsz array of pointers to button text iDup number of duplicated buttons pbs button structure iWindow index of calling window RETURNS: COMMENTS: MODIFICATION DATES: 01-Jan-1994 [ralphw] ***************************************************************************/ static void STDCALL ResolveDuplicate(DUP_HWND* aDupHwnd, PSTR* ppsz, int iDup, PBS pbs, int iWindow) { int i; dupBtn.ppsz = ppsz; dupBtn.iDup = iDup; i = CallDialog(IDDLG_DUP_BUTTON, ahwnd[iCurWindow].hwndParent, DupBtnDlg); if (i >= 0) { if (aDupHwnd[i].fAuthorableButton) doBtnCmd(aDupHwnd[i].hwnd); else { if (IsWindowEnabled(aDupHwnd[i].hwnd)) { PostMessage(ahwnd[iWindow].hwndButtonBar, IWM_COMMAND, GetWindowLong(aDupHwnd[i].hwnd, GWL_ID), (LPARAM) aDupHwnd[i].hwnd); } } } } /*-----------------------------------------------------------------*\ * static helper functions \*-----------------------------------------------------------------*/ /*************************************************************************** * - Name CxFromSZ - * Purpose Calculates the size of the button needed to display the * given text. * * Arguments szButtonText * * Returns The width of the button. * * +++ * * Notes This function pads the button for the size of the default * button borders and 3-d beveling effects. It seems to make * the smalles button that still fits the Contents word. * ***************************************************************************/ INLINE static int STDCALL CxFromSz(PSTR szButtonText) { return LOWORD(LGetSmallTextExtent(szButtonText)) + GetSystemMetrics(SM_CXBORDER) * 8; } /******************* - - Name: WCmpButtonQch * * Purpose: Compares to button/menu strings to see if they are equal. * The comparison ignores the '&' character and also * is case insensitive. * * Arguments: qch1, qch2 - the strings to compare. * * Returns: 0 if the two strings are equal. * ******************/ INLINE static int STDCALL WCmpButtonQch(PCSTR qch1, PCSTR qch2) { char rgch1[cchBTNTEXT_SIZE]; char rgch2[cchBTNTEXT_SIZE]; PSTR psz; // REVIEW: do we really need to strip out the accelerator key? lstrcpy(rgch1, qch1); while ((psz = StrChrDBCS(rgch1, ACCESS_KEY))) lstrcpy(psz, psz + 1); lstrcpy(rgch2, qch2); while ((psz = StrChrDBCS(rgch2, ACCESS_KEY))) lstrcpy(psz, psz + 1); return WCmpiSz(rgch1, rgch2); } /******************* - - Name: FAbleButton * * Purpose: Enables or disables a button based on button id. * * Arguments: qch - Button name to disable/enable * fEnable - Flag for operation (TRUE == enable) * * Returns: TRUE if the button was found and the operation was successful. * ******************/ static BOOL STDCALL FAbleButton(HASH hash, BOOL fEnable, int iWindow) { HWND hwnd; if ((hwnd = HwndFromHash(hash, ahwnd[iWindow].hwndButtonBar)) != NULL) { EnableWindow(hwnd, fEnable); return TRUE; } if (fHelpAuthor) Error(wERRS_NOABLEBUTTON, wERRA_RETURN); return FALSE; } /*************************************************************************** FUNCTION: SearchCtxDlg PURPOSE: Get a context string or id and optional different filename, and execute it as a JI or JC macro. PARAMETERS: hwnd wMsg wParam lParam RETURNS: COMMENTS: Used by help authors to test their files. MODIFICATION DATES: 26-Sep-1993 [ralphw] ***************************************************************************/ static BOOL fCtxNumber; BOOL EXPORT SearchCtxDlg(HWND hwndDlg, UINT wMsg, WPARAM wParam, LPARAM lParam) { char szFile[MAX_PATH]; switch (wMsg) { case WM_INITDIALOG: ChangeDlgFont(hwndDlg); GetFmParts(QDE_FM(QdeFromGh(HdeGetEnv())), szFile, PARTBASE); SetWindowText(GetDlgItem(hwndDlg, ID_HELP_FILE), szFile); SetFocus(GetDlgItem(hwndDlg, ID_CTX)); if (fCtxNumber) CheckDlgButton(hwndDlg, IDC_RADIO_CTX_ID, TRUE); else CheckDlgButton(hwndDlg, IDC_RADIO_TOPIC, TRUE); return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: { char szMacro[cchBTNMCRO_SIZE]; int cb; GetDlgItemText(hwndDlg, ID_HELP_FILE, szFile, sizeof(szFile)); GetFmParts(QDE_FM(QdeFromGh(HdeGetEnv())), szMacro, PARTBASE); if (_strcmpi(szFile, szMacro) != 0) { strcpy(szMacro, "JI(\""); strcat(szMacro, szFile); } else strcpy(szMacro, "JI(\""); strcat(szMacro, "\", \""); cb = strlen(szMacro); GetDlgItemText(hwndDlg, ID_CTX, szMacro + cb, sizeof(szMacro) - cb); fCtxNumber = IsDlgButtonChecked(hwndDlg, IDC_RADIO_CTX_ID); if (!fCtxNumber && isdigit((BYTE) szMacro[cb])) { JumpToTopicNumber(atoi(szMacro + cb) - 1); goto EndDlg; } /* * If the string starts with a '!', then ignore everything * we've done so far, and just execute the macro. */ if (szMacro[cb] == '!') { GH gh = lcStrDup(szMacro + cb + 1); PostMessage(ahwnd[iCurWindow].hwndParent, MSG_NEW_MACRO, 0, (LPARAM) gh); EndDialog(hwndDlg, 0); break; } // If a number was specified, change to JC instead of JI if (szMacro[cb] >= '0' && szMacro[cb] <= '9') { strcpy(szMacro + (cb - 1), szMacro + cb); // eliminate the quote strcat(szMacro, ")"); szMacro[1] = 'C'; } else strcat(szMacro, "\")"); Execute(szMacro); EndDlg: EndDialog(hwndDlg, 0); } break; case IDCANCEL: EndDialog(hwndDlg, 0); break; default: return FALSE; } break; default: return(FALSE); } return TRUE; } /*************************************************************************** FUNCTION: FixDupButtonAccelerator PURPOSE: If another button already has this buttons accelerator, then move this accelerator. PARAMETERS: psz RETURNS: COMMENTS: MODIFICATION DATES: 01-Apr-1994 [ralphw] ***************************************************************************/ static void STDCALL FixDupButtonAccelerator(HBTNS hbtns, PSTR psz) { UINT16 ch; int i; PSTR pszOrg; PBS pbs = (PBS) PtrFromGh(hbtns); PSTR pszTmp = StrChrDBCS(psz, ACCESS_KEY); // If no accelerator, force one now ASSERT(hbtns); if (!pszTmp) { MoveMemory(psz + 1, psz, strlen(psz) + 1); *psz = ACCESS_KEY; pszTmp = psz; } pszOrg = pszTmp; ch = VkKeyScan(pszTmp[1]); for (i = 0; i < pbs->cbp; i++) { // check for a duplicate accelerator key if ((pbs->rgbp[i].vkKey & 0xFF) == (ch & 0xFF)) { strcpy(pszTmp, pszTmp + 1); // remove the accelerator pszTmp++; if (!*pszTmp) { // End of string, nothing we can do MoveMemory(pszOrg + 1, pszOrg, strlen(pszOrg) + 1); *pszOrg = ACCESS_KEY; return; } else { MoveMemory(pszTmp + 1, pszTmp, strlen(pszTmp) + 1); *pszTmp = ACCESS_KEY; ch = VkKeyScan(pszTmp[1]); i = -1; // start over } } } } BOOL STDCALL Compare(PCSTR pszFileName) { char szBuf[256]; HDE hde; QDE qde; if ((hde = HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic)) != NULL) qde = QdeFromGh(hde); else if ((hde = GetMacroHde())) qde = QdeFromGh(hde); else return FALSE; strcpy(szBuf, txtWinHlp32); strcat(szBuf, " -7 "); strcat(szBuf, pszFileName); fHelpAuthor = TRUE; if (WinExec(szBuf, SW_SHOW) > HINSTANCE_ERROR) { hwndSecondHelp = FindWindow("MS_WINDOC", NULL); if (hwndSecondHelp) { WRECT wrcFirst, wrcSecond; wrcFirst.left = rcWorkArea.left; wrcFirst.top = rcWorkArea.top; wrcFirst.cx = RECT_WIDTH(rcWorkArea) / 2 - 1; wrcFirst.cy = RECT_HEIGHT(rcWorkArea); MoveMemory(&wrcSecond, &wrcFirst, sizeof(WRECT)); wrcSecond.left = wrcFirst.cx + 2; MoveWindow(ahwnd[iCurWindow].hwndParent, wrcFirst.left, wrcFirst.top, wrcFirst.cx, wrcFirst.cy, TRUE); MoveWindow(hwndSecondHelp, wrcSecond.left, wrcSecond.top, wrcSecond.cx, wrcSecond.cy, TRUE); SendMessage(hwndSecondHelp, MSG_LINKED_HELP, 0, (LPARAM) ahwnd[iCurWindow].hwndParent); JumpToTopicNumber(0); // will send to both WinHelps } else return FALSE; } } ///////////////////////////// DEBUG CODE /////////////////////////////////// #ifdef _DEBUG /*************************************************************************** * - Name VDebugAddButton - * Purpose Executes the debugging command to add a button * * Arguments none * * Returns nothing * ***************************************************************************/ VOID STDCALL VDebugAddButton(VOID) { CallDialog(ADDBTNDLG, ahwnd[iCurWindow].hwndButtonBar, BAddBtnDlg); } /*************************************************************************** * - Name BAddBtnDlg - * Purpose The dialog box procedure for the debugging AddButton command. * * Arguments This is a Windows callback function. * * Returns Message dependent. * ***************************************************************************/ BOOL EXPORT BAddBtnDlg( HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam ) { char szId[cchBTNID_SIZE]; char szBtnName[cchBTNTEXT_SIZE]; char szMacro[cchBTNMCRO_SIZE]; switch (wMsg) { case WM_INITDIALOG: ChangeDlgFont(hwnd); SetFocus(GetDlgItem(hwnd, DLGBTNID)); return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case DLGOK: GetDlgItemText(hwnd, DLGBTNID, szId, cchBTNID_SIZE); GetDlgItemText(hwnd, DLGBTNNAME, szBtnName, cchBTNTEXT_SIZE); GetDlgItemText(hwnd, DLGBTNMACRO, szMacro, cchBTNMCRO_SIZE); VCreateAuthorButton(szId, szBtnName, szMacro); EndDialog(hwnd, wParam == DLGOK); break; case DLGCANCEL: EndDialog(hwnd, wParam == DLGOK); break; default: return FALSE; } break; default: return( FALSE ); } return TRUE; } #endif #if defined(_DEBUG) || defined(_PRIVATE) /*************************************************************************** FUNCTION: FormatNumber PURPOSE: Convert a number into a string, and insert commas every 3 digits PARAMETERS: num RETURNS: Pointer to the string containing the number COMMENTS: Cycles through an array of strings, allowing up to MAX_STRING requests before a duplicate would occur. This is important for calls to sprintf() where all the pointers are retrieved before the strings are actually used. MODIFICATION DATES: 03-Jul-1994 [ralphw] ***************************************************************************/ #define MAX_NUM 15 #define MAX_STRING 10 PCSTR STDCALL FormatNumber(int num) { static int pos = 0; static char szNum[MAX_NUM * MAX_STRING]; PSTR pszNum = szNum + (pos * MAX_NUM); int cb; if (++pos >= MAX_STRING) pos = 0; wsprintf(pszNum, txtFormatUnsigned, num); cb = strlen(pszNum) - 3; while (cb > 0) { MoveMemory(pszNum + cb + 1, pszNum + cb, strlen(pszNum + cb) + 1); pszNum[cb] = ','; cb -= 4; } return pszNum; } #endif // DEBUG || _PRIVATE