core/vgui/impl/mfc/vgui_mfc_utils.cxx
Go to the documentation of this file.
00001 // This is core/vgui/impl/mfc/vgui_mfc_utils.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief See vgui_mfc_utils.h for a description of this file.
00008 // \author  Marko Bacic, Oxford RRG
00009 // \date    4 August 2000
00010 //
00011 // \verbatim
00012 //  Modifications
00013 //   20-JUL-2001  K.Y.McGaul  Added menu accelerators.
00014 //   22-AUG-2001  K.Y.McGaul  Added destructor to fix memory leak: all menus now deleted.
00015 //   25-JUL-2006  A. Tamrakar Fixed the "Popup Menu timeout" bug.
00016 //   11-FEB-2007  A. Khropov Fixed the "Main menu callbacks not initialized" bug.
00017 // \endverbatim
00018 
00019 #include "vgui_mfc_utils.h"
00020 #include <vgui/vgui_command.h>
00021 #include <vcl_iostream.h>
00022 static bool debug = false;
00023 
00024 
00025 //: Called within message service routine of vgui_mfc_mainfrm.
00026 void vgui_mfc_utils::menu_dispatcher(UINT nID)
00027 {
00028   // Make sure nID is in the relevant range
00029   ASSERT(nID>=ID_MENU_ITEMS && int(nID)<ID_MENU_ITEMS+item_count);
00030   // Call the callback function associated with the menu item
00031   callbacks[nID-ID_MENU_ITEMS]->execute();
00032 }
00033 
00034 //: (Create if necessary and) return singleton instance of this class.
00035 vgui_mfc_utils *vgui_mfc_utils::instance()
00036 {
00037   static vgui_mfc_utils instance_;
00038   return &instance_;
00039 }
00040 
00041 //: Destructor.
00042 vgui_mfc_utils::~vgui_mfc_utils()
00043 {
00044   for (unsigned i=0; i<menus_to_be_deleted.size(); i++)
00045     delete menus_to_be_deleted[i];
00046 }
00047 
00048 //: Add keyboard shortcut for this menu item to our accelerator table (accels).
00049 //  Also add text to the_menu_name for the shortcut.
00050 void vgui_mfc_utils::add_menu_accelerator(const vgui_menu_item menu_item, const WORD function_id, vcl_string& the_menu_name)
00051 {
00052   ACCEL acc;
00053   acc.cmd = function_id;
00054   the_menu_name += vcl_string("\t");  // tab to right hand side of label
00055   if (menu_item.short_cut.mod == vgui_SHIFT)
00056   {
00057     acc.fVirt = FSHIFT|FVIRTKEY;  // you can't use modifiers unless you use virtual key codes
00058                                   // hence the need for FVIRTKEY here.
00059     the_menu_name += vcl_string("Shift+");
00060   }
00061   else if (menu_item.short_cut.mod == vgui_CTRL)
00062   {
00063     acc.fVirt = FCONTROL|FVIRTKEY;
00064     the_menu_name += vcl_string("Ctrl+");
00065   }
00066   else if (menu_item.short_cut.mod == vgui_ALT)
00067   {
00068     acc.fVirt = FALT|FVIRTKEY;
00069     the_menu_name += vcl_string("Alt+");
00070   }
00071   else // we are going to give a virtual key code, even if there is no modifier:
00072     acc.fVirt = FVIRTKEY;
00073   // VkKeyScan converts the key code from ASCII to a virtual key code:
00074   acc.key = VkKeyScan(menu_item.short_cut.key);
00075   the_menu_name += menu_item.short_cut.key;
00076   accels.push_back(acc);
00077 }
00078 
00079 //: Create a MFC sub-menu from the given vgui_menu.
00080 HMENU vgui_mfc_utils::add_submenu(const vgui_menu& menu)
00081 {
00082   CMenu *popdown_menu;
00083 
00084   // Create a new menu
00085   popdown_menu = new CMenu();
00086   menus_to_be_deleted.push_back(popdown_menu);
00087   popdown_menu->CreatePopupMenu();
00088 
00089   for (unsigned i=0;i<menu.size();i++)
00090   {
00091     if (menu[i].is_separator())
00092     {
00093       if (debug) vcl_cerr << " <separator>\n";
00094         popdown_menu->AppendMenu(MF_SEPARATOR);
00095     }
00096     else if (menu[i].is_command())
00097     {
00098       if (debug) vcl_cerr << " <command>\n";
00099       int the_menu_id = ID_MENU_ITEMS+item_count++;
00100       vcl_string the_menu_name = menu[i].name;
00101 
00102       // Add menu accelerators:
00103       if (menu[i].short_cut.mod!=vgui_MODIFIER_NULL ||
00104           menu[i].short_cut.key!=vgui_KEY_NULL)
00105          add_menu_accelerator(menu[i], the_menu_id, the_menu_name);
00106 
00107       popdown_menu->AppendMenu(MF_STRING,the_menu_id,the_menu_name.c_str());
00108       // Add to our callback list, the associated callback function pointer
00109       vgui_command *cmnd = static_cast<vgui_command *>(menu[i].cmnd.as_pointer());
00110       callbacks.push_back(cmnd);
00111     }
00112     else if (menu[i].is_submenu()) {
00113       if (debug) vcl_cerr << " <submenu>\n";
00114       popdown_menu->AppendMenu(MF_POPUP,(UINT)add_submenu(*menu[i].menu),menu[i].name.c_str());
00115     }
00116   }
00117   return popdown_menu->GetSafeHmenu();
00118 }
00119 
00120 //: Sets the menu of the application window
00121 void vgui_mfc_utils::set_menu(const vgui_menu& menu)
00122 {
00123   // Get the application thread (this is the single CWinApp object associated with the application):
00124   CWinApp *theapp = AfxGetApp();
00125   // Obtain the main window associated with it
00126   CWnd *window = theapp->GetMainWnd();
00127  
00128   CMenu *menu_bar = new CMenu();
00129   menus_to_be_deleted.push_back(menu_bar);
00130   menu_bar->CreateMenu();
00131   window->SetMenu(menu_bar);
00132 
00133   for (unsigned i=0;i<menu.size();i++)
00134   {
00135     if (menu[i].is_separator())
00136     {
00137       if (debug) vcl_cerr << " <separator>\n";
00138       menu_bar->AppendMenu(MF_SEPARATOR);
00139     }
00140     else if (menu[i].is_command())
00141     {
00142       int the_menu_id = ID_MENU_ITEMS+item_count++;
00143       vcl_string the_menu_name = menu[i].name;
00144 
00145       // Add menu accelerators
00146       if (menu[i].short_cut.mod!=vgui_MODIFIER_NULL ||
00147           menu[i].short_cut.key!=vgui_KEY_NULL)
00148           add_menu_accelerator(menu[i], the_menu_id, the_menu_name);
00149 
00150       if (debug) vcl_cerr << " <command> " << menu[i].name << vcl_endl;
00151       menu_bar->AppendMenu(MF_STRING|MF_ENABLED,the_menu_id,
00152                            the_menu_name.c_str());
00153       // Add to our callback list, the associated callback function pointer
00154       vgui_command *cmnd = static_cast<vgui_command *>(menu[i].cmnd.as_pointer());
00155       callbacks.push_back(cmnd);
00156     }
00157     else if (menu[i].is_submenu())
00158     {
00159       if (debug) vcl_cerr << " <submenu> " << menu[i].name << vcl_endl;
00160       menu_bar->AppendMenu(MF_POPUP,(UINT)add_submenu(*menu[i].menu),menu[i].name.c_str());
00161     }
00162   }
00163   if (accels.size() > 0)
00164     AccelTable = CreateAcceleratorTable(&accels[0], accels.size());
00165   window->DrawMenuBar();
00166   window->UpdateWindow();
00167   window->ShowWindow(SW_SHOW);
00168 }
00169 
00170 //: Create popup menu.
00171 //  Make sure to call delete after its use otherwise MLK!!!
00172 CMenu *vgui_mfc_utils::set_popup_menu(const vgui_menu &menu)
00173 {
00174   //if there was a popup menu created before, 
00175   //delete the callback functions from that menu
00176   if (!first_popup)
00177     delete_last_popup_menu_callbacks();
00178   else
00179     first_popup = false;
00180 
00181   //store the current item count
00182   last_item_count = item_count;
00183 
00184   // Create a new menu
00185   CMenu *pop_up = new CMenu();
00186   pop_up->CreatePopupMenu();
00187 
00188   for (unsigned i=0;i<menu.size();i++)
00189   {
00190     if (menu[i].is_separator())
00191     {
00192       if (debug) vcl_cerr << " <separator>\n";
00193           pop_up->AppendMenu(MF_SEPARATOR);
00194     }
00195     else if (menu[i].is_command())
00196     {
00197       if (debug) vcl_cerr << " <command>\n";
00198       int the_menu_id = ID_MENU_ITEMS+item_count++;
00199       vcl_string the_menu_name = menu[i].name;
00200 
00201       // Add menu accelerators:
00202       if (menu[i].short_cut.mod!=vgui_MODIFIER_NULL ||
00203           menu[i].short_cut.key!=vgui_KEY_NULL)
00204           add_menu_accelerator(menu[i], the_menu_id, the_menu_name);
00205 
00206       pop_up->AppendMenu(MF_STRING,the_menu_id,menu[i].name.c_str());
00207       // Add to our callback list, the associated callback function pointer
00208       vgui_command *cmnd = static_cast<vgui_command *>(menu[i].cmnd.as_pointer());
00209       callbacks.push_back(cmnd);
00210     }
00211     else if (menu[i].is_submenu())
00212     {
00213       if (debug) vcl_cerr << " <submenu>\n";
00214         pop_up->AppendMenu(MF_POPUP,(UINT)add_submenu(*menu[i].menu),menu[i].name.c_str());
00215     }
00216   }
00217   return pop_up;
00218 }
00219 
00220 //: Delete the callback functions from the last popup menu.
00221 void vgui_mfc_utils::delete_last_popup_menu_callbacks()
00222 {
00223   //Amir: Delete the callbacks that were created for the last popup menu
00224   //      Without this step, the menu ids keep increasing everytime 
00225   //      a popup menu is created. However, the menu servicing table has a
00226   //      predefined range of ids it can handle (see line 32 of vgui_mfc_mainfrm.cxx)
00227   //      which means that after a while the popup menus just stop functioning. 
00228   //      The local lab jargon for this event is "Menu timeout" :)
00229 
00230   //delete all the callbacks up to the last item count
00231   for (int i=0; i<item_count-last_item_count; i++)
00232     callbacks.pop_back();
00233 
00234   //reset item_count
00235   item_count = last_item_count;
00236 
00237 }