]> git.saurik.com Git - wxWidgets.git/blame - src/palmos/menu.cpp
first stab at supporting custom renderers
[wxWidgets.git] / src / palmos / menu.cpp
CommitLineData
ffecfa5a 1/////////////////////////////////////////////////////////////////////////////
e2731512 2// Name: src/palmos/menu.cpp
ffecfa5a 3// Purpose: wxMenu, wxMenuBar, wxMenuItem
e2731512 4// Author: William Osborne - minimal working wxPalmOS port
ffecfa5a
JS
5// Modified by:
6// Created: 10/12/04
e2731512 7// RCS-ID: $Id$
ffecfa5a
JS
8// Copyright: (c) William Osborne
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
ffecfa5a
JS
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
3b3dc801
WS
29#include "wx/menu.h"
30
ffecfa5a
JS
31#ifndef WX_PRECOMP
32 #include "wx/frame.h"
ffecfa5a
JS
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
6afc1b46 42#ifdef __WXPALMOS6__
20bc5ad8 43#include <Loader.h>
6afc1b46
VZ
44#else // __WXPALMOS5__
45#include <UIResources.h> // MenuRscType
46#endif
47
20bc5ad8
WS
48#include <Form.h>
49#include <Menu.h>
50
ffecfa5a
JS
51// ----------------------------------------------------------------------------
52// global variables
53// ----------------------------------------------------------------------------
54
55extern wxMenu *wxCurrentPopupMenu;
56
57// ----------------------------------------------------------------------------
58// constants
59// ----------------------------------------------------------------------------
60
61// the (popup) menu title has this special id
62static const int idMenuTitle = -3;
63
64// ----------------------------------------------------------------------------
65// private functions
66// ----------------------------------------------------------------------------
67
68// ============================================================================
69// implementation
70// ============================================================================
71
ffecfa5a
JS
72// ---------------------------------------------------------------------------
73// wxMenu construction, adding and removing menu items
74// ---------------------------------------------------------------------------
75
76// Construct a menu with optional title (then use append)
77void wxMenu::Init()
78{
79}
80
81// The wxWindow destructor will take care of deleting the submenus.
82wxMenu::~wxMenu()
83{
84}
85
86void wxMenu::Break()
87{
88}
89
90void wxMenu::Attach(wxMenuBarBase *menubar)
91{
92 wxMenuBase::Attach(menubar);
93}
94
95#if wxUSE_ACCEL
96
97int wxMenu::FindAccel(int id) const
98{
99 return wxNOT_FOUND;
100}
101
102void wxMenu::UpdateAccel(wxMenuItem *item)
103{
104}
105
106#endif // wxUSE_ACCEL
107
108// append a new item or submenu to the menu
109bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
110{
111 if ( IsAttached() && GetMenuBar()->IsAttached() )
112 {
4055ed82 113 // Regenerate the menu resource
ffecfa5a
JS
114 GetMenuBar()->Refresh();
115 }
4055ed82
WS
116
117 return true;
ffecfa5a
JS
118}
119
120void wxMenu::EndRadioGroup()
121{
122}
123
124wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
125{
9a83f860 126 wxCHECK_MSG( item, NULL, wxT("NULL item in wxMenu::DoAppend") );
ffecfa5a
JS
127
128 if(!wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item))
129 {
130 return NULL;
131 }
132 else if(IsAttached() && GetMenuBar()->IsAttached())
133 {
4055ed82 134 // Regenerate the menu resource
ffecfa5a
JS
135 GetMenuBar()->Refresh();
136 }
4055ed82
WS
137
138 return item;
ffecfa5a
JS
139}
140
141wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
142{
143 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos))
144 return item;
145 else
4055ed82 146 return NULL;
ffecfa5a
JS
147}
148
149wxMenuItem *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);
4055ed82 167
ffecfa5a
JS
168 if ( IsAttached() && GetMenuBar()->IsAttached() )
169 {
4055ed82 170 // Regenerate the menu resource
ffecfa5a
JS
171 GetMenuBar()->Refresh();
172 }
4055ed82 173
ffecfa5a
JS
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
185size_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
202void wxMenu::SetTitle(const wxString& label)
203{
204 m_title = label;
205
206 if ( IsAttached() && GetMenuBar()->IsAttached() )
207 {
4055ed82 208 // Regenerate the menu resource
ffecfa5a
JS
209 GetMenuBar()->Refresh();
210 }
211}
212
213// ---------------------------------------------------------------------------
214// event processing
215// ---------------------------------------------------------------------------
216
217bool wxMenu::PalmCommand(WXUINT WXUNUSED(param), WXWORD id)
218{
219 return false;
220}
221
222// ---------------------------------------------------------------------------
223// other
224// ---------------------------------------------------------------------------
225
226wxWindow *wxMenu::GetWindow() const
227{
228 return NULL;
229}
230
231// ---------------------------------------------------------------------------
232// Menu Bar
233// ---------------------------------------------------------------------------
234
235void wxMenuBar::Init()
236{
237}
238
239wxMenuBar::wxMenuBar()
240{
241}
242
243wxMenuBar::wxMenuBar( long WXUNUSED(style) )
244{
245}
246
294ea16d 247wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
ffecfa5a
JS
248{
249}
250
251wxMenuBar::~wxMenuBar()
252{
253}
254
255// ---------------------------------------------------------------------------
256// wxMenuBar helpers
257// ---------------------------------------------------------------------------
258
259void wxMenuBar::Refresh()
260{
261 wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") );
262
263 // Regenerate the menu resource
264 LoadMenu();
265}
266
267WXHMENU wxMenuBar::Create()
268{
269 return NULL;
270}
271
272int 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
281void wxMenuBar::EnableTop(size_t pos, bool enable)
282{
283 // Palm OS does not have support for grayed or disabled items
284}
285
52af3158 286void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
ffecfa5a
JS
287{
288 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
289
52af3158 290 m_titles[pos] = label;
ffecfa5a
JS
291
292 if ( !IsAttached() )
293 {
294 return;
295 }
296
297 // Regenerate the menu resource
298 Refresh();
299}
300
52af3158 301wxString wxMenuBar::GetMenuLabel(size_t pos) const
ffecfa5a
JS
302{
303 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
52af3158 304 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
ffecfa5a 305
52af3158 306 return m_titles[pos];
ffecfa5a
JS
307}
308
309// ---------------------------------------------------------------------------
310// wxMenuBar construction
311// ---------------------------------------------------------------------------
312
313wxMenu *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
52af3158 319 m_titles[pos] = title;
ffecfa5a
JS
320
321 if ( IsAttached() )
322 {
323 // Regenerate the menu resource
324 Refresh();
325 }
326
327 return menuOld;
328}
329
330bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
331{
332 if ( !wxMenuBarBase::Insert(pos, menu, title) )
4055ed82 333 return false;
ffecfa5a 334
52af3158 335 m_titles.Insert(title, pos);
ffecfa5a
JS
336
337 if ( IsAttached() )
338 {
339 // Regenerate the menu resource
340 Refresh();
341 }
342
4055ed82 343 return true;
ffecfa5a
JS
344}
345
346bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
347{
348 if ( !wxMenuBarBase::Append(menu, title) )
4055ed82 349 return false;
ffecfa5a 350
52af3158 351 m_titles.Add(title);
ffecfa5a
JS
352
353 if(IsAttached())
354 {
355 // Regenerate the menu resource
356 Refresh();
357 }
358
4055ed82 359 return true;
ffecfa5a
JS
360}
361
362wxMenu *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
381void wxMenuBar::RebuildAccelTable()
382{
383}
384
385#endif // wxUSE_ACCEL
386
387int wxMenuBar::ProcessCommand(int ItemID)
388{
389 if(!IsAttached())
390 return -1;
4055ed82 391
ffecfa5a
JS
392 int MenuNum=(ItemID/1000)-1;
393 int ItemNum=(ItemID-(1000*(MenuNum+1)));
4055ed82 394
ffecfa5a
JS
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);
4055ed82 401
ffecfa5a
JS
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
4055ed82
WS
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,
ffecfa5a 421 * delete, or resize drop-down menus in OS 6.
4055ed82 422 *
ffecfa5a 423 * The following routine attempt to work around these limitations present in the
4055ed82 424 * Palm OS API to provide limited dynamic menu support. This solution is far
ffecfa5a
JS
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.
4055ed82 427 *
ffecfa5a
JS
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.
4055ed82
WS
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
ffecfa5a 434 * menus.
4055ed82
WS
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
ffecfa5a 438 * MenuAddItem function to add the real items.
4055ed82
WS
439 *
440 * The labels on the drop-down menus are then replaced with the labels of the
ffecfa5a 441 * real menus.
4055ed82
WS
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,
ffecfa5a
JS
445 * MenuHideItem is called to remove the dummy items from each drop-down menu.
446 */
447void wxMenuBar::LoadMenu()
448{
449 int i=0;
450 int j=0;
6afc1b46 451#ifdef __WXPALMOS6__
ffecfa5a
JS
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);
6afc1b46 457#endif // __WXPALMOS6__
ffecfa5a
JS
458
459 // Get the number of menus
460 int NumMenus=GetMenuCount();
461
462 // Set up the pointers and handles
4055ed82 463 char *PalmOSMenuBarPtr;
ffecfa5a 464 MemHandle PalmOSMenuBar;
4055ed82 465
ffecfa5a
JS
466 // Load the menu template and set up the menu pointers
467 if(NumMenus==1)
468 {
6afc1b46
VZ
469 PalmOSMenuBar = POS_DmGetResource (AppDB, MenuRscType, 1000);
470 PalmOSMenuBarPtr = (char *)MemHandleLock (PalmOSMenuBar);
ffecfa5a 471
6afc1b46 472 PalmOSMenuBarPtr += 74;
ffecfa5a
JS
473 }
474 else if(NumMenus==2)
475 {
6afc1b46
VZ
476 PalmOSMenuBar = POS_DmGetResource (AppDB, MenuRscType, 2000);
477 PalmOSMenuBarPtr = (char *)MemHandleLock (PalmOSMenuBar);
ffecfa5a 478
6afc1b46 479 PalmOSMenuBarPtr += 116;
ffecfa5a
JS
480 }
481 else if(NumMenus==3)
482 {
6afc1b46
VZ
483 PalmOSMenuBar = POS_DmGetResource (AppDB, MenuRscType, 3000);
484 PalmOSMenuBarPtr = (char *)MemHandleLock (PalmOSMenuBar);
ffecfa5a 485
6afc1b46 486 PalmOSMenuBarPtr += 158;
ffecfa5a
JS
487 }
488 else
489 {
4055ed82 490 // We support a maximum of 4 menus, so make sure that do not create
ffecfa5a
JS
491 // more than we can handle.
492 NumMenus=4;
493
6afc1b46
VZ
494 PalmOSMenuBar = POS_DmGetResource (AppDB, MenuRscType, 4000);
495 PalmOSMenuBarPtr = (char *)MemHandleLock (PalmOSMenuBar);
ffecfa5a 496
6afc1b46 497 PalmOSMenuBarPtr += 200;
ffecfa5a 498 }
4055ed82 499
ffecfa5a
JS
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);
4055ed82 506
ffecfa5a
JS
507 wxString MenuTitle=m_titles.Item(i);
508
509 // Make sure we don't copy more than 8 bytes for the label
6afc1b46
VZ
510 int LengthToCopy = MenuTitle.length();
511 if(LengthToCopy > 8)
512 LengthToCopy = 8;
ffecfa5a 513
6afc1b46 514 MemMove(PalmOSMenuBarPtr,(char*)(&MenuTitle),LengthToCopy);
ffecfa5a
JS
515 PalmOSMenuBarPtr+=11;
516 }
517
518 // We are done with the menu pointer.
4055ed82 519 MemHandleUnlock(PalmOSMenuBar);
ffecfa5a
JS
520 DmReleaseResource(PalmOSMenuBar);
521
4055ed82 522 // We must make the menu active before we can add items to the drop-down
ffecfa5a 523 // triggers.
6afc1b46 524 POS_FrmSetMenu (FrmGetActiveForm(), AppDB, NumMenus * 1000);
ffecfa5a 525
4055ed82
WS
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
ffecfa5a
JS
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);
4055ed82 535
ffecfa5a
JS
536 for(j=0;j<CurrentMenu->GetMenuItemCount();j++)
537 {
538 wxMenuItem *CurrentItem=CurrentMenu->FindItemByPosition(j);
539 wxString ItemLabel=CurrentItem->GetLabel();
4055ed82 540
ffecfa5a
JS
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)
6afc1b46 552 MenuAddItem(9000+i,((i*1000)+1000)+j,0x00,(char *)(&ItemLabel));
ffecfa5a 553 else
6afc1b46 554 MenuAddItem(((i*1000)+1000)+j-1,((i*1000)+1000)+j,0x00,(char *)(&ItemLabel));
ffecfa5a
JS
555 }
556 }
4055ed82 557
ffecfa5a
JS
558 // Hide the dummy menu item, since we don't need it anymore.
559 MenuHideItem(9000+i);
560 }
561}
562
563void wxMenuBar::Attach(wxFrame *frame)
564{
17131ffd
WS
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
ffecfa5a 648 wxMenuBarBase::Attach(frame);
ffecfa5a 649
4055ed82 650 LoadMenu();
ffecfa5a 651}
ffecfa5a
JS
652
653void wxMenuBar::Detach()
654{
655 wxMenuBarBase::Detach();
656}
657
658#endif // wxUSE_MENUS