Menus and Accelerators

This example is composed of three files:

Source code(menusaccel.cpp)
//=============================================================================
//RESOURCES: MENUS AND ACCELERATORS - Copyright © 2000,2005 Ken Fitlike
//=============================================================================
//API functions used: CreateDialog,DestroyWindow,DispatchMessage,GetLastError,
//GetMessage,IsDialogMessage,IsWindow,LoadAccelerators,LoadImage,
//MAKEINTRESOURCE,MessageBox,PostQuitMessage,PostMessage,SendMessage,
//TranslateAccelerator,TranslateMessage,WinMain.
//=============================================================================
//This demonstrates the creation of menu and accelerator resources within a 
//modeless dialog box.
//=============================================================================
#include <windows.h>  //include all the basics
#include <tchar.h>    //string and other mapping macros
#include <string>
#include <vector>
#include "resources.h"

//define an unicode string type alias
typedef std::basic_string<TCHAR> ustring;
//=============================================================================
//message processing function declarations
INT_PTR CALLBACK DlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
void OnCommand(const HWND,int,int,const HWND);
INT_PTR OnInitDlg(const HWND,LPARAM);

//non-message function declarations
inline int ErrMsg(const ustring&);
//=============================================================================
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int)
{
HWND hDlg=CreateDialog(hInst,MAKEINTRESOURCE(IDD_DIALOG1),0,DlgProc);

if (!hDlg)
  {
  ErrMsg(_T("CreateDialog failed."));
  return 0;
  }
  
//Load keyboard accelerator so that it's available for use
HACCEL hAccelTable=LoadAccelerators(hInst,MAKEINTRESOURCE(IDR_ACCELERATOR1));
  
//start message loop - windows applications are 'event driven' waiting on user,
//application or system signals to determine what action, if any, to take. Note 
//that an error may cause GetMessage to return a negative value so, ideally,  
//this result should be tested for and appropriate action taken to deal with 
//it(the approach taken here is to simply quit the application).
MSG msg;
while (GetMessage(&msg,0,0,0)>0)
  {
  //first check if the message is from a resource defined keyboard accelerator key
  if (!TranslateAccelerator(hDlg,hAccelTable,&msg))
    {
    //get default keyboard message handling for dialog box eg control tabbing
    if (!IsWindow(hDlg) || !IsDialogMessage(hDlg,&msg))
      {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      }
    }
  }
return static_cast<int>(msg.wParam);
}
//=============================================================================
INT_PTR CALLBACK DlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
  {
  case WM_COMMAND:
    {
    OnCommand(hwnd,LOWORD(wParam),HIWORD(wParam),
              reinterpret_cast<HWND>(lParam));
    return 0;
    }
  case WM_DESTROY:
    PostQuitMessage(0);    //signal end of application
    return 0;
  case WM_INITDIALOG:
    {
    return OnInitDlg(hwnd,lParam);
    }
  default:
    return 0;  //let system deal with msg
  }
}
//=============================================================================
void OnCommand(const HWND hwnd,int id,int notifycode,const HWND hCntrl)
{
//handles WM_COMMAND message
if (!hCntrl)
  {
  ustring txt;
  switch (id)
    {
    case IDCANCEL:
      txt=_T("exit");
      break;
    case IDM_COPY:
      txt=_T("copy");
      break;
    case IDM_PASTE:
      txt=_T("paste");
      break;
    case IDM_CUT:
      txt=_T("cut");
      break;
    case IDM_ABOUT:
      txt=_T("about");
      break;
    case IDM_HELP:
      txt=_T("help");
      break;
    case IDM_OPEN:
      txt=_T("open");
      break;
    case IDM_SAVE:
      txt=_T("save");
      break;
    }
  if (notifycode==0)
    {
    ustring title=_T("Menu Selected");
    MessageBox(0,txt.c_str(),title.c_str(),MB_OK);
    }
  else if (notifycode==1)
    {
    ustring title=_T("Accelerator Selected");
    MessageBox(0,txt.c_str(),title.c_str(),MB_OK);
    }
  }
switch (id)
  {
  case IDOK:        //RETURN key pressed or 'ok' button selected
  case IDCANCEL:    //ESC key pressed or 'cancel' button selected
    DestroyWindow(hwnd);
  }
}
//=============================================================================
INT_PTR OnInitDlg(const HWND hwnd,LPARAM lParam)
{
//set the small icon for the dialog. IDI_APPLICATION icon is set by default 
//for winxp
SendMessage(hwnd,WM_SETICON,ICON_SMALL,
            reinterpret_cast<LPARAM>(LoadImage(0,IDI_APPLICATION,IMAGE_ICON,
                                               0,0,LR_SHARED)));
//ensure focus rectangle is properly draw around control with focus
PostMessage(hwnd,WM_KEYDOWN,VK_TAB,0);
return TRUE;
}
//=============================================================================
inline int ErrMsg(const ustring& s)
{
return MessageBox(0,s.c_str(),_T("ERROR"),MB_OK|MB_ICONEXCLAMATION);
}
//=============================================================================
Resource Definitions(resources.h)
//=============================================================================
//Resource script constants for Resources: Menus and Accelerators example.
//=============================================================================
#define IDD_DIALOG1                200

