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