mirror of https://github.com/lianthony/NT4.0
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
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
|