#define IDR_MENU1                  10000
#define IDM_OPEN                   10001
#define IDM_SAVE                   10002
#define IDM_COPY                   10003
#define IDM_CUT                    10004
#define IDM_PASTE                  10005
#define IDM_UNDO                   10006
#define IDM_HELP                   10007
#define IDM_ABOUT                  10008

#define IDR_ACCELERATOR1           20000
Resource Script(script.rc)
//=============================================================================
//RESOURCES: MENUS AND ACCELERATORS - Copyright (c) 2000,2006 Ken Fitlike
//resource script
//=============================================================================
#if !defined __BORLANDC__
#include <afxres.h>
#endif
#include "resources.h"
//=============================================================================
//Resource languages: Codes for languages and sub-languages are declared in 
//winnt.h eg. for US english replace SUBLANG_ENGLISH_UK with SUBLANG_ENGLISH_US
//eg. for FRENCH replace LANG_ENGLISH with LANG_FRENCH and then replace 
//SUBLANG_ENGLISH_UK with either SUBLANG_FRENCH,SUBLANG_FRENCH_BELGIAN,
//SUBLANG_FRENCH_CANADIAN,SUBLANG_FRENCH_SWISS,SUBLANG_FRENCH_LUXEMBOURG,
//SUBLANG_FRENCH_MONACO depending on which national or regional variation of
//the language corresponds best with your requirements.
//
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK  
//=============================================================================
//Dialog
//=============================================================================
IDD_DIALOG1 DIALOGEX 0, 0, 252, 149
STYLE DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | 
      WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Resources: Menus and Accelerators"  
MENU IDR_MENU1
FONT 8, "MS Sans Serif"
  {
  DEFPUSHBUTTON   "OK",IDOK,22,62,50,14
  PUSHBUTTON      "Cancel",IDCANCEL,111,63,50,14
  }
//=============================================================================
//Menu
//=============================================================================
IDR_MENU1 MENU
{
  POPUP "&File"
    {  
    MENUITEM "&Open\tCtrl + O",    IDM_OPEN
    MENUITEM "&Save\tCtrl + S",    IDM_SAVE
    MENUITEM SEPARATOR
    MENUITEM "E&xit",    IDCANCEL
    }
  POPUP "&Edit"
    {
    MENUITEM "Copy\tCtrl + C",     IDM_COPY
    MENUITEM "Cut\tCtrl + X",      IDM_CUT
    MENUITEM "Paste\tCtrl + V",    IDM_PASTE
    MENUITEM "Undo\tCtrl + Z",     IDM_UNDO, GRAYED
    }
  POPUP "Help"
    {
    MENUITEM "Help\tF1",     IDM_HELP
    MENUITEM "About...\tShift + Alt + A", IDM_ABOUT
    }
}
//=============================================================================
//Accelerators
//
//mingw does not recognise the 'control' statement with lowercase alphanumeric 
//keys but requires the caret (^) instead. If 'ASCII' is used instead of 
//VIRTKEY, msvc treats it as if VIRTKEY has been used. If uppercase letter is 
//used then mingw behaves properly with respect to the 'CONTROL' statement
//=============================================================================
IDR_ACCELERATOR1 ACCELERATORS
{
  "C", IDM_COPY,  VIRTKEY, CONTROL, NOINVERT
  "V", IDM_PASTE, VIRTKEY, CONTROL, NOINVERT
  "X", IDM_CUT,   VIRTKEY, CONTROL, NOINVERT
  "Z", IDM_UNDO,  VIRTKEY, CONTROL, NOINVERT
  
  "O", IDM_OPEN,  VIRTKEY, CONTROL, NOINVERT
  "S", IDM_SAVE,  VIRTKEY, CONTROL, NOINVERT
  
  VK_F1, IDM_HELP,VIRTKEY, NOINVERT
  "A", IDM_ABOUT, VIRTKEY, ALT, SHIFT, NOINVERT
}
//=============================================================================