1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/palmos/menu.cpp 
   3 // Purpose:     wxMenu, wxMenuBar, wxMenuItem 
   4 // Author:      William Osborne - minimal working wxPalmOS port 
   8 // Copyright:   (c) William Osborne 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // --------------------------------------------------------------------------- 
  18 // --------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  39     #include "wx/ownerdrw.h" 
  46 // ---------------------------------------------------------------------------- 
  48 // ---------------------------------------------------------------------------- 
  50 extern wxMenu 
*wxCurrentPopupMenu
; 
  52 // ---------------------------------------------------------------------------- 
  54 // ---------------------------------------------------------------------------- 
  56 // the (popup) menu title has this special id 
  57 static const int idMenuTitle 
= -3; 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  63 // ============================================================================ 
  65 // ============================================================================ 
  67 #include "wx/listimpl.cpp" 
  69 WX_DEFINE_LIST( wxMenuInfoList 
) 
  71 #if wxUSE_EXTENDED_RTTI 
  73 WX_DEFINE_FLAGS( wxMenuStyle 
) 
  75 wxBEGIN_FLAGS( wxMenuStyle 
) 
  76     wxFLAGS_MEMBER(wxMENU_TEAROFF
) 
  77 wxEND_FLAGS( wxMenuStyle 
) 
  79 IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenu
, wxEvtHandler
,"wx/menu.h") 
  81 wxCOLLECTION_TYPE_INFO( wxMenuItem 
* , wxMenuItemList 
) ; 
  83 template<> void wxCollectionToVariantArray( wxMenuItemList 
const &theList
, wxxVariantArray 
&value
) 
  85     wxListCollectionToVariantArray
