wxWidgets (3) - Creating A Menu Bar (Visual C++)

wxWidgets (3) - Creating A Menu Bar (Visual C++)
This tutorial continues on from the previous wxWidgets - (2) Creating A Status Bar (Visual C++).
  1. Open the previously created wxWidgets toolbar creation project.

  2. Specify "#include <wx/menu.h>" and #include <wx/menuitem.h> in its main header file.

  3. Prepare xpm bitmaps data with Bin2CU for the menu item glyphs.
    The main menu structure is as follows:
    File | Edit | Options | Help
    File menu item contains :
    New, -(separator), Open, Save, -(separator), Exit

    Edit menu items :
    Undo, Redo, -(separator), Clear Undo Buffer

    Options menu items :
    Preferences..., -(separator), Stay On Top, -(separator), User Interface Language

    User Interface Language's sub-items (Radio items) are as follows :
    English, Japanese

    Help menu items :
    Help Topics..., -(separator), About...


    Some items would display glyphs before their captions and some others won't. Let's prepare xpm glyphs data for the glyph-displaying items:

    •   New
    •   Open
    •   Save
    •   Exit

    •   Undo
    •   Redo
    •   Clear Undo Buffer
    •   Preferences...
    •   Stay On Top (Checkable item: 2 glyphs() for checked state connotation)
    •   Help Topics...
    •   About...

  4. After copying xpm text data converted from png images, write down the following at the bottom of "src_glyphs_png.cpp":
    
    static const size_t sn_edit_clear_png = sizeof edit_clear_png;
    static const size_t sn_edit_redo_png = sizeof edit_redo_png;
    static const size_t sn_edit_uno_png = sizeof edit_uno_png;
    static const size_t sn_file_new_png = sizeof file_new_png;
    static const size_t sn_file_exit_png = sizeof file_exit_png;
    static const size_t sn_file_open_png = sizeof file_open_png;
    static const size_t sn_file_save_png = sizeof file_save_png;
    static const size_t sn_help_about_png = sizeof help_about_png;
    static const size_t sn_help_topics_png = sizeof help_topics_png;
    static const size_t sn_opt_ontop_dn_png = sizeof opt_ontop_dn_png;
    static const size_t sn_opt_ontop_up_png = sizeof opt_ontop_up_png;
    static const size_t sn_opt_preferences_png = sizeof opt_preferences_png;
    
    
    

  5. Declare menu item ID constants in its header file just before MainFrame class declaration :
    
    //----------------------------------------------------------------------------
    enum enMenuTop {mtFile, mtEdit, mtOpt, mtHelp, cimenu_tops};
    enum enMenuStarts { IDM_FILE_START = 0x0800, IDM_EDIT_START = IDM_FILE_START + 0x0800
        , IDM_OPTS_START = IDM_EDIT_START + 0x0800, IDM_HELP_START = IDM_OPTS_START + 0x0800
        , IDM_SUB_LANG_START = IDM_HELP_START + 0x0800
    };
    enum enMenuFile
    {
        mfOpen = IDM_FILE_START, mfSave, mfSepExit, mfExit
        , cifile_items = mfExit - IDM_FILE_START + 0x01
    };
    enum enMenuEdit
    {
        meUndo = IDM_EDIT_START, meRedo, meSepClearBuffer, meClearBuffer
        , ciedit_items = meClearBuffer - IDM_EDIT_START + 0x01
    };
    enum enMenuOpts
    {
        moPrefs = IDM_OPTS_START, moSepOnTop, moOnTop, moSepUILang, moUILang
        , ciopt_items = moUILang - IDM_OPTS_START + 0x01
    };
    enum enMenuHelps
    {
        mhTopics = IDM_HELP_START, mhSepAbout, mhAbout
        , cihelp_items = mhAbout - IDM_HELP_START + 0x01
    };
    //------------------------ language sub-menu IDs ----------------------------:
    enum enMenuLangSubs
    {
        msLangEng = IDM_SUB_LANG_START, msLangJpn
            , cisub_langs = msLangJpn - IDM_SUB_LANG_START + 0x01
    };
    //------------------------ language sub-menu IDs ----------------------------:
    enum enMenuLangSubs
    {
        msLangEng = IDM_SUB_LANG, msLangJpn, cisub_lang = msLangJpn - IDM_SUB_LANG + 0x01
    };
    //----------------------------------------------------------------------------
    
    
    

  6. Declare wxMenu variables and a menu bar variable in the private section of its header file :
    
        wxMenu *m_menus[cimenu_tops];
        wxMenuBar m_menubar;
    

  7. Declare menu items captions in the MainFrame's private section :
    ...
    ...
    
    //----------------------------------------------------------------------------
    class MainFrame : public wxFrame
    {
        wxDECLARE_CLASS(MainFrame);
        wxDECLARE_EVENT_TABLE();
    private:
        wxString m_SMenuTops[cimenu_tops] // add anew
            , m_SMenuFiles[cifile_items]  // add anew
            , m_SMenuEdits[ciedit_items]  // add anew
            , m_SMenuOpts[ciopt_items]    // add anew
            , m_SMenuHelps[cihelp_items]  // add anew
            , m_SSubUILangs[cisub_langs]  // add anew
        ;                                 // add anew
    
    
    ...
    ...
    ...
    

  8. Write down the following simple method for identification of separator and menu item kind in the header file :
    
        wxItemKind GetItemKind(int iid)
        {
            switch (iid)
            {
            case moOnTop:
                return wxITEM_CHECK;
            case msLangEng:
            case msLangJpn:
                return wxITEM_RADIO;
            case mfSepExit:
            case meSepClearBuffer:
            case moSepOnTop:
            case moSepUILang:
            case mhSepAbout:
                return wxITEM_SEPARATOR;
            }
            return wxITEM_NORMAL;
        }
    
    
    

  9. Initialize the menu caption strings in its MainFrame constructor :
    
        //------------ Menu Captions ------------------:
        m_SMenuTops[mtFile] = L"&File";
        m_SMenuTops[mtEdit] = L"&Edit";
        m_SMenuTops[mtOpts] = L"&Options";
        m_SMenuTops[mtHelp] = L"&Help";
    
        //------------ Menu Item Captions -------------:
        m_SMenuFiles[mfOpen - IDM_FILE_START] = L"&Open";
        m_SMenuFiles[mfSave - IDM_FILE_START] = L"&Save";
        m_SMenuFiles[mfExit - IDM_FILE_START] = L"E&xit";
    
        m_SMenuEdits[meUndo - IDM_EDIT_START] = L"&Undo";
        m_SMenuEdits[meRedo - IDM_EDIT_START] = L"&Redo";
        m_SMenuEdits[meClearBuffer - IDM_EDIT_START] = L"&Clear Undo Buffer";
    
        m_SMenuOpts[moPrefs - IDM_OPTS_START] = L"&Preferences...";
        m_SMenuOpts[moOnTop - IDM_OPTS_START] = L"Always Stay On &Top";
        m_SMenuOpts[moUILang - IDM_OPTS_START] = L"User-interface &Language";
    
        m_SMenuHelps[mhTopics - IDM_HELP_START] = L"&Help Topics...";
        m_SMenuHelps[mhAbout - IDM_HELP_START] = L"&About...";
    
        m_SSubUILangs[msLangEng - IDM_SUB_LANG_START] = L"&English";
        m_SSubUILangs[msLangJpn - IDM_SUB_LANG_START] = L"&Japanese";
    
    
    

  10. Now write down code for the menu bar creation in its main source file :
    
    //---------------------------------------------------------------------------
    void MainFrame::AllocFreeMenuBar(bool bAlloc)
    {
        if (bAlloc)
        {
            const int ciitems[] = {cifile_items, ciedit_items, ciopt_items, cihelp_items};
            const int ciids_s[] = { IDM_FILE_START, IDM_EDIT_START
                , IDM_OPTS_START, IDM_HELP_START };
    
            wxString *pSs[] = { m_SMenuFiles, m_SMenuEdits, m_SMenuOpts, m_SMenuHelps };
            wxMenuItem *pmi;
            int i, n, iid;
            wxItemKind ikind;
            const byte *cpglyph;
            size_t nglyph;
            for (i = 0x00; i < cimenu_tops; i++)
            {
                m_menus[i] = new wxMenu;
                m_menubar.Append(m_menus[i], m_SMenuTops[i]);
    
                for (n = 0x00; n < ciitems[i]; n++)
                {
                    iid = ciids_s[i] + n;
                    ikind = GetItemKind(iid);
                    //------------------------------------------------------------:
                    if (iid == moUILang) // A menu item with sub-menu items:
                    {
                        pmi = AllocFreeUILangMenu(true);
                    }
                    else
                    {
                        pmi = new wxMenuItem(m_menus[i], iid
                            , pSs[i][n], wxEmptyString, ikind);
    
                        //---------------------- Glyph assignment ----------------:
                        if (iid == moOnTop)
                        {
                            pmi->SetBitmaps(wxBitmap::NewFromPNGData(opt_ontop_dn_png
                                , sn_opt_ontop_dn_png)
                                , wxBitmap::NewFromPNGData(opt_ontop_up_png
                                    , sn_opt_ontop_up_png));
                        }
                        else
                        {
                            cpglyph = NULL;
                            switch (iid)
                            {
                                case mfNew:
                                    cpglyph = btn_new_high_png;
                                    nglyph = sizeof(btn_new_high_png); break;
    
                                case mfOpen:
                                    cpglyph = file_open_png;
                                    nglyph = sn_file_open_png; break;
    
                                case mfSave:
                                    cpglyph = file_save_png;
                                    nglyph = sn_file_save_png; break;
    
                                case mfExit:
                                    cpglyph = file_exit_png;
                                    nglyph = sn_file_exit_png; break;
    
                                case meUndo :
                                    cpglyph = edit_undo_png;
                                    nglyph = sn_edit_undo_png; break;
    
                                case meRedo :
                                    cpglyph = edit_redo_png;
                                    nglyph = sn_edit_redo_png; break;
    
                                case meClearBuffer:
                                    cpglyph = edit_clear_png;
                                    nglyph = sn_edit_clear_png; break;
    
                                case moPrefs:
                                    cpglyph = opt_preferences_png;
                                    nglyph = sn_opt_preferences_png; break;
    
                                case mhTopics:
                                    cpglyph = help_topics_png;
                                    nglyph = sn_help_topics_png; break;
    
                                case mhAbout:
                                    cpglyph = help_about_png;
                                    nglyph = sn_help_about_png; break;
                            }
                            if (cpglyph)
                                pmi->SetBitmap(wxBitmap::NewFromPNGData(cpglyph, nglyph));
                        }
                        //---------------------- Glyph assignment ----------------.
                        Bind(wxEVT_MENU, &MainFrame::OnMenuClicks, this, iid);
                        m_menus[i]->Append(pmi);
                    }
                }
            }
            this->SetMenuBar(&m_menubar);
        }
        else
        {
            AllocFreeUILangMenu(false);
            this->SetMenuBar(NULL);
        }
    }
    //----------------------------------------------------------------------------
    
    
    

  11. Now write down code for creating a menu item with some sub-menu items :
    
    //----------------------------------------------------------------------------
    wxMenuItem *MainFrame::AllocFreeUILangMenu(bool bAlloc)
    {
        wxMenuItem *pmiret = NULL;
        if (bAlloc)
        {
            int iid;
            wxMenu *mnuUILang = new wxMenu;
            for (int i = 0x00; i < cisub_langs; i++)
            {
                iid = IDM_SUB_LANG_START + i;
                pmiret = new wxMenuItem(mnuUILang, iid, m_SSubUILangs[i]
                    , wxEmptyString, wxITEM_RADIO);
                pmiret = mnuUILang->Append(pmiret);
                this->Bind(wxEVT_MENU, &MainFrame::OnMenuClicks, this, iid);
                // this->Bind(wxEVT_MENU_HIGHLIGHT, &MainFrame::OnMenuHighlight, this, iid);
            }
            pmiret = m_menubar.GetMenu(mtOpts)->AppendSubMenu(mnuUILang
                , m_SMenuOpts[moUILang - IDM_OPTS_START]);
        }
        else
        {
            wxMenu *pmnuOpts = (wxMenu *)m_menubar.GetMenu(mtOpts);
            if (pmnuOpts)
            {
                pmiret = pmnuOpts->FindChildItem(moUILang);
                if (pmiret)
                {
                    wxMenu *pmnu = pmiret->GetSubMenu();
                    if (pmnu)
                    {
                        wxMenuItemList &lst = pmnu->GetMenuItems();
                        wxwxMenuItemListNode *p = lst.GetLast();
                        wxMenuItem *pmi;
                        while (p)
                        {
                            pmi = p->GetData();
                            p = p->GetPrevious();
                            pmnu->Remove(pmi);
                            delete pmi;
                        }
                        if (NULL != (pmiret = pmnuOpts->Remove(pmiret)))
                        {
                            delete pmiret;
                            pmiret = NULL;
                        }
                    }
                }
            }
        }
        return pmiret;
    }
    //----------------------------------------------------------------------------
    
    

  12. Finally write down code for each menu item click event (process all in one) :
    
    //----------------------------------------------------------------------------
    void MainFrame::OnMenuClicks(wxCommandEvent &evt)
    {
        int iid = evt.GetId();
        switch (iid)
        {
            case mfExit:
                Close(true);
    
            default:
            {
                wchar_t wsnum[020];
                wxMessageBox(_itow(iid, wsnum, 0x0a), L"MainFrame::OnMenuClicks"
                    , wxOK | wxICON_INFORMATION);
            }
        }
    }
    //----------------------------------------------------------------------------
    
    
    

  13. Let its constructor create the menu bar on its construction :
        ...
        
        m_SSubUILangs[msLangEng - IDM_SUB_LANG_START] = L"&English";
        m_SSubUILangs[msLangJpn - IDM_SUB_LANG_START] = L"&Japanese";
    
        AllocFreeMenuBar(true); // add anew.
        AllocFreeToolBar(true);
        AllocFreeStatusBar(true);
    
    
        ...
    

  14. Now in its close event handler :
        ...
    
        AllocFreeStatusBar(false);
        if (m_bmpStatus)
            delete m_bmpStatus;
        AllocFreeMenuBar(false); // Add anew.
    
        evt.Skip();
    
    
    

  15. Run the project and we could see the following application with a toolbar, a status bar and a menu on screen:
    wxapp_menubar

    Job Done !

Download Source code for the project (Visual C++ 2013) :
src_Test_Toolbar(+sb+mnb).7z (808 KB)

Previous tutorial (2) Next tutorial (4)


No comments:

Post a Comment