/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/
/****************************** Module Header *******************************
* Module Name: menu.c
*
* Contains the main menu switching functions and the clipboard functions.
*
* Functions:
* DialogMenu()
* LoadMenuBitmaps()
* FreeMenuBitmaps()
* InitMenu()
* MsgFilterHookFunc()
* ShowHelp()
* CopyToClipboard()
* DlgInClipboard()
* PasteFromClipboard()
* GetHelpContext()
*
* Comments:
*
****************************************************************************/
#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"
#include "dialogs.h"
#include <string.h>
#define MM10PERINCH 254 // Tenths of a millimeter per inch.
typedef struct {
WORD idm;
INT idbm;
HBITMAP hbm;
} BITMAPMENU;
STATICFN VOID CopyToClipboard(VOID);
STATICFN BOOL DlgInClipboard(VOID);
STATICFN VOID PasteFromClipboard(VOID);
STATICFN INT GetHelpContext(INT idSubject, PHELPMAP phmap);
static BITMAPMENU bmpmenuTable[] = {
{MENU_ALIGNLEFT, IDBM_ALEFT, NULL },
{MENU_ALIGNVERT, IDBM_AVERT, NULL },
{MENU_ALIGNRIGHT, IDBM_ARIGHT, NULL },
{MENU_ALIGNTOP, IDBM_ATOP, NULL },
{MENU_ALIGNHORZ, IDBM_AHORZ, NULL },
{MENU_ALIGNBOTTOM, IDBM_ABOTTOM, NULL },
{MENU_SPACEVERT, IDBM_ASPCVERT, NULL },
{MENU_SPACEHORZ, IDBM_ASPCHORZ, NULL },
{MENU_ARRSIZEWIDTH, IDBM_ASZWIDTH, NULL },
{MENU_ARRSIZEHEIGHT, IDBM_ASZHGHT, NULL },
{MENU_ARRPUSHBOTTOM, IDBM_APBBOTTM, NULL },
{MENU_ARRPUSHRIGHT, IDBM_APBRIGHT, NULL }
};
/************************************************************************
* DialogMenu
*
* This is the main switching function to send menu commands to the
* appropriate function.
*
* Arguments:
* INT - the menu command
*
************************************************************************/
VOID DialogMenu(
INT cmd)
{
/*
* Be sure any outstanding changes get applied without errors.
*/
if (!StatusApplyChanges())
return;
switch (cmd) {
/*
* File menu ----------------------------------------------------
*/
case MENU_NEWRES:
if (DoWeSave(FILE_INCLUDE) == IDCANCEL ||
DoWeSave(FILE_RESOURCE) == IDCANCEL)
break;
FreeInclude();
FreeRes();
AddNewDialog();
ShowFileStatus(TRUE);
break;
case MENU_OPEN:
if (DoWeSave(FILE_INCLUDE) == IDCANCEL ||
DoWeSave(FILE_RESOURCE) == IDCANCEL)
break;
Open(FILE_RESOURCE);
break;
case MENU_SAVE:
if (gfIncChged) {
if (!Save(FILE_NOSHOW | FILE_INCLUDE))
break;
}
if (gfResChged)
Save(FILE_NOSHOW | FILE_RESOURCE);
break;
case MENU_SAVEAS:
/*
* Save the include file, but only if there is one.
*/
if (pszIncludeFile || plInclude) {
if (!Save(FILE_NOSHOW | FILE_INCLUDE | FILE_SAVEAS))
break;
}
/*
* Save the resource file.
*/
Save(FILE_NOSHOW | FILE_RESOURCE | FILE_SAVEAS);
break;
case MENU_NEWCUST:
DlgBox(DID_NEWCUST, (WNDPROC)NewCustDlgProc);
break;
case MENU_OPENCUST:
OpenCustomDialog();
break;
case MENU_REMCUST:
DlgBox(DID_REMCUST, (WNDPROC)RemCustDlgProc);
break;
case MENU_SETINCLUDE:
if (DoWeSave(FILE_INCLUDE) != IDCANCEL)
Open(FILE_INCLUDE);
break;
case MENU_EXIT:
PostMessage(ghwndMain, WM_CLOSE, 0, 0L);
break;
/*
* Edit menu ----------------------------------------------------
*/
case MENU_RESTOREDIALOG:
RestoreDialog();
break;
case MENU_CUT:
case MENU_COPY:
if (gfEditingDlg) {
/*
* Save current stuff in clipboard.
*/
CopyToClipboard();
/*
* Clear the selection if they did a "cut" instead of
* a "copy".
*/
if (cmd == MENU_CUT)
DeleteControl();
}
break;
case MENU_PASTE:
PasteFromClipboard();
break;
case MENU_DELETE:
DeleteControl();
break;
case MENU_DUPLICATE:
Duplicate();
break;
case MENU_SYMBOLS:
ViewInclude();
break;
case MENU_STYLES:
StylesDialog();
break;
case MENU_SIZETOTEXT:
SizeToText();
break;
case MENU_NEWDIALOG:
AddNewDialog();
break;
case MENU_SELECTDIALOG:
SelectDialogDialog();
break;
/*
* Arrange menu -------------------------------------------------
*/
case MENU_ALIGNLEFT:
case MENU_ALIGNVERT:
case MENU_ALIGNRIGHT:
case MENU_ALIGNTOP:
case MENU_ALIGNHORZ:
case MENU_ALIGNBOTTOM:
AlignControls(cmd);
break;
case MENU_SPACEVERT:
case MENU_SPACEHORZ:
ArrangeSpacing(cmd);
break;
case MENU_ARRSIZEWIDTH:
case MENU_ARRSIZEHEIGHT:
ArrangeSize(cmd);
break;
case MENU_ARRPUSHBOTTOM:
case MENU_ARRPUSHRIGHT:
ArrangePushButtons(cmd);
break;
case MENU_ORDERGROUP:
OrderGroupDialog();
break;
case MENU_ARRSETTINGS:
ArrangeSettingsDialog();
break;
/*
* Options menu -------------------------------------------------
*/
case MENU_TESTMODE:
if (gfTestMode)
DestroyTestDialog();
else
CreateTestDialog();
break;
case MENU_HEXMODE:
/*
* Flip the flag, and update the status display so that
* the id value will be displayed in the new format.
*/
gfHexMode = gfHexMode ? FALSE : TRUE;
StatusUpdate();
break;
case MENU_TRANSLATE:
/*
* Flip the flag, and set the enable state of the fields
* in the status window. Changing the translate mode can
* effect whether they are allowed to change ids of controls.
*/
gfTranslateMode = gfTranslateMode ? FALSE : TRUE;
/*
* If they turned on Translate mode, reset the tool and
* hide the Toolbox. Otherwise, show the toolbox if they
* want it shown.
*/
if (gfTranslateMode) {
ToolboxShow(FALSE);
ToolboxSelectTool(W_NOTHING, FALSE);
}
else if (gfShowToolbox) {
ToolboxShow(TRUE);
}
StatusSetEnable();
break;
case MENU_USENEWKEYWORDS:
/*
* Flip the flag.
*/
gfUseNewKeywords = gfUseNewKeywords ? FALSE : TRUE;
break;
case MENU_SHOWTOOLBOX:
/*
* Toggle the state of the Toolbox.
*/
gfShowToolbox = gfShowToolbox ? FALSE : TRUE;
ToolboxShow(gfShowToolbox);
break;
/*
* Help menu ----------------------------------------------------
*/
case MENU_CONTENTS:
WinHelp(ghwndMain, gszHelpFile, HELP_CONTENTS, 0L);
break;
case MENU_SEARCH:
/*
* Tell winhelp to be sure this app's help file is current,
* then invoke a search with an empty starting key.
*/
WinHelp(ghwndMain, gszHelpFile, HELP_FORCEFILE, 0);
WinHelp(ghwndMain, gszHelpFile, HELP_PARTIALKEY, (DWORD)szEmpty);
break;
case MENU_ABOUT:
DlgBox(DID_ABOUT, (WNDPROC)AboutDlgProc);
break;
/*
* Hidden menu commands (accessed by accelerators) --------------
*/
case MENU_HIDDEN_TOTOOLBOX:
if (ghwndToolbox && IsWindowVisible(ghwndToolbox))
SetFocus(ghwndToolbox);
break;
case MENU_HIDDEN_TOPROPBAR:
SetFocus(hwndStatus);
break;
}
}
/************************************************************************
* LoadMenuBitmaps
*
* This function loads and inserts the menu items that are bitmaps.
* This has to be done at runtime because windows does not have a
* way to specify bitmap menu items in the rc file.
*
* Arguments:
* HMENU hMenu - The menu handle.
*
************************************************************************/
VOID LoadMenuBitmaps(
HMENU hMenu)
{
INT i;
for (i = 0; i < sizeof(bmpmenuTable) / sizeof(BITMAPMENU); i++) {
bmpmenuTable[i].hbm =
LoadBitmap(ghInst, MAKEINTRESOURCE(bmpmenuTable[i].idbm));
ModifyMenu(hMenu, bmpmenuTable[i].idm,
MF_BYCOMMAND | MF_BITMAP, bmpmenuTable[i].idm,
(LPTSTR)(DWORD)bmpmenuTable[i].hbm);
}
}
/************************************************************************
* FreeMenuBitmaps
*
* This function frees the menu bitmaps that were loaded by
* LoadMenuBitmaps. This function should be called only when
* the application is exiting, because it frees the bitmaps
* without removing them from the menu first.
*
************************************************************************/
VOID FreeMenuBitmaps(VOID)
{
INT i;
for (i = 0; i < sizeof(bmpmenuTable) / sizeof(BITMAPMENU); i++)
if (bmpmenuTable[i].hbm)
DeleteObject(bmpmenuTable[i].hbm);
}
/************************************************************************
* InitMenu
*
* This function grays/enables and checks/unchecks the menu items
* appropriately for the given state.
*
* Arguments:
* HMENU hMenu - The menu handle.
*
************************************************************************/
VOID InitMenu(
HMENU hMenu)
{
register INT i;
BOOL fEnable;
NPCTYPE npc;
HMENU hMenuArrange;
MyEnableMenuItem(hMenu, MENU_NEWRES, !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_SAVE,
(gfEditingDlg || gprlHead) && (gfResChged || gfIncChged));
MyEnableMenuItem(hMenu, MENU_SAVEAS, gfEditingDlg || gprlHead);
MyEnableMenuItem(hMenu, MENU_SETINCLUDE,
(gfEditingDlg || gprlHead) && !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_REMCUST, gpclHead);
MyEnableMenuItem(hMenu, MENU_RESTOREDIALOG, gfDlgChanged && gcd.prl);
MyEnableMenuItem(hMenu, MENU_CUT,
gnpcSel && gfEditingDlg && !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_COPY, gnpcSel && gfEditingDlg);
MyEnableMenuItem(hMenu, MENU_PASTE, !gfTranslateMode &&
IsClipboardFormatAvailable(fmtDlg) &&
(gfEditingDlg || DlgInClipboard()));
MyEnableMenuItem(hMenu, MENU_DELETE, gnpcSel && !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_DUPLICATE, gnpcSel && !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_SYMBOLS,
(gfEditingDlg || gprlHead) && !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_STYLES, gnpcSel && !gfTranslateMode);
/*
* For the "Size to text" menu command to be enabled, there
* must be at least one control selected, and one of the
* controls selected has to be able to be sized to its text.
*/
fEnable = FALSE;
if (gcSelected) {
for (npc = npcHead; npc; npc = npc->npcNext) {
if (npc->fSelected && npc->pwcd->fSizeToText) {
fEnable = TRUE;
break;
}
}
}
MyEnableMenuItem(hMenu, MENU_SIZETOTEXT, fEnable);
MyEnableMenuItem(hMenu, MENU_NEWDIALOG, !gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_SELECTDIALOG, gfEditingDlg || gprlHead);
hMenuArrange = GetSubMenu(hMenu, MENUPOS_ARRANGE);
MyEnableMenuItemByPos(hMenuArrange, MENUPOS_ARRANGEALIGN, gcSelected > 1);
MyEnableMenuItemByPos(hMenuArrange, MENUPOS_ARRANGESPACE, gcSelected > 1);
/*
* For the "Same size" menu option to be enabled, there
* must be more than one control selected, and they
* must be sizeable controls.
*/
fEnable = FALSE;
if (gcSelected > 1) {
for (i = 0, npc = npcHead; npc; npc = npc->npcNext) {
if (npc->fSelected && npc->pwcd->fSizeable) {
i++;
if (i > 1) {
fEnable = TRUE;
break;
}
}
}
}
MyEnableMenuItemByPos(hMenuArrange, MENUPOS_ARRANGESIZE, fEnable);
/*
* For the Arrange/Push buttons menu item to be enabled,
* there must be a dialog up and it must have at least one
* push button. In addition, if there are control(s) other
* than the dialog selected, at least one of the selected
* controls must be a push button.
*/
fEnable = FALSE;
if (gfEditingDlg || gprlHead) {
if (!gcSelected || gfDlgSelected) {
for (npc = npcHead; npc; npc = npc->npcNext) {
if (npc->pwcd->iType == W_PUSHBUTTON) {
fEnable = TRUE;
break;
}
}
}
else {
for (npc = npcHead; npc; npc = npc->npcNext) {
if (npc->pwcd->iType == W_PUSHBUTTON && npc->fSelected) {
fEnable = TRUE;
break;
}
}
}
}
MyEnableMenuItemByPos(hMenuArrange, MENUPOS_ARRANGEPUSH, fEnable);
MyEnableMenuItem(hMenu, MENU_ORDERGROUP,
npcHead && !gfTranslateMode && !gfTestMode && cWindows > 1);
MyEnableMenuItem(hMenu, MENU_TESTMODE, gfEditingDlg);
MyCheckMenuItem(hMenu, MENU_TESTMODE, gfTestMode);
MyEnableMenuItem(hMenu, MENU_HEXMODE, !gfTestMode);
MyCheckMenuItem(hMenu, MENU_HEXMODE, gfHexMode);
MyEnableMenuItem(hMenu, MENU_TRANSLATE, !gfTestMode);
MyCheckMenuItem(hMenu, MENU_TRANSLATE, gfTranslateMode);
MyEnableMenuItem(hMenu, MENU_USENEWKEYWORDS, !gfTestMode);
MyCheckMenuItem(hMenu, MENU_USENEWKEYWORDS, gfUseNewKeywords);
MyEnableMenuItem(hMenu, MENU_SHOWTOOLBOX, !gfTestMode && !gfTranslateMode);
MyCheckMenuItem(hMenu, MENU_SHOWTOOLBOX, gfShowToolbox);
}
/************************************************************************
* CopyToClipboard
*
* Puts the current dialog and a bitmap image of it into the clipboard.
* Gives a dialog box resource to the Clipboard.
* Gives a bit map to the clipboard.
* Clears everything else out of clipboard.
*
************************************************************************/
STATICFN VOID CopyToClipboard(VOID)
{
INT cbRes;
HANDLE hResClip;
PRES lpRes;
PRES pRes;
HDC hdcSrc;
HDC hdcDst;
RECT rc;
HBITMAP hbm;
/*
* Store the current selection in a dialog resource.
*/
if (!(pRes = AllocDialogResource(FALSE, TRUE)))
return;
/*
* Allocate global memory for it.
*/
cbRes = ResourceSize(pRes);
if (!cbRes || !(hResClip = GlobalAlloc(GHND | GMEM_DDESHARE, cbRes))) {
MyFree(pRes);
Message(MSG_OUTOFMEMORY);
return;
}
/*
* Copy it to the global memory.
*/
lpRes = (PRES)GlobalLock(hResClip);
memcpy(lpRes, pRes, cbRes);
GlobalUnlock(hResClip);
MyFree(pRes);
/*
* Now place it in the clipboard.
*/
if (OpenClipboard(ghwndMain)) {
EmptyClipboard();
SetClipboardData(fmtDlg, hResClip);
/*
* If the dialog is selected, place a bitmap image of it
* in the clipboard also. The drag handles will be removed
* first and the dialog will be activated so that the
* image looks proper.
*/
if (gfDlgSelected) {
CancelSelection(FALSE);
SetActiveWindow(gcd.npc->hwnd);
UpdateWindow(gcd.npc->hwnd);
if (hdcSrc = GetDC(NULL)) {
GetWindowRect(gcd.npc->hwnd, &rc);
if (hbm = CreateCompatibleBitmap(hdcSrc,
rc.right - rc.left, rc.bottom - rc.top)) {
if (hdcDst = CreateCompatibleDC(hdcSrc)) {
/*
* Calculate the dimensions of the bitmap and
* convert them to tenths of a millimeter for
* setting the size with the SetBitmapDimensionEx
* call. This allows programs like WinWord to
* retrieve the bitmap and know what size to
* display it as.
*/
SetBitmapDimensionEx(hbm,
((rc.right - rc.left) * MM10PERINCH) /
GetDeviceCaps(hdcSrc, LOGPIXELSX),
((rc.bottom - rc.top) * MM10PERINCH) /
GetDeviceCaps(hdcSrc, LOGPIXELSY),
NULL);
SelectObject(hdcDst, hbm);
BitBlt(hdcDst, 0, 0,
rc.right - rc.left, rc.bottom - rc.top,
hdcSrc, rc.left, rc.top, SRCCOPY);
DeleteDC(hdcDst);
SetClipboardData(CF_BITMAP, hbm);
}
else {
DeleteObject(hbm);
}
}
ReleaseDC(NULL, hdcSrc);
}
SetActiveWindow(ghwndMain);
SelectControl(gcd.npc, FALSE);
}
CloseClipboard();
}
else {
Message(MSG_NOCLIP);
}
}
/************************************************************************
* DlgInClipboard
*
* This function returns TRUE if there is data in the clipboard in the
* dialog format, and this data is for a complete dialog, not just for
* a group of controls.
*
************************************************************************/
STATICFN BOOL DlgInClipboard(VOID)
{
HANDLE hClip;
PRES pRes;
PDIALOGBOXHEADER pdbh;
BOOL fDlgResFound = FALSE;
if (!OpenClipboard(ghwndMain))
return FALSE;
if (hClip = GetClipboardData(fmtDlg)) {
pRes = (PRES)GlobalLock(hClip);
/*
* If cx is CONTROLS_ONLY, then we know that we only
* want to copy the controls in the template, not the
* entire dialog plus controls.
*/
pdbh = (PDIALOGBOXHEADER)SkipResHeader(pRes);
if (pdbh->cx != CONTROLS_ONLY)
fDlgResFound = TRUE;
GlobalUnlock(hClip);
}
CloseClipboard();
return fDlgResFound;
}
/************************************************************************
* PasteFromClipboard
*
* This routine pastes any data from the clipboard into the current
* resource file. If the data represents a complete dialog, a new dialog
* is added. If it represents some controls, the operation to drop them
* into the current dialog is begun.
*
************************************************************************/
STATICFN VOID PasteFromClipboard(VOID)
{
HANDLE hClip;
PRES pResClip;
PRES pResCopy;
INT cbRes;
if (!OpenClipboard(ghwndMain)) {
Message(MSG_NOCLIP);
return;
}
if (hClip = GetClipboardData(fmtDlg)) {
pResClip = (PRES)GlobalLock(hClip);
cbRes = ResourceSize(pResClip);
/*
* Make a copy of the clipboard data. This needs to be done
* because we may need to drag the new controls for a while,
* and it is rude to leave the clipboard open that long.
*/
if (pResCopy = (PRES)MyAlloc(cbRes)) {
memcpy((PBYTE)pResCopy, (PBYTE)pResClip, cbRes);
/*
* Now duplicate the dialog or controls in the clipboard.
* The pResCopy buffer does NOT need to be freed here, because
* it will be freed after the drag operation is complete.
*/
MakeCopyFromRes(pResCopy);
}
GlobalUnlock(hClip);
}
CloseClipboard();
}
/************************************************************************
* MsgFilterHookFunc
*
* This is the exported message filter function that is hooked into
* the message stream for detecting the pressing of the F1 key, at
* which time it calls up the appropriate help.
*
************************************************************************/
BOOL APIENTRY MsgFilterHookFunc(
INT nCode,
WPARAM wParam,
LPMSG lpMsg)
{
if ((nCode == MSGF_MENU || nCode == MSGF_DIALOGBOX) &&
(lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_F1)) {
/*
* Display help.
*/
ShowHelp((nCode == MSGF_MENU) ? TRUE : FALSE);
/*
* Tell Windows to swallow this message.
*/
return 1;
}
return CallNextHookEx(ghhkMsgFilter, nCode, wParam, (LONG)lpMsg);
}
/************************************************************************
* ShowHelp
*
* This function is called when the user has requested help. It will
* look at the menu state (if fMenuHelp is TRUE) or which dialog
* is currently up to determine the help topic, then it calls WinHelp.
*
* Arguments:
* BOOL fMenuHelp - TRUE if this help is for a menu (help was requested
* in the menu modal loop). If FALSE, general help
* or help for a dialog is assumed.
*
************************************************************************/
VOID ShowHelp(
BOOL fMenuHelp)
{
INT nHelpContext = 0;
HWND hwndFocus;
if (fMenuHelp) {
nHelpContext = GetHelpContext(gMenuSelected, gahmapMenu);
}
else {
/*
* Look for help for the current dialog.
*/
if (gidCurrentDlg) {
nHelpContext = GetHelpContext(gidCurrentDlg, gahmapDialog);
}
else {
/*
* There is no current dialog. Is the window with the
* focus a control on the Properties Bar?
*/
if ((hwndFocus = GetFocus()) && IsChild(hwndStatus, hwndFocus))
nHelpContext = GetHelpContext(DID_STATUS, gahmapDialog);
}
}
/*
* If there is help context, display it. Otherwise display
* the Contents screen.
*/
if (nHelpContext)
WinHelp(ghwndMain, gszHelpFile, HELP_CONTEXT, nHelpContext);
else
WinHelp(ghwndMain, gszHelpFile, HELP_CONTENTS, 0L);
}
/************************************************************************
* GetHelpContext
*
* This function takes a subject and returns its matching help
* context id from the given HELPMAP table.
*
* Arguments:
* INT idSubject - ID of the subject to find the help context for.
* PHELPMAP phmap - The help map table. It is assumed that the
* last entry in the table has a NULL subject id.
*
************************************************************************/
STATICFN INT GetHelpContext(
INT idSubject,
PHELPMAP phmap)
{
while (phmap->idSubject) {
if (phmap->idSubject == idSubject)
return phmap->HelpContext;
phmap++;
}
return 0;
}