<wxMenuItemList::compatibility_iterator
>( theList 
, value 
) ; 
  88 wxBEGIN_PROPERTIES_TABLE(wxMenu
) 
  89     wxEVENT_PROPERTY( Select 
, wxEVT_COMMAND_MENU_SELECTED 
, wxCommandEvent
) 
  90     wxPROPERTY( Title
, wxString 
, SetTitle
, GetTitle
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
  91     wxREADONLY_PROPERTY_FLAGS( MenuStyle 
, wxMenuStyle 
, long , GetStyle 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
  92     wxPROPERTY_COLLECTION( MenuItems 
, wxMenuItemList 
, wxMenuItem
* , Append 
, GetMenuItems 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
  93 wxEND_PROPERTIES_TABLE() 
  95 wxBEGIN_HANDLERS_TABLE(wxMenu
) 
  96 wxEND_HANDLERS_TABLE() 
  98 wxDIRECT_CONSTRUCTOR_2( wxMenu 
, wxString 
, Title 
, long , MenuStyle  
) 
 100 WX_DEFINE_FLAGS( wxMenuBarStyle 
) 
 102 wxBEGIN_FLAGS( wxMenuBarStyle 
) 
 103     wxFLAGS_MEMBER(wxMB_DOCKABLE
) 
 104 wxEND_FLAGS( wxMenuBarStyle 
) 
 106 // the negative id would lead the window (its superclass !) to vetoe streaming out otherwise 
 107 bool wxMenuBarStreamingCallback( const wxObject 
*WXUNUSED(object
), wxWriter 
* , wxPersister 
* , wxxVariantArray 
& ) 
 112 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuBar
, wxWindow 
,"wx/menu.h",wxMenuBarStreamingCallback
) 
 114 IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenuInfo
, wxObject 
, "wx/menu.h" ) 
 116 wxBEGIN_PROPERTIES_TABLE(wxMenuInfo
) 
 117     wxREADONLY_PROPERTY( Menu 
, wxMenu
* , GetMenu 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 118     wxREADONLY_PROPERTY( Title 
, wxString 
, GetTitle 
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 119 wxEND_PROPERTIES_TABLE() 
 121 wxBEGIN_HANDLERS_TABLE(wxMenuInfo
) 
 122 wxEND_HANDLERS_TABLE() 
 124 wxCONSTRUCTOR_2( wxMenuInfo 
, wxMenu
* , Menu 
, wxString 
, Title 
) 
 126 wxCOLLECTION_TYPE_INFO( wxMenuInfo 
* , wxMenuInfoList 
) ; 
 128 template<> void wxCollectionToVariantArray( wxMenuInfoList 
const &theList
, wxxVariantArray 
&value
) 
 130     wxListCollectionToVariantArray
<wxMenuInfoList::compatibility_iterator
>( theList 
, value 
) ; 
 133 wxBEGIN_PROPERTIES_TABLE(wxMenuBar
) 
 134     wxPROPERTY_COLLECTION( MenuInfos 
, wxMenuInfoList 
, wxMenuInfo
* , Append 
, GetMenuInfos 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 135 wxEND_PROPERTIES_TABLE() 
 137 wxBEGIN_HANDLERS_TABLE(wxMenuBar
) 
 138 wxEND_HANDLERS_TABLE() 
 140 wxCONSTRUCTOR_DUMMY( wxMenuBar 
) 
 143 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
) 
 144 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxWindow
) 
 145 IMPLEMENT_DYNAMIC_CLASS(wxMenuInfo
, wxObject
) 
 148 const wxMenuInfoList
& wxMenuBar::GetMenuInfos() const 
 150     wxMenuInfoList
* list 
= const_cast< wxMenuInfoList
* >( &m_menuInfos 
) ; 
 151     WX_CLEAR_LIST( wxMenuInfoList 
, *list 
) ; 
 152     for( size_t i 
= 0 ; i 
< GetMenuCount() ; ++i 
) 
 154         wxMenuInfo
* info 
= new wxMenuInfo() ; 
 155         info
->Create( const_cast<wxMenuBar
*>(this)->GetMenu(i
) , GetLabelTop(i
) ) ; 
 156         list
->Append( info 
) ; 
 161 // --------------------------------------------------------------------------- 
 162 // wxMenu construction, adding and removing menu items 
 163 // --------------------------------------------------------------------------- 
 165 // Construct a menu with optional title (then use append) 
 170 // The wxWindow destructor will take care of deleting the submenus. 
 179 void wxMenu::Attach(wxMenuBarBase 
*menubar
) 
 181     wxMenuBase::Attach(menubar
); 
 186 int wxMenu::FindAccel(int id
) const 
 191 void wxMenu::UpdateAccel(wxMenuItem 
*item
) 
 195 #endif // wxUSE_ACCEL 
 197 // append a new item or submenu to the menu 
 198 bool wxMenu::DoInsertOrAppend(wxMenuItem 
*pItem
, size_t pos
) 
 200     if ( IsAttached() && GetMenuBar()->IsAttached() ) 
 202         // Regenerate the menu resource 
 203         GetMenuBar()->Refresh(); 
 209 void wxMenu::EndRadioGroup() 
 213 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*item
) 
 215     wxCHECK_MSG( item
, NULL
, _T("NULL item in wxMenu::DoAppend") ); 
 217     if(!wxMenuBase::DoAppend(item
) || !DoInsertOrAppend(item
)) 
 221     else if(IsAttached() && GetMenuBar()->IsAttached()) 
 223         // Regenerate the menu resource 
 224         GetMenuBar()->Refresh(); 
 230 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
 232     if (wxMenuBase::DoInsert(pos
, item
) && DoInsertOrAppend(item
, pos
)) 
 238 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
 240     // we need to find the items position in the child list 
 242     wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 243     for ( pos 
= 0; node
; pos
++ ) 
 245         if ( node
->GetData() == item 
) 
 248         node 
= node
->GetNext(); 
 251     // DoRemove() (unlike Remove) can only be called for existing item! 
 252     wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") ); 
 254     // remove the item from the menu 
 255     wxMenuItem 
*ret
=wxMenuBase::DoRemove(item
); 
 257     if ( IsAttached() && GetMenuBar()->IsAttached() ) 
 259         // Regenerate the menu resource 
 260         GetMenuBar()->Refresh(); 
 266 // --------------------------------------------------------------------------- 
 267 // accelerator helpers 
 268 // --------------------------------------------------------------------------- 
 272 // create the wxAcceleratorEntries for our accels and put them into provided 
 273 // array - return the number of accels we have 
 274 size_t wxMenu::CopyAccels(wxAcceleratorEntry 
*accels
) const 
 276     size_t count 
= GetAccelCount(); 
 277     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 279         *accels
++ = *m_accels
[n
]; 
 285 #endif // wxUSE_ACCEL 
 287 // --------------------------------------------------------------------------- 
 289 // --------------------------------------------------------------------------- 
 291 void wxMenu::SetTitle(const wxString
& label
) 
 295     if ( IsAttached() && GetMenuBar()->IsAttached() ) 
 297         // Regenerate the menu resource 
 298         GetMenuBar()->Refresh(); 
 302 // --------------------------------------------------------------------------- 
 304 // --------------------------------------------------------------------------- 
 306 bool wxMenu::PalmCommand(WXUINT 
WXUNUSED(param
), WXWORD id
) 
 311 // --------------------------------------------------------------------------- 
 313 // --------------------------------------------------------------------------- 
 315 wxWindow 
*wxMenu::GetWindow() const 
 320 // --------------------------------------------------------------------------- 
 322 // --------------------------------------------------------------------------- 
 324 void wxMenuBar::Init() 
 328 wxMenuBar::wxMenuBar() 
 332 wxMenuBar::wxMenuBar( long WXUNUSED(style
) ) 
 336 wxMenuBar::wxMenuBar(size_t count
, wxMenu 
*menus
[], const wxString titles
[], long WXUNUSED(style
)) 
 340 wxMenuBar::~wxMenuBar() 
 344 // --------------------------------------------------------------------------- 
 346 // --------------------------------------------------------------------------- 
 348 void wxMenuBar::Refresh() 
 350     wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") ); 
 352        // Regenerate the menu resource 
 356 WXHMENU 
wxMenuBar::Create() 
 361 int wxMenuBar::PalmPositionForWxMenu(wxMenu 
*menu
, int wxpos
) 
 366 // --------------------------------------------------------------------------- 
 367 // wxMenuBar functions to work with the top level submenus 
 368 // --------------------------------------------------------------------------- 
 370 void wxMenuBar::EnableTop(size_t pos
, bool enable
) 
 372     // Palm OS does not have support for grayed or disabled items 
 375 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
) 
 377     wxCHECK_RET( pos 
< GetMenuCount(), wxT("invalid menu index") ); 
 379     m_titles
