///////////////////////////////////////////////////////////////////////////// // Name: src/palmos/menu.cpp // Purpose: wxMenu, wxMenuBar, wxMenuItem // Author: William Osborne - minimal working wxPalmOS port // Modified by: // Created: 10/12/04 // RCS-ID: $Id$ // Copyright: (c) William Osborne // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // =========================================================================== // declarations // =========================================================================== // --------------------------------------------------------------------------- // headers // --------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #if wxUSE_MENUS #include "wx/menu.h" #ifndef WX_PRECOMP #include "wx/frame.h" #include "wx/utils.h" #include "wx/intl.h" #include "wx/log.h" #endif #if wxUSE_OWNER_DRAWN #include "wx/ownerdrw.h" #endif #include #include #include // ---------------------------------------------------------------------------- // global variables // ---------------------------------------------------------------------------- extern wxMenu *wxCurrentPopupMenu; // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- // the (popup) menu title has this special id static const int idMenuTitle = -3; // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- // ============================================================================ // implementation // ============================================================================ #include "wx/listimpl.cpp" WX_DEFINE_LIST( wxMenuInfoList ) #if wxUSE_EXTENDED_RTTI WX_DEFINE_FLAGS( wxMenuStyle ) wxBEGIN_FLAGS( wxMenuStyle ) wxFLAGS_MEMBER(wxMENU_TEAROFF) wxEND_FLAGS( wxMenuStyle ) IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenu, wxEvtHandler,"wx/menu.h") wxCOLLECTION_TYPE_INFO( wxMenuItem * , wxMenuItemList ) ; template<> void wxCollectionToVariantArray( wxMenuItemList const &theList, wxxVariantArray &value) { wxListCollectionToVariantArray( theList , value ) ; } wxBEGIN_PROPERTIES_TABLE(wxMenu) wxEVENT_PROPERTY( Select , wxEVT_COMMAND_MENU_SELECTED , wxCommandEvent) wxPROPERTY( Title, wxString , SetTitle, GetTitle, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) wxREADONLY_PROPERTY_FLAGS( MenuStyle , wxMenuStyle , long , GetStyle , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style wxPROPERTY_COLLECTION( MenuItems , wxMenuItemList , wxMenuItem* , Append , GetMenuItems , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) wxEND_PROPERTIES_TABLE() wxBEGIN_HANDLERS_TABLE(wxMenu) wxEND_HANDLERS_TABLE() wxDIRECT_CONSTRUCTOR_2( wxMenu , wxString , Title , long , MenuStyle ) WX_DEFINE_FLAGS( wxMenuBarStyle ) wxBEGIN_FLAGS( wxMenuBarStyle ) wxFLAGS_MEMBER(wxMB_DOCKABLE) wxEND_FLAGS( wxMenuBarStyle ) // the negative id would lead the window (its superclass !) to vetoe streaming out otherwise bool wxMenuBarStreamingCallback( const wxObject *WXUNUSED(object), wxWriter * , wxPersister * , wxxVariantArray & ) { return true ; } IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuBar, wxWindow ,"wx/menu.h",wxMenuBarStreamingCallback) IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenuInfo, wxObject , "wx/menu.h" ) wxBEGIN_PROPERTIES_TABLE(wxMenuInfo) wxREADONLY_PROPERTY( Menu , wxMenu* , GetMenu , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) wxREADONLY_PROPERTY( Title , wxString , GetTitle , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) wxEND_PROPERTIES_TABLE() wxBEGIN_HANDLERS_TABLE(wxMenuInfo) wxEND_HANDLERS_TABLE() wxCONSTRUCTOR_2( wxMenuInfo , wxMenu* , Menu , wxString , Title ) wxCOLLECTION_TYPE_INFO( wxMenuInfo * , wxMenuInfoList ) ; template<> void wxCollectionToVariantArray( wxMenuInfoList const &theList, wxxVariantArray &value) { wxListCollectionToVariantArray( theList , value ) ; } wxBEGIN_PROPERTIES_TABLE(wxMenuBar) wxPROPERTY_COLLECTION( MenuInfos , wxMenuInfoList , wxMenuInfo* , Append , GetMenuInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) wxEND_PROPERTIES_TABLE() wxBEGIN_HANDLERS_TABLE(wxMenuBar) wxEND_HANDLERS_TABLE() wxCONSTRUCTOR_DUMMY( wxMenuBar ) #else IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow) IMPLEMENT_DYNAMIC_CLASS(wxMenuInfo, wxObject) #endif const wxMenuInfoList& wxMenuBar::GetMenuInfos() const { wxMenuInfoList* list = const_cast< wxMenuInfoList* >( &m_menuInfos ) ; WX_CLEAR_LIST( wxMenuInfoList , *list ) ; for( size_t i = 0 ; i < GetMenuCount() ; ++i ) { wxMenuInfo* info = new wxMenuInfo() ; info->Create( const_cast(this)->GetMenu(i) , GetLabelTop(i) ) ; list->Append( info ) ; } return m_menuInfos ; } // --------------------------------------------------------------------------- // wxMenu construction, adding and removing menu items // --------------------------------------------------------------------------- // Construct a menu with optional title (then use append) void wxMenu::Init() { } // The wxWindow destructor will take care of deleting the submenus. wxMenu::~wxMenu() { } void wxMenu::Break() { } void wxMenu::Attach(wxMenuBarBase *menubar) { wxMenuBase::Attach(menubar); } #if wxUSE_ACCEL int wxMenu::FindAccel(int id) const { return wxNOT_FOUND; } void wxMenu::UpdateAccel(wxMenuItem *item) { } #endif // wxUSE_ACCEL // append a new item or submenu to the menu bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) { if ( IsAttached() && GetMenuBar()->IsAttached() ) { // Regenerate the menu resource GetMenuBar()->Refresh(); } return true; } void wxMenu::EndRadioGroup() { } wxMenuItem* wxMenu::DoAppend(wxMenuItem *item) { wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") ); if(!wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item)) { return NULL; } else if(IsAttached() && GetMenuBar()->IsAttached()) { // Regenerate the menu resource GetMenuBar()->Refresh(); } return item; } wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item) { if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos)) return item; else return NULL; } wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) { // we need to find the items position in the child list size_t pos; wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); for ( pos = 0; node; pos++ ) { if ( node->GetData() == item ) break; node = node->GetNext(); } // DoRemove() (unlike Remove) can only be called for existing item! wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") ); // remove the item from the menu wxMenuItem *ret=wxMenuBase::DoRemove(item); if ( IsAttached() && GetMenuBar()->IsAttached() ) { // Regenerate the menu resource GetMenuBar()->Refresh(); } return ret; } // --------------------------------------------------------------------------- // accelerator helpers // --------------------------------------------------------------------------- #if wxUSE_ACCEL // create the wxAcceleratorEntries for our accels and put them into provided // array - return the number of accels we have size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const { size_t count = GetAccelCount(); for ( size_t n = 0; n < count; n++ ) { *accels++ = *m_accels[n]; } return count; } #endif // wxUSE_ACCEL // --------------------------------------------------------------------------- // set wxMenu title // --------------------------------------------------------------------------- void wxMenu::SetTitle(const wxString& label) { m_title = label; if ( IsAttached() && GetMenuBar()->IsAttached() ) { // Regenerate the menu resource GetMenuBar()->Refresh(); } } // --------------------------------------------------------------------------- // event processing // --------------------------------------------------------------------------- bool wxMenu::PalmCommand(WXUINT WXUNUSED(param), WXWORD id) { return false; } // --------------------------------------------------------------------------- // other // --------------------------------------------------------------------------- wxWindow *wxMenu::GetWindow() const { return NULL; } // --------------------------------------------------------------------------- // Menu Bar // --------------------------------------------------------------------------- void wxMenuBar::Init() { } wxMenuBar::wxMenuBar() { } wxMenuBar::wxMenuBar( long WXUNUSED(style) ) { } wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style)) { } wxMenuBar::~wxMenuBar() { } // --------------------------------------------------------------------------- // wxMenuBar helpers // --------------------------------------------------------------------------- void wxMenuBar::Refresh() { wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") ); // Regenerate the menu resource LoadMenu(); } WXHMENU wxMenuBar::Create() { return NULL; } int wxMenuBar::PalmPositionForWxMenu(wxMenu *menu, int wxpos) { return -1; } // --------------------------------------------------------------------------- // wxMenuBar functions to work with the top level submenus // --------------------------------------------------------------------------- void wxMenuBar::EnableTop(size_t pos, bool enable) { // Palm OS does not have support for grayed or disabled items } void wxMenuBar::SetLabelTop(size_t pos, const wxString& label) { wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") ); m_titles[pos]=wxStripMenuCodes(label); if ( !IsAttached() ) { return; } // Regenerate the menu resource Refresh(); } wxString wxMenuBar::GetLabelTop(size_t pos) const { wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString, wxT("invalid menu index in wxMenuBar::GetLabelTop") ); return wxMenuItem::GetLabelFromText(m_titles[pos]); } // Gets the original label at the top-level of the menubar wxString wxMenuBar::GetMenuLabel(size_t pos) const { wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString, wxT("invalid menu index in wxMenuBar::GetMenuLabel") ); return m_titles[pos]; } // --------------------------------------------------------------------------- // wxMenuBar construction // --------------------------------------------------------------------------- wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) { wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title); if ( !menuOld ) return NULL; m_titles[pos]=wxStripMenuCodes(title); if ( IsAttached() ) { // Regenerate the menu resource Refresh(); } return menuOld; } bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title) { if ( !wxMenuBarBase::Insert(pos, menu, title) ) return false; m_titles.Insert(wxStripMenuCodes(title), pos); if ( IsAttached() ) { // Regenerate the menu resource Refresh(); } return true; } bool wxMenuBar::Append(wxMenu *menu, const wxString& title) { if ( !wxMenuBarBase::Append(menu, title) ) return false; m_titles.Add(wxStripMenuCodes(title)); if(IsAttached()) { // Regenerate the menu resource Refresh(); } return true; } wxMenu *wxMenuBar::Remove(size_t pos) { wxMenu *menu = wxMenuBarBase::Remove(pos); if ( !menu ) return NULL; m_titles.RemoveAt(pos); if (IsAttached()) { // Regenerate the menu resource Refresh(); } return menu; } #if wxUSE_ACCEL void wxMenuBar::RebuildAccelTable() { } #endif // wxUSE_ACCEL int wxMenuBar::ProcessCommand(int ItemID) { if(!IsAttached()) return -1; int MenuNum=(ItemID/1000)-1; int ItemNum=(ItemID-(1000*(MenuNum+1))); // Should never happen, but it doesn't hurt to check anyway. if(MenuNum>GetMenuCount()) return -1; // Get the menu wxMenu *ActiveMenu=GetMenu(MenuNum); // Make sure this is a valid item. if(ItemNum>ActiveMenu->GetMenuItemCount()) return -1; // Get the item wxMenuItem *ActiveItem=ActiveMenu->FindItemByPosition(ItemNum); int ActiveID=ActiveItem->GetId(); return ActiveID; } /* Palm OS does not have good dynamic menu support. About all you can do with * the standard API calls is to add new items to an existing drop-down menu and * hide/show items in a drop-down menu. It is impossible to add, hide, or * change the label on a drop-down menu. * * The easiest and simplest way around this limitation is to modify the Palm OS * MenuBarType structure directly. This gives limited ability to change the * label on a drop-down menu. I have not been able to find a safe way to add, * delete, or resize drop-down menus in OS 6. * * The following routine attempt to work around these limitations present in the * Palm OS API to provide limited dynamic menu support. This solution is far * from perfect, but the only other option is to wait for PalmSource to add full * dynamic menu support, or to recreate the Palm OS menu system from scratch. * * This system is limited in that no more than 4 drop-down menus are allowed per * menu bar, and the label for each drop-down menu is limited to 8 characters of * text. However, this menu system should work for most applications. * * Basically the menu routines select one of four menu bars, depending on * whether or not the requested menu bar has one, two, three, or four drop-down * menus. * * These four "template" menu bars contain one, two, three, or four drop-down * menus. Each menu has a dummy menu item attached to it to allow the Palm OS * MenuAddItem function to add the real items. * * The labels on the drop-down menus are then replaced with the labels of the * real menus. * * The menu is then attached to the active window and the MenuAddItem API * function is called to add the items to each drop-down menu. Finally, * MenuHideItem is called to remove the dummy items from each drop-down menu. */ void wxMenuBar::LoadMenu() { int i=0; int j=0; // Handle to the currently running application database DmOpenRef AppDB; // Get app database reference - needed for some Palm OS Menu API calls. SysGetModuleDatabase(SysGetRefNum(), NULL, &AppDB); // Get the number of menus int NumMenus=GetMenuCount(); // Set up the pointers and handles char *PalmOSMenuBarPtr; MemHandle PalmOSMenuBar; // Load the menu template and set up the menu pointers if(NumMenus==1) { PalmOSMenuBar=DmGetResource(AppDB,'MBAR',1000); PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar); PalmOSMenuBarPtr+=74; } else if(NumMenus==2) { PalmOSMenuBar=DmGetResource(AppDB,'MBAR',2000); PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar); PalmOSMenuBarPtr+=116; } else if(NumMenus==3) { PalmOSMenuBar=DmGetResource(AppDB,'MBAR',3000); PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar); PalmOSMenuBarPtr+=158; } else { // We support a maximum of 4 menus, so make sure that do not create // more than we can handle. NumMenus=4; PalmOSMenuBar=DmGetResource(AppDB,'MBAR',4000); PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar); PalmOSMenuBarPtr+=200; } // Set the proper names for the drop-down triggers. for(i=0;i8) LengthToCopy=8; MemMove(PalmOSMenuBarPtr,MenuTitle,LengthToCopy); PalmOSMenuBarPtr+=11; } // We are done with the menu pointer. MemHandleUnlock(PalmOSMenuBar); DmReleaseResource(PalmOSMenuBar); // We must make the menu active before we can add items to the drop-down // triggers. FrmSetMenu(FrmGetActiveForm(),AppDB,NumMenus*1000); /* Add the menu items to the drop-down triggers. This must be done after * setting the triggers, because setting the names of drop-down triggers * that have a variable number of items requires carefull calculation of * the offsets in the MenuBarType structure. Setting the triggers first * avoids this. */ for(i=0;iGetMenuItemCount();j++) { wxMenuItem *CurrentItem=CurrentMenu->FindItemByPosition(j); wxString ItemLabel=CurrentItem->GetLabel(); if(CurrentItem->IsSeparator()==true) { char Separator=MenuSeparatorChar; if(j==0) MenuAddItem(9000+i,((i*1000)+1000)+j,0x00,&Separator); else MenuAddItem(((i*1000)+1000)+j-1,((i*1000)+1000)+j,0x00,&Separator); } else { if(j==0) MenuAddItem(9000+i,((i*1000)+1000)+j,0x00,ItemLabel); else MenuAddItem(((i*1000)+1000)+j-1,((i*1000)+1000)+j,0x00,ItemLabel); } } // Hide the dummy menu item, since we don't need it anymore. MenuHideItem(9000+i); } } void wxMenuBar::Attach(wxFrame *frame) { // before attaching preprocess menus to not include wxID_EXIT item // as PalmOS guidelines suggest wxMenuItem *item; wxMenu *menu; int i; while( item = FindItem(wxID_EXIT) ) { menu = item->GetMenu(); if( !menu ) break; // something broken ? size_t count = menu->GetMenuItemCount(); if( count == 0 ) break; // something broken ? // if EXIT is last item in menu if( menu->FindItemByPosition( count - 1 ) == item ) { menu->Destroy( item ); // was more than one item? // was previous separator ? if( count > 2 ) { item = menu->FindItemByPosition( count - 2 ); if(item && item->IsSeparator()) menu->Destroy( item ); } } // if EXIT is first item in menu else if( menu->FindItemByPosition( 0 ) == item ) { menu->Destroy( item ); // was more than one item? // was previous separator ? if( count > 2 ) { item = menu->FindItemByPosition( 0 ); if(item && item->IsSeparator()) menu->Destroy( item ); } } // if EXIT is in the middle but before and after are selectors else { i = 1; // 0 case already done while ( (i < count) && (menu->FindItemByPosition( 0 ) != item) ) { i++; } if (i >= count) break; if (menu->FindItemByPosition( i ) != item) break; menu->Destroy( item ); item = menu->FindItemByPosition( i ); if ( item && item->IsSeparator() && menu->FindItemByPosition( i-1 )->IsSeparator() ) { // noe need for two neighbouring separators menu->Destroy( item ); } } } // check if we received any empty menu! i = 0; while(i < GetMenuCount()) { menu = GetMenu(i); if( menu && (menu->GetMenuItemCount()==0) ) { menu = Remove( i ); delete menu; } else i++; } wxMenuBarBase::Attach(frame); LoadMenu(); } void wxMenuBar::Detach() { wxMenuBarBase::Detach(); } #endif // wxUSE_MENUS