]> git.saurik.com Git - wxWidgets.git/blob - src/palmos/menu.cpp
Consistent wxWindow::Enable with wxRadioBox::Enable for control and its items. wxRadi...
[wxWidgets.git] / src / palmos / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/palmos/menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: William Osborne - minimal working wxPalmOS port
5 // Modified by:
6 // Created: 10/12/04
7 // RCS-ID: $Id$
8 // Copyright: (c) William Osborne
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "menu.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_MENUS
32
33 #ifndef WX_PRECOMP
34 #include "wx/frame.h"
35 #include "wx/menu.h"
36 #include "wx/utils.h"
37 #include "wx/intl.h"
38 #include "wx/log.h"
39 #endif
40
41 #if wxUSE_OWNER_DRAWN
42 #include "wx/ownerdrw.h"
43 #endif
44
45 // other standard headers
46 #include <string.h>
47
48 #include <Menu.h>
49
50 // ----------------------------------------------------------------------------
51 // global variables
52 // ----------------------------------------------------------------------------
53
54 extern wxMenu *wxCurrentPopupMenu;
55
56 // ----------------------------------------------------------------------------
57 // constants
58 // ----------------------------------------------------------------------------
59
60 // the (popup) menu title has this special id
61 static const int idMenuTitle = -3;
62
63 // ----------------------------------------------------------------------------
64 // private functions
65 // ----------------------------------------------------------------------------
66
67 // ============================================================================
68 // implementation
69 // ============================================================================
70
71 #include <wx/listimpl.cpp>
72
73 WX_DEFINE_LIST( wxMenuInfoList ) ;
74
75 #if wxUSE_EXTENDED_RTTI
76
77 WX_DEFINE_FLAGS( wxMenuStyle )
78
79 wxBEGIN_FLAGS( wxMenuStyle )
80 wxFLAGS_MEMBER(wxMENU_TEAROFF)
81 wxEND_FLAGS( wxMenuStyle )
82
83 IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenu, wxEvtHandler,"wx/menu.h")
84
85 wxCOLLECTION_TYPE_INFO( wxMenuItem * , wxMenuItemList ) ;
86
87 template<> void wxCollectionToVariantArray( wxMenuItemList const &theList, wxxVariantArray &value)
88 {
89 wxListCollectionToVariantArray<wxMenuItemList::compatibility_iterator>( theList , value ) ;
90 }
91
92 wxBEGIN_PROPERTIES_TABLE(wxMenu)
93 wxEVENT_PROPERTY( Select , wxEVT_COMMAND_MENU_SELECTED , wxCommandEvent)
94 wxPROPERTY( Title, wxString , SetTitle, GetTitle, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
95 wxREADONLY_PROPERTY_FLAGS( MenuStyle , wxMenuStyle , long , GetStyle , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
96 wxPROPERTY_COLLECTION( MenuItems , wxMenuItemList , wxMenuItem* , Append , GetMenuItems , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
97 wxEND_PROPERTIES_TABLE()
98
99 wxBEGIN_HANDLERS_TABLE(wxMenu)
100 wxEND_HANDLERS_TABLE()
101
102 wxDIRECT_CONSTRUCTOR_2( wxMenu , wxString , Title , long , MenuStyle )
103
104 WX_DEFINE_FLAGS( wxMenuBarStyle )
105
106 wxBEGIN_FLAGS( wxMenuBarStyle )
107 wxFLAGS_MEMBER(wxMB_DOCKABLE)
108 wxEND_FLAGS( wxMenuBarStyle )
109
110 // the negative id would lead the window (its superclass !) to vetoe streaming out otherwise
111 bool wxMenuBarStreamingCallback( const wxObject *WXUNUSED(object), wxWriter * , wxPersister * , wxxVariantArray & )
112 {
113 return true ;
114 }
115
116 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuBar, wxWindow ,"wx/menu.h",wxMenuBarStreamingCallback)
117
118 IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenuInfo, wxObject , "wx/menu.h" )
119
120 wxBEGIN_PROPERTIES_TABLE(wxMenuInfo)
121 wxREADONLY_PROPERTY( Menu , wxMenu* , GetMenu , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
122 wxREADONLY_PROPERTY( Title , wxString , GetTitle , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
123 wxEND_PROPERTIES_TABLE()
124
125 wxBEGIN_HANDLERS_TABLE(wxMenuInfo)
126 wxEND_HANDLERS_TABLE()
127
128 wxCONSTRUCTOR_2( wxMenuInfo , wxMenu* , Menu , wxString , Title )
129
130 wxCOLLECTION_TYPE_INFO( wxMenuInfo * , wxMenuInfoList ) ;
131
132 template<> void wxCollectionToVariantArray( wxMenuInfoList const &theList, wxxVariantArray &value)
133 {
134 wxListCollectionToVariantArray<wxMenuInfoList::compatibility_iterator>( theList , value ) ;
135 }
136
137 wxBEGIN_PROPERTIES_TABLE(wxMenuBar)
138 wxPROPERTY_COLLECTION( MenuInfos , wxMenuInfoList , wxMenuInfo* , Append , GetMenuInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
139 wxEND_PROPERTIES_TABLE()
140
141 wxBEGIN_HANDLERS_TABLE(wxMenuBar)
142 wxEND_HANDLERS_TABLE()
143
144 wxCONSTRUCTOR_DUMMY( wxMenuBar )
145
146 #else
147 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
148 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
149 IMPLEMENT_DYNAMIC_CLASS(wxMenuInfo, wxObject)
150 #endif
151
152 const wxMenuInfoList& wxMenuBar::GetMenuInfos() const
153 {
154 wxMenuInfoList* list = const_cast< wxMenuInfoList* >( &m_menuInfos ) ;
155 WX_CLEAR_LIST( wxMenuInfoList , *list ) ;
156 for( size_t i = 0 ; i < GetMenuCount() ; ++i )
157 {
158 wxMenuInfo* info = new wxMenuInfo() ;
159 info->Create( const_cast<wxMenuBar*>(this)->GetMenu(i) , GetLabelTop(i) ) ;
160 list->Append( info ) ;
161 }
162 return m_menuInfos ;
163 }
164
165 // ---------------------------------------------------------------------------
166 // wxMenu construction, adding and removing menu items
167 // ---------------------------------------------------------------------------
168
169 // Construct a menu with optional title (then use append)
170 void wxMenu::Init()
171 {
172 }
173
174 // The wxWindow destructor will take care of deleting the submenus.
175 wxMenu::~wxMenu()
176 {
177 }
178
179 void wxMenu::Break()
180 {
181 }
182
183 void wxMenu::Attach(wxMenuBarBase *menubar)
184 {
185 wxMenuBase::Attach(menubar);
186 }
187
188 #if wxUSE_ACCEL
189
190 int wxMenu::FindAccel(int id) const
191 {
192 return wxNOT_FOUND;
193 }
194
195 void wxMenu::UpdateAccel(wxMenuItem *item)
196 {
197 }
198
199 #endif // wxUSE_ACCEL
200
201 // append a new item or submenu to the menu
202 bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
203 {
204 if ( IsAttached() && GetMenuBar()->IsAttached() )
205 {
206 // Regenerate the menu resource
207 GetMenuBar()->Refresh();
208 }
209
210 return true;
211 }
212
213 void wxMenu::EndRadioGroup()
214 {
215 }
216
217 wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
218 {
219 wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") );
220
221 if(!wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item))
222 {
223 return NULL;
224 }
225 else if(IsAttached() && GetMenuBar()->IsAttached())
226 {
227 // Regenerate the menu resource
228 GetMenuBar()->Refresh();
229 }
230
231 return item;
232 }
233
234 wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
235 {
236 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
237 return item;
238 else
239 return NULL;
240 }
241
242 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
243 {
244 // we need to find the items position in the child list
245 size_t pos;
246 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
247 for ( pos = 0; node; pos++ )
248 {
249 if ( node->GetData() == item )
250 break;
251
252 node = node->GetNext();
253 }
254
255 // DoRemove() (unlike Remove) can only be called for existing item!
256 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
257
258 // remove the item from the menu
259 wxMenuItem *ret=wxMenuBase::DoRemove(item);
260
261 if ( IsAttached() && GetMenuBar()->IsAttached() )
262 {
263 // Regenerate the menu resource
264 GetMenuBar()->Refresh();
265 }
266
267 return ret;
268 }
269
270 // ---------------------------------------------------------------------------
271 // accelerator helpers
272 // ---------------------------------------------------------------------------
273
274 #if wxUSE_ACCEL
275
276 // create the wxAcceleratorEntries for our accels and put them into provided
277 // array - return the number of accels we have
278 size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
279 {
280 size_t count = GetAccelCount();
281 for ( size_t n = 0; n < count; n++ )
282 {
283 *accels++ = *m_accels[n];
284 }
285
286 return count;
287 }
288
289 #endif // wxUSE_ACCEL
290
291 // ---------------------------------------------------------------------------
292 // set wxMenu title
293 // ---------------------------------------------------------------------------
294
295 void wxMenu::SetTitle(const wxString& label)
296 {
297 m_title = label;
298
299 if ( IsAttached() && GetMenuBar()->IsAttached() )
300 {
301 // Regenerate the menu resource
302 GetMenuBar()->Refresh();
303 }
304 }
305
306 // ---------------------------------------------------------------------------
307 // event processing
308 // ---------------------------------------------------------------------------
309
310 bool wxMenu::PalmCommand(WXUINT WXUNUSED(param), WXWORD id)
311 {
312 return false;
313 }
314
315 // ---------------------------------------------------------------------------
316 // other
317 // ---------------------------------------------------------------------------
318
319 wxWindow *wxMenu::GetWindow() const
320 {
321 return NULL;
322 }
323
324 // ---------------------------------------------------------------------------
325 // Menu Bar
326 // ---------------------------------------------------------------------------
327
328 void wxMenuBar::Init()
329 {
330 }
331
332 wxMenuBar::wxMenuBar()
333 {
334 }
335
336 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
337 {
338 }
339
340 wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
341 {
342 }
343
344 wxMenuBar::~wxMenuBar()
345 {
346 }
347
348 // ---------------------------------------------------------------------------
349 // wxMenuBar helpers
350 // ---------------------------------------------------------------------------
351
352 void wxMenuBar::Refresh()
353 {
354 wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") );
355
356 // Regenerate the menu resource
357 LoadMenu();
358 }
359
360 WXHMENU wxMenuBar::Create()
361 {
362 return NULL;
363 }
364
365 int wxMenuBar::PalmPositionForWxMenu(wxMenu *menu, int wxpos)
366 {
367 return -1;
368 }
369
370 // ---------------------------------------------------------------------------
371 // wxMenuBar functions to work with the top level submenus
372 // ---------------------------------------------------------------------------
373
374 void wxMenuBar::EnableTop(size_t pos, bool enable)
375 {
376 // Palm OS does not have support for grayed or disabled items
377 }
378
379 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
380 {
381 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
382
383 m_titles[pos]=wxStripMenuCodes(label);
384
385 if ( !IsAttached() )
386 {
387 return;
388 }
389
390 // Regenerate the menu resource
391 Refresh();
392 }
393
394 wxString wxMenuBar::GetLabelTop(size_t pos) const
395 {
396 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
397 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
398
399 return wxMenuItem::GetLabelFromText(m_titles[pos]);
400 }
401
402 // ---------------------------------------------------------------------------
403 // wxMenuBar construction
404 // ---------------------------------------------------------------------------
405
406 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
407 {
408 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
409 if ( !menuOld )
410 return NULL;
411
412 m_titles[pos]=wxStripMenuCodes(title);
413
414 if ( IsAttached() )
415 {
416 // Regenerate the menu resource
417 Refresh();
418 }
419
420 return menuOld;
421 }
422
423 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
424 {
425 if ( !wxMenuBarBase::Insert(pos, menu, title) )
426 return false;
427
428 m_titles.Insert(wxStripMenuCodes(title), pos);
429
430 if ( IsAttached() )
431 {
432 // Regenerate the menu resource
433 Refresh();
434 }
435
436 return true;
437 }
438
439 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
440 {
441 if ( !wxMenuBarBase::Append(menu, title) )
442 return false;
443
444 m_titles.Add(wxStripMenuCodes(title));
445
446 if(IsAttached())
447 {
448 // Regenerate the menu resource
449 Refresh();
450 }
451
452 return true;
453 }
454
455 wxMenu *wxMenuBar::Remove(size_t pos)
456 {
457 wxMenu *menu = wxMenuBarBase::Remove(pos);
458 if ( !menu )
459 return NULL;
460
461 m_titles.RemoveAt(pos);
462
463 if (IsAttached())
464 {
465 // Regenerate the menu resource
466 Refresh();
467 }
468
469 return menu;
470 }
471
472 #if wxUSE_ACCEL
473
474 void wxMenuBar::RebuildAccelTable()
475 {
476 }
477
478 #endif // wxUSE_ACCEL
479
480 int wxMenuBar::ProcessCommand(int ItemID)
481 {
482 if(!IsAttached())
483 return -1;
484
485 int MenuNum=(ItemID/1000)-1;
486 int ItemNum=(ItemID-(1000*(MenuNum+1)));
487
488 // Should never happen, but it doesn't hurt to check anyway.
489 if(MenuNum>GetMenuCount())
490 return -1;
491
492 // Get the menu
493 wxMenu *ActiveMenu=GetMenu(MenuNum);
494
495 // Make sure this is a valid item.
496 if(ItemNum>ActiveMenu->GetMenuItemCount())
497 return -1;
498
499 // Get the item
500 wxMenuItem *ActiveItem=ActiveMenu->FindItemByPosition(ItemNum);
501 int ActiveID=ActiveItem->GetId();
502
503 return ActiveID;
504 }
505
506 /* Palm OS does not have good dynamic menu support. About all you can do with
507 * the standard API calls is to add new items to an existing drop-down menu and
508 * hide/show items in a drop-down menu. It is impossible to add, hide, or
509 * change the label on a drop-down menu.
510 *
511 * The easiest and simplest way around this limitation is to modify the Palm OS
512 * MenuBarType structure directly. This gives limited ability to change the
513 * label on a drop-down menu. I have not been able to find a safe way to add,
514 * delete, or resize drop-down menus in OS 6.
515 *
516 * The following routine attempt to work around these limitations present in the
517 * Palm OS API to provide limited dynamic menu support. This solution is far
518 * from perfect, but the only other option is to wait for PalmSource to add full
519 * dynamic menu support, or to recreate the Palm OS menu system from scratch.
520 *
521 * This system is limited in that no more than 4 drop-down menus are allowed per
522 * menu bar, and the label for each drop-down menu is limited to 8 characters of
523 * text. However, this menu system should work for most applications.
524 *
525 * Basically the menu routines select one of four menu bars, depending on
526 * whether or not the requested menu bar has one, two, three, or four drop-down
527 * menus.
528 *
529 * These four "template" menu bars contain one, two, three, or four drop-down
530 * menus. Each menu has a dummy menu item attached to it to allow the Palm OS
531 * MenuAddItem function to add the real items.
532 *
533 * The labels on the drop-down menus are then replaced with the labels of the
534 * real menus.
535 *
536 * The menu is then attached to the active window and the MenuAddItem API
537 * function is called to add the items to each drop-down menu. Finally,
538 * MenuHideItem is called to remove the dummy items from each drop-down menu.
539 */
540 void wxMenuBar::LoadMenu()
541 {
542 int i=0;
543 int j=0;
544
545 // Handle to the currently running application database
546 DmOpenRef AppDB;
547
548 // Get app database reference - needed for some Palm OS Menu API calls.
549 SysGetModuleDatabase(SysGetRefNum(), NULL, &AppDB);
550
551 // Get the number of menus
552 int NumMenus=GetMenuCount();
553
554 // Set up the pointers and handles
555 char *PalmOSMenuBarPtr;
556 MemHandle PalmOSMenuBar;
557
558 // Load the menu template and set up the menu pointers
559 if(NumMenus==1)
560 {
561 PalmOSMenuBar=DmGetResource(AppDB,'MBAR',1000);
562 PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar);
563
564 PalmOSMenuBarPtr+=74;
565 }
566 else if(NumMenus==2)
567 {
568 PalmOSMenuBar=DmGetResource(AppDB,'MBAR',2000);
569 PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar);
570
571 PalmOSMenuBarPtr+=116;
572 }
573 else if(NumMenus==3)
574 {
575 PalmOSMenuBar=DmGetResource(AppDB,'MBAR',3000);
576 PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar);
577
578 PalmOSMenuBarPtr+=158;
579 }
580 else
581 {
582 // We support a maximum of 4 menus, so make sure that do not create
583 // more than we can handle.
584 NumMenus=4;
585
586 PalmOSMenuBar=DmGetResource(AppDB,'MBAR',4000);
587 PalmOSMenuBarPtr=(char *)MemHandleLock(PalmOSMenuBar);
588
589 PalmOSMenuBarPtr+=200;
590 }
591
592 // Set the proper names for the drop-down triggers.
593 for(i=0;i<NumMenus;i++)
594 {
595 // Clear out the old label
596 char buffer[8]={' ',' ',' ',' ',' ',' ',' ',' '};
597 MemMove(PalmOSMenuBarPtr,buffer,8);
598
599 wxString MenuTitle=m_titles.Item(i);
600
601 // Make sure we don't copy more than 8 bytes for the label
602 int LengthToCopy=MenuTitle.length();
603 if(LengthToCopy>8)
604 LengthToCopy=8;
605
606 MemMove(PalmOSMenuBarPtr,MenuTitle,LengthToCopy);
607 PalmOSMenuBarPtr+=11;
608 }
609
610 // We are done with the menu pointer.
611 MemHandleUnlock(PalmOSMenuBar);
612 DmReleaseResource(PalmOSMenuBar);
613
614 // We must make the menu active before we can add items to the drop-down
615 // triggers.
616 FrmSetMenu(FrmGetActiveForm(),AppDB,NumMenus*1000);
617
618 /* Add the menu items to the drop-down triggers. This must be done after
619 * setting the triggers, because setting the names of drop-down triggers
620 * that have a variable number of items requires carefull calculation of
621 * the offsets in the MenuBarType structure. Setting the triggers first
622 * avoids this.
623 */
624 for(i=0;i<NumMenus;i++)
625 {
626 wxMenu *CurrentMenu=GetMenu(i);
627
628 for(j=0;j<CurrentMenu->GetMenuItemCount();j++)
629 {
630 wxMenuItem *CurrentItem=CurrentMenu->FindItemByPosition(j);
631 wxString ItemLabel=CurrentItem->GetLabel();
632
633 if(CurrentItem->IsSeparator()==true)
634 {
635 char Separator=MenuSeparatorChar;
636 if(j==0)
637 MenuAddItem(9000+i,((i*1000)+1000)+j,0x00,&Separator);
638 else
639 MenuAddItem(((i*1000)+1000)+j-1,((i*1000)+1000)+j,0x00,&Separator);
640 }
641 else
642 {
643 if(j==0)
644 MenuAddItem(9000+i,((i*1000)+1000)+j,0x00,ItemLabel);
645 else
646 MenuAddItem(((i*1000)+1000)+j-1,((i*1000)+1000)+j,0x00,ItemLabel);
647 }
648 }
649
650 // Hide the dummy menu item, since we don't need it anymore.
651 MenuHideItem(9000+i);
652 }
653 }
654
655 void wxMenuBar::Attach(wxFrame *frame)
656 {
657 wxMenuBarBase::Attach(frame);
658
659 LoadMenu();
660 }
661
662 void wxMenuBar::Detach()
663 {
664 wxMenuBarBase::Detach();
665 }
666
667 #endif // wxUSE_MENUS