[pos
]=wxStripMenuCodes(label
); 
 386        // Regenerate the menu resource 
 390 wxString 
wxMenuBar::GetLabelTop(size_t pos
) const 
 392     wxCHECK_MSG( pos 
< GetMenuCount(), wxEmptyString
, 
 393                  wxT("invalid menu index in wxMenuBar::GetLabelTop") ); 
 395     return wxMenuItem::GetLabelFromText(m_titles
[pos
]); 
 398 // --------------------------------------------------------------------------- 
 399 // wxMenuBar construction 
 400 // --------------------------------------------------------------------------- 
 402 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 404    wxMenu 
*menuOld 
= wxMenuBarBase::Replace(pos
, menu
, title
); 
 408     m_titles
[pos
]=wxStripMenuCodes(title
); 
 412         // Regenerate the menu resource 
 419 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 421     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
 424     m_titles
.Insert(wxStripMenuCodes(title
), pos
); 
 428         // Regenerate the menu resource 
 435 bool wxMenuBar::Append(wxMenu 
*menu
, const wxString
& title
) 
 437     if ( !wxMenuBarBase::Append(menu
, title
) ) 
 440     m_titles
.Add(wxStripMenuCodes(title
)); 
 444         // Regenerate the menu resource 
 451 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
 453     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
 457     m_titles
.RemoveAt(pos
); 
 461         // Regenerate the menu resource 
 470 void wxMenuBar::RebuildAccelTable() 
 474 #endif // wxUSE_ACCEL 
 476 int wxMenuBar::ProcessCommand(int ItemID
) 
 481     int MenuNum
=(ItemID
/1000)-1; 
 482     int ItemNum
=(ItemID
-(1000*(MenuNum
+1))); 
 484     // Should never happen, but it doesn't hurt to check anyway. 
 485     if(MenuNum
>GetMenuCount()) 
 489     wxMenu 
*ActiveMenu
=GetMenu(MenuNum
); 
 491     // Make sure this is a valid item. 
 492     if(ItemNum
>ActiveMenu
->GetMenuItemCount()) 
 496     wxMenuItem 
*ActiveItem
=ActiveMenu
->FindItemByPosition(ItemNum
); 
 497     int ActiveID
=ActiveItem
->GetId(); 
 502 /* Palm OS does not have good dynamic menu support.  About all you can do with 
 503  * the standard API calls is to add new items to an existing drop-down menu and 
 504  * hide/show items in a drop-down menu.  It is impossible to add, hide, or 
 505  * change the label on a drop-down menu. 
 507  * The easiest and simplest way around this limitation is to modify the Palm OS 
 508  * MenuBarType structure directly.  This gives limited ability to change the 
 509  * label on a drop-down menu.  I have not been able to find a safe way to add, 
 510  * delete, or resize drop-down menus in OS 6. 
 512  * The following routine attempt to work around these limitations present in the 
 513  * Palm OS API to provide limited dynamic menu support.  This solution is far 
 514  * from perfect, but the only other option is to wait for PalmSource to add full 
 515  * dynamic menu support, or to recreate the Palm OS menu system from scratch. 
 517  * This system is limited in that no more than 4 drop-down menus are allowed per 
 518  * menu bar, and the label for each drop-down menu is limited to 8 characters of 
 519  * text.  However, this menu system should work for most applications. 
 521  * Basically the menu routines select one of four menu bars, depending on 
 522  * whether or not the requested menu bar has one, two, three, or four drop-down 
 525  * These four "template" menu bars contain one, two, three, or four drop-down 
 526  * menus.  Each menu has a dummy menu item attached to it to allow the Palm OS 
 527  * MenuAddItem function to add the real items. 
 529  * The labels on the drop-down menus are then replaced with the labels of the 
 532  * The menu is then attached to the active window and the MenuAddItem API 
 533  * function is called to add the items to each drop-down menu.  Finally, 
 534  * MenuHideItem is called to remove the dummy items from each drop-down menu. 
 536 void wxMenuBar::LoadMenu() 
 541     // Handle to the currently running application database 
 544     // Get app database reference - needed for some Palm OS Menu API calls. 
 545     SysGetModuleDatabase(SysGetRefNum(), NULL
, &AppDB
); 
 547     // Get the number of menus 
 548     int NumMenus
=GetMenuCount(); 
 550     // Set up the pointers and handles 
 551     char *PalmOSMenuBarPtr
; 
 552     MemHandle PalmOSMenuBar
; 
 554     // Load the menu template and set up the menu pointers 
 557         PalmOSMenuBar
=DmGetResource(AppDB
,'MBAR',1000); 
 558         PalmOSMenuBarPtr
=(char *)MemHandleLock(PalmOSMenuBar
); 
 560         PalmOSMenuBarPtr
+=74; 
 564         PalmOSMenuBar
=DmGetResource(AppDB
,'MBAR',2000); 
 565         PalmOSMenuBarPtr
=(char *)MemHandleLock(PalmOSMenuBar
); 
 567         PalmOSMenuBarPtr
+=116; 
 571         PalmOSMenuBar
=DmGetResource(AppDB
,'MBAR',3000); 
 572         PalmOSMenuBarPtr
=(char *)MemHandleLock(PalmOSMenuBar
); 
 574         PalmOSMenuBarPtr
+=158; 
 578         // We support a maximum of 4 menus, so make sure that do not create 
 579         // more than we can handle. 
 582         PalmOSMenuBar
=DmGetResource(AppDB
,'MBAR',4000); 
 583         PalmOSMenuBarPtr
=(char *)MemHandleLock(PalmOSMenuBar
); 
 585         PalmOSMenuBarPtr
+=200; 
 588     // Set the proper names for the drop-down triggers. 
 589     for(i
=0;i
<NumMenus
;i
++) 
 591         // Clear out the old label 
 592         char buffer
[8]={' ',' ',' ',' ',' ',' ',' ',' '}; 
 593         MemMove(PalmOSMenuBarPtr
,buffer
,8); 
 595         wxString MenuTitle
=m_titles
.Item(i
); 
 597         // Make sure we don't copy more than 8 bytes for the label 
 598         int LengthToCopy
=MenuTitle
.length(); 
 602         MemMove(PalmOSMenuBarPtr
,MenuTitle
,LengthToCopy
); 
 603         PalmOSMenuBarPtr
+=11; 
 606     // We are done with the menu pointer. 
 607     MemHandleUnlock(PalmOSMenuBar
); 
 608     DmReleaseResource(PalmOSMenuBar
); 
 610     // We must make the menu active before we can add items to the drop-down 
 612     FrmSetMenu(FrmGetActiveForm(),AppDB
,NumMenus
*1000); 
 614     /* Add the menu items to the drop-down triggers.  This must be done after 
 615      * setting the triggers, because setting the names of drop-down triggers 
 616      * that have a variable number of items requires carefull calculation of 
 617      * the offsets in the MenuBarType structure.  Setting the triggers first 
 620     for(i
=0;i
<NumMenus
;i
++) 
 622         wxMenu 
*CurrentMenu
=GetMenu(i
); 
 624         for(j
=0;j
<CurrentMenu
->GetMenuItemCount();j
++) 
 626             wxMenuItem 
*CurrentItem
=CurrentMenu
->FindItemByPosition(j
); 
 627             wxString ItemLabel
=CurrentItem
->GetLabel(); 
 629             if(CurrentItem
->IsSeparator()==true) 
 631                 char Separator
=MenuSeparatorChar
; 
 633                     MenuAddItem(9000+i
,((i
*1000)+1000)+j
,0x00,&Separator
); 
 635                     MenuAddItem(((i
*1000)+1000)+j
-1,((i
*1000)+1000)+j
,0x00,&Separator
); 
 640                     MenuAddItem(9000+i
,((i
*1000)+1000)+j
,0x00,ItemLabel
); 
 642                     MenuAddItem(((i
*1000)+1000)+j
-1,((i
*1000)+1000)+j
,0x00,ItemLabel
); 
 646         // Hide the dummy menu item, since we don't need it anymore. 
 647         MenuHideItem(9000+i
); 
 651 void wxMenuBar::Attach(wxFrame 
*frame
) 
 653     // before attaching preprocess menus to not include wxID_EXIT item 
 654     // as PalmOS guidelines suggest 
 660     while( item 
= FindItem(wxID_EXIT
) ) 
 662         menu 
= item
->GetMenu(); 
 663         if( !menu 
) break; // something broken ? 
 665         size_t count 
= menu
->GetMenuItemCount(); 
 666         if( count 
== 0 ) break; // something broken ? 
 668         // if EXIT is last item in menu 
 669         if( menu
->FindItemByPosition( count 
- 1 ) == item 
) 
 671             menu
->Destroy( item 
); 
 673             // was more than one item? 
 674             // was previous separator ? 
 677                 item 
= menu
->FindItemByPosition( count 
- 2 ); 
 678                 if(item 
&& item
->IsSeparator()) 
 679                     menu
->Destroy( item 
); 
 683         // if EXIT is first item in menu 
 684         else if( menu
->FindItemByPosition( 0 ) == item 
) 
 686             menu
->Destroy( item 
); 
 688             // was more than one item? 
 689             // was previous separator ? 
 692                 item 
= menu
->FindItemByPosition( 0 ); 
 693                 if(item 
&& item
->IsSeparator()) 
 694                     menu
->Destroy( item 
); 
 698         // if EXIT is in the middle but before and after are selectors 
 701             i 
= 1; // 0 case already done 
 702             while ( (i 
< count
) && (menu
->FindItemByPosition( 0 ) != item
) ) 
 707             if (i 
>= count
) break; 
 708             if (menu
->FindItemByPosition( i 
) != item
) break; 
 709             menu
->Destroy( item 
); 
 710             item 
= menu
->FindItemByPosition( i 
); 
 712                  item
->IsSeparator() && 
 713                  menu
->FindItemByPosition( i
-1 )->IsSeparator() ) 
 715                 // noe need for two neighbouring separators 
 716                 menu
->Destroy( item 
); 
 721     // check if we received any empty menu! 
 723     while(i 
< GetMenuCount()) 
 727         if( menu 
&& (menu
->GetMenuItemCount()==0) ) 
 736     wxMenuBarBase::Attach(frame
); 
 741 void wxMenuBar::Detach() 
 743     wxMenuBarBase::Detach(); 
 746 #endif // wxUSE_MENUS