core/vgui/impl/wx/vgui_wx_menu.cxx
Go to the documentation of this file.
00001 // This is core/vgui/impl/wx/vgui_wx_menu.cxx
00002 #include "vgui_wx_menu.h"
00003 //=========================================================================
00004 //:
00005 // \file
00006 // \brief  wxWidgets implementation of vgui_menu.
00007 //
00008 // See vgui_wx_menu.h for details.
00009 //=========================================================================
00010 
00011 #include <vgui/vgui_command.h>
00012 
00013 #include <wx/menu.h>
00014 
00015 #ifndef wxCommandEventHandler        // wxWidgets-2.5.3 doesn't define this
00016 #define wxCommandEventHandler(func) \
00017     (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxCommandEventFunction, &func)
00018 #endif
00019 
00020 #include <vcl_cctype.h>  // for vcl_toupper
00021 #include <vcl_cassert.h>
00022 
00023 //-------------------------------------------------------------------------
00024 // Private helpers - declarations.
00025 //-------------------------------------------------------------------------
00026 namespace
00027 {
00028   wxMenuBar* strip_extra_separators(wxMenuBar* menubar);
00029   wxMenu*    strip_extra_separators(wxMenu* menu);
00030 }
00031 
00032 //-------------------------------------------------------------------------
00033 // vgui_wx_menu implementation - construction & destruction.
00034 //-------------------------------------------------------------------------
00035 const int vgui_wx_menu::MENU_ID_OFFSET = 1000;
00036 
00037 vgui_wx_menu::vgui_wx_menu(void)
00038   : item_count_(-1)
00039 {
00040 }
00041 
00042 vgui_wx_menu::~vgui_wx_menu(void)
00043 {
00044   disconnect_handlers();
00045 
00046   // ***** doesn't this get destroyed by the parent window?
00047   //delete menu_;
00048 }
00049 
00050 //-------------------------------------------------------------------------
00051 // vgui_wx_menu implementation.
00052 //-------------------------------------------------------------------------
00053 BEGIN_EVENT_TABLE(vgui_wx_menu, wxEvtHandler)
00054 END_EVENT_TABLE()
00055 
00056 //: Create a wxMenuBar that maps to the vgui_menu.
00057 wxMenuBar* vgui_wx_menu::create_wx_menubar(const vgui_menu& menu)
00058 {
00059   disconnect_handlers();
00060 
00061   wxMenuBar* menubar = new wxMenuBar;
00062 
00063   // ***** do i need to reset the item_count_?
00064   for (unsigned int i = 0; i < menu.size(); i++)
00065   {
00066     if (menu[i].is_separator())
00067     {
00068       assert(false);
00069     }
00070     else if (menu[i].is_command())
00071     {
00072 #ifdef DEBUG
00073       vcl_cerr << __FILE__ ":command as top level menu item!\n";
00074 #endif
00075       // create a submenu and add this top-level command to it
00076       vgui_menu submenu;
00077       submenu.add(menu[i].name,
00078                   menu[i].cmnd,
00079                   menu[i].short_cut.key,
00080                   menu[i].short_cut.mod);
00081 
00082       menubar->Append(create_wx_submenu(submenu), wxString(menu[i].name.c_str(),wxConvUTF8));
00083     }
00084     else if (menu[i].is_submenu())
00085     {
00086       menubar->Append(
00087         create_wx_submenu(*menu[i].menu), wxString(menu[i].name.c_str(),wxConvUTF8));
00088     }
00089   }
00090 
00091   return strip_extra_separators(menubar);
00092 }
00093 
00094 //: Create a wxMenu that maps to the vgui_menu.
00095 wxMenu* vgui_wx_menu::create_wx_menu(const vgui_menu& menu)
00096 {
00097   disconnect_handlers();
00098   return strip_extra_separators(create_wx_submenu(menu));
00099 }
00100 
00101 //: Helper class that actually builds the mapped menus.
00102 wxMenu* vgui_wx_menu::create_wx_submenu(const vgui_menu& menu)
00103 {
00104   wxMenu* popup = new wxMenu;
00105 
00106   for (unsigned int i = 0; i < menu.size(); i++)
00107   {
00108     const int menu_id = MENU_ID_OFFSET + ++item_count_;
00109 
00110     if (menu[i].is_separator())
00111     {
00112       popup->AppendSeparator();
00113     }
00114     else if (menu[i].is_command())
00115     {
00116       // add menu accelerators
00117       vcl_string menu_item = menu[i].name
00118                            + create_accelerator_string(menu[i]);
00119 
00120       popup->Append(menu_id, wxString(menu_item.c_str(),wxConvUTF8));
00121 
00122       // save the handle
00123       handlers_[menu_id] = static_cast<vgui_command_sptr>(menu[i].cmnd);
00124 
00125       // connect the event handler
00126       Connect(menu_id, wxEVT_COMMAND_MENU_SELECTED,
00127               wxCommandEventHandler(vgui_wx_menu::on_command));
00128     }
00129     else if (menu[i].is_submenu())
00130     {
00131       popup->Append(
00132         menu_id,
00133         wxString(menu[i].name.c_str(),wxConvUTF8),
00134         strip_extra_separators(create_wx_submenu(*menu[i].menu)));
00135     }
00136   }
00137 
00138   return popup;
00139 }
00140 
00141 //: Create the accelerator substring to add to the menu item name.
00142 vcl_string
00143 vgui_wx_menu::create_accelerator_string(const vgui_menu_item& item) const
00144 {
00145   if ( item.short_cut.key == vgui_KEY_NULL )
00146   {
00147     return vcl_string("");
00148   }
00149 
00150   vcl_string accelerator("\t");
00151 
00152   // ***** taken from mfc impl, but what about combinations??
00153   if (item.short_cut.mod == vgui_CTRL)
00154   {
00155     accelerator += vcl_string("Ctrl+");
00156   }
00157   else if (item.short_cut.mod == vgui_SHIFT)
00158   {
00159     accelerator += vcl_string("Shift+");
00160   }
00161   else if (item.short_cut.mod == vgui_ALT)
00162   {
00163     accelerator += vcl_string("Alt+");
00164   }
00165 
00166   accelerator += vcl_toupper(item.short_cut.key);
00167 
00168   return accelerator;
00169 }
00170 
00171 //: Disconnect the event handlers from the event table.
00172 void vgui_wx_menu::disconnect_handlers()
00173 {
00174   vcl_map<int,vgui_command_sptr>::const_iterator iter = handlers_.begin();
00175   for (; iter != handlers_.end(); iter++)
00176   {
00177     Disconnect(iter->first, wxEVT_COMMAND_MENU_SELECTED);
00178   }
00179   handlers_.clear();
00180 }
00181 
00182 //: The event handler that delegates the call to the correct command.
00183 void vgui_wx_menu::on_command(wxCommandEvent& event)
00184 {
00185   handlers_[event.GetId()]->execute();
00186 }
00187 
00188 //-------------------------------------------------------------------------
00189 // Private helpers - declarations.
00190 //-------------------------------------------------------------------------
00191 namespace
00192 {
00193   wxMenuBar* strip_extra_separators(wxMenuBar* menubar)
00194   {
00195     assert(menubar);
00196 
00197     for (unsigned int i = 0; i < menubar->GetMenuCount(); i++)
00198     {
00199       strip_extra_separators(menubar->GetMenu(i));
00200     }
00201 
00202     return menubar;
00203   }
00204 
00205   wxMenu* strip_extra_separators(wxMenu* menu)
00206   {
00207     assert(menu);
00208 
00209     wxMenuItemList::Node* node = menu->GetMenuItems().GetLast();
00210     while (node)
00211     {
00212       if (node->GetData()->IsSeparator())
00213       {
00214         if ( !node->GetNext()      ||                // it's the last item
00215              !node->GetPrevious()  ||                // it's the first item
00216               node->GetPrevious()->GetData()->IsSeparator()) // it's double
00217         {
00218           menu->Delete(node->GetData());
00219           node = menu->GetMenuItems().GetLast();
00220         }
00221       }
00222       node = node->GetPrevious();
00223     }
00224 
00225     return menu;
00226   }
00227 } // unnamed namespace