Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2401 lines
57 KiB

/****************************************************************************
*
* 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