]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: menu.cpp | |
3 | // Purpose: wxMenu, wxMenuBar, wxMenuItem | |
4 | // Author: AUTHOR | |
5 | // Modified by: | |
6 | // Created: ??/??/98 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) AUTHOR | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | ||
13 | // ============================================================================ | |
14 | // headers & declarations | |
15 | // ============================================================================ | |
16 | ||
17 | // wxWindows headers | |
18 | // ----------------- | |
19 | ||
20 | #ifdef __GNUG__ | |
21 | #pragma implementation "menu.h" | |
22 | #pragma implementation "menuitem.h" | |
23 | #endif | |
24 | ||
25 | #include "wx/menu.h" | |
26 | #include "wx/menuitem.h" | |
27 | #include "wx/log.h" | |
28 | #include "wx/utils.h" | |
29 | ||
30 | #include "wx/mac/uma.h" | |
31 | ||
32 | // other standard headers | |
33 | // ---------------------- | |
34 | #include <string.h> | |
35 | ||
36 | IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) | |
37 | IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler) | |
38 | ||
39 | // the (popup) menu title has this special id | |
40 | static const int idMenuTitle = -2; | |
41 | static int formerHelpMenuItems = 0 ; | |
42 | ||
43 | const short kwxMacMenuBarResource = 1 ; | |
44 | const short kwxMacAppleMenuId = 1 ; | |
45 | ||
46 | // ============================================================================ | |
47 | // implementation | |
48 | // ============================================================================ | |
49 | ||
50 | // | |
51 | // Helper Functions to get Mac Menus the way they should be ;-) | |
52 | // | |
53 | ||
54 | void wxMacCtoPString(const char* theCString, Str255 thePString); | |
55 | ||
56 | // remove inappropriate characters, if useShortcuts is false, the ampersand will not auto-generate a mac menu-shortcut | |
57 | ||
58 | void wxMacBuildMenuString(StringPtr outMacItemText, char *outMacShortcutChar , short *outMacModifiers , const char *inItemText , bool useShortcuts ) | |
59 | { | |
60 | char *p = (char *) &outMacItemText[1] ; | |
61 | short macModifiers = 0 ; | |
62 | char macShortCut = 0 ; | |
63 | const char *inItemName ; | |
64 | wxString inItemTextMac ; | |
65 | ||
66 | if (wxApp::s_macDefaultEncodingIsPC) | |
67 | { | |
68 | inItemTextMac = wxMacMakeMacStringFromPC( inItemText ) ; | |
69 | inItemName = inItemTextMac ; | |
70 | } | |
71 | else | |
72 | { | |
73 | inItemName = inItemText ; | |
74 | } | |
75 | ||
76 | if ( useShortcuts && !wxApp::s_macSupportPCMenuShortcuts ) | |
77 | useShortcuts = false ; | |
78 | ||
79 | // we have problems with a leading hypen - it will be taken as a separator | |
80 | ||
81 | while ( *inItemName == '-' ) | |
82 | inItemName++ ; | |
83 | ||
84 | while( *inItemName ) | |
85 | { | |
86 | switch ( *inItemName ) | |
87 | { | |
88 | // special characters for macintosh menus -> use some replacement | |
89 | case ';' : | |
90 | *p++ = ',' ; | |
91 | break ; | |
92 | case '^' : | |
93 | *p++ = ' ' ; | |
94 | break ; | |
95 | case '!' : | |
96 | *p++ = ' ' ; | |
97 | break ; | |
98 | case '<' : | |
99 | *p++ = ' ' ; | |
100 | break ; | |
101 | case '/' : | |
102 | *p++ = '|' ; | |
103 | break ; | |
104 | case '(' : | |
105 | *p++ = '[' ; | |
106 | break ; | |
107 | case ')' : | |
108 | *p++ = ']' ; | |
109 | break ; | |
110 | // shortcuts | |
111 | case '&' : | |
112 | { | |
113 | ++inItemName ; | |
114 | if ( *inItemName ) | |
115 | { | |
116 | *p++ = *inItemName ; | |
117 | if ( useShortcuts ) | |
118 | macShortCut = *inItemName ; | |
119 | } | |
120 | else | |
121 | --inItemName ; | |
122 | } | |
123 | break ; | |
124 | // win-like accelerators | |
125 | case '\t' : | |
126 | { | |
127 | ++inItemName ; | |
128 | while( *inItemName ) | |
129 | { | |
130 | if (strncmp("Ctrl", inItemName, 4) == 0) | |
131 | { | |
132 | inItemName = inItemName + 5; | |
133 | macShortCut = *inItemName; | |
134 | } | |
135 | else if (strncmp("Cntrl", inItemName, 5) == 0) | |
136 | { | |
137 | inItemName = inItemName + 6; | |
138 | macShortCut = *inItemName; | |
139 | } | |
140 | else if (strncmp("Alt", inItemName, 3) == 0) | |
141 | { | |
142 | inItemName = inItemName + 4; | |
143 | macModifiers |= kMenuOptionModifier ; | |
144 | macShortCut = *inItemName ; | |
145 | } | |
146 | else if (strncmp("Shift", inItemName, 5) == 0) | |
147 | { | |
148 | inItemName = inItemName + 6; | |
149 | macModifiers |= kMenuShiftModifier ; | |
150 | macShortCut = *inItemName ; | |
151 | } | |
152 | else if (strncmp("F", inItemName, 1) == 0) | |
153 | { | |
154 | inItemName += strlen( inItemName ) ; | |
155 | // no function keys at the moment | |
156 | // macModifiers |= kMenuShiftModifier ; | |
157 | // macShortCut = *inItemName ; | |
158 | } | |
159 | else | |
160 | { | |
161 | break ; | |
162 | } | |
163 | } | |
164 | ||
165 | if ( *inItemName == 0 ) | |
166 | --inItemName ; | |
167 | ||
168 | } | |
169 | break ; | |
170 | default : | |
171 | *p++ = *inItemName ; | |
172 | } | |
173 | ++inItemName ; | |
174 | } | |
175 | ||
176 | outMacItemText[0] = (p - (char *)outMacItemText) - 1; | |
177 | if ( outMacShortcutChar ) | |
178 | *outMacShortcutChar = macShortCut ; | |
179 | if ( outMacModifiers ) | |
180 | *outMacModifiers = macModifiers ; | |
181 | if ( macShortCut ) | |
182 | { | |
183 | int pos = outMacItemText[0] ; | |
184 | outMacItemText[++pos] = '/'; | |
185 | outMacItemText[++pos] = toupper( macShortCut ); | |
186 | outMacItemText[0] = pos ; | |
187 | } | |
188 | } | |
189 | ||
190 | // Menus | |
191 | ||
192 | // Construct a menu with optional title (then use append) | |
193 | ||
194 | short wxMenu::s_macNextMenuId = 2 ; | |
195 | ||
196 | void wxMenu::Init() | |
197 | { | |
198 | m_doBreak = FALSE; | |
199 | ||
200 | // create the menu | |
201 | Str255 label; | |
202 | wxMacBuildMenuString( label, NULL , NULL , m_title , false ); | |
203 | m_macMenuId = s_macNextMenuId++; | |
204 | wxCHECK_RET( s_macNextMenuId < 236 , "menu ids > 235 cannot be used for submenus on mac" ); | |
205 | m_hMenu = ::NewMenu(m_macMenuId, label); | |
206 | ||
207 | if ( !m_hMenu ) | |
208 | { | |
209 | wxLogLastError("CreatePopupMenu"); | |
210 | } | |
211 | ||
212 | // if we have a title, insert it in the beginning of the menu | |
213 | if ( !!m_title ) | |
214 | { | |
215 | Append(idMenuTitle, m_title) ; | |
216 | AppendSeparator() ; | |
217 | } | |
218 | } | |
219 | ||
220 | wxMenu::~wxMenu() | |
221 | { | |
222 | if (m_hMenu) | |
223 | ::DisposeMenu(m_hMenu); | |
224 | ||
225 | #if wxUSE_ACCEL | |
226 | // delete accels | |
227 | WX_CLEAR_ARRAY(m_accels); | |
228 | #endif // wxUSE_ACCEL | |
229 | } | |
230 | ||
231 | void wxMenu::Break() | |
232 | { | |
233 | // not available on the mac platform | |
234 | } | |
235 | ||
236 | #if wxUSE_ACCEL | |
237 | ||
238 | int wxMenu::FindAccel(int id) const | |
239 | { | |
240 | size_t n, count = m_accels.GetCount(); | |
241 | for ( n = 0; n < count; n++ ) | |
242 | { | |
243 | if ( m_accels[n]->m_command == id ) | |
244 | return n; | |
245 | } | |
246 | ||
247 | return wxNOT_FOUND; | |
248 | } | |
249 | ||
250 | void wxMenu::UpdateAccel(wxMenuItem *item) | |
251 | { | |
252 | // find the (new) accel for this item | |
253 | wxAcceleratorEntry *accel = wxGetAccelFromString(item->GetText()); | |
254 | if ( accel ) | |
255 | accel->m_command = item->GetId(); | |
256 | ||
257 | // find the old one | |
258 | int n = FindAccel(item->GetId()); | |
259 | if ( n == wxNOT_FOUND ) | |
260 | { | |
261 | // no old, add new if any | |
262 | if ( accel ) | |
263 | m_accels.Add(accel); | |
264 | else | |
265 | return; // skipping RebuildAccelTable() below | |
266 | } | |
267 | else | |
268 | { | |
269 | // replace old with new or just remove the old one if no new | |
270 | delete m_accels[n]; | |
271 | if ( accel ) | |
272 | m_accels[n] = accel; | |
273 | else | |
274 | m_accels.Remove(n); | |
275 | } | |
276 | ||
277 | if ( IsAttached() ) | |
278 | { | |
279 | m_menuBar->RebuildAccelTable(); | |
280 | } | |
281 | } | |
282 | ||
283 | #endif // wxUSE_ACCEL | |
284 | ||
285 | // function appends a new item or submenu to the menu | |
286 | // append a new item or submenu to the menu | |
287 | bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) | |
288 | { | |
289 | wxASSERT_MSG( pItem != NULL, "can't append NULL item to the menu" ); | |
290 | #if wxUSE_ACCEL | |
291 | UpdateAccel(pItem); | |
292 | #endif // wxUSE_ACCEL | |
293 | ||
294 | if ( pItem->IsSeparator() ) | |
295 | { | |
296 | if ( pos == (size_t)-1 ) | |
297 | { | |
298 | MacAppendMenu(m_hMenu, "\p-"); | |
299 | } | |
300 | else | |
301 | { | |
302 | MacInsertMenuItem(m_hMenu, "\p-" , pos); | |
303 | } | |
304 | } | |
305 | else | |
306 | { | |
307 | wxMenu *pSubMenu = pItem->GetSubMenu() ; | |
308 | if ( pSubMenu != NULL ) | |
309 | { | |
310 | Str255 label; | |
311 | wxASSERT_MSG( pSubMenu->m_hMenu != NULL , "invalid submenu added"); | |
312 | pSubMenu->m_menuParent = this ; | |
313 | wxMacBuildMenuString( label , NULL , NULL , pItem->GetText() ,false); | |
314 | ||
315 | // hardcoded adding of the submenu combination for mac | |
316 | ||
317 | int theEnd = label[0] + 1; | |
318 | if (theEnd > 251) | |
319 | theEnd = 251; // mac allows only 255 characters | |
320 | label[theEnd++] = '/'; | |
321 | label[theEnd++] = hMenuCmd; | |
322 | label[theEnd++] = '!'; | |
323 | label[theEnd++] = pSubMenu->m_macMenuId; | |
324 | label[theEnd] = 0x00; | |
325 | label[0] = theEnd; | |
326 | ||
327 | if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar) | |
328 | { | |
329 | ::InsertMenu( pSubMenu->m_hMenu , -1 ) ; | |
330 | } | |
331 | ||
332 | if ( pos == (size_t)-1 ) | |
333 | { | |
334 | MacAppendMenu(m_hMenu, label); | |
335 | } | |
336 | else | |
337 | { | |
338 | MacInsertMenuItem(m_hMenu, label , pos); | |
339 | } | |
340 | } | |
341 | else | |
342 | { | |
343 | Str255 label ; | |
344 | wxMacBuildMenuString( label , NULL , NULL , pItem->GetText(), pItem->GetId() == wxApp::s_macAboutMenuItemId); | |
345 | if ( label[0] == 0 ) | |
346 | { | |
347 | // we cannot add empty menus on mac | |
348 | label[0] = 1 ; | |
349 | label[1] = ' ' ; | |
350 | } | |
351 | if ( pos == (size_t)-1 ) | |
352 | { | |
353 | MacAppendMenu(m_hMenu, label); | |
354 | } | |
355 | else | |
356 | { | |
357 | MacInsertMenuItem(m_hMenu, label , pos); | |
358 | } | |
359 | if ( pItem->GetId() == idMenuTitle ) | |
360 | { | |
361 | if ( pos == (size_t)-1 ) | |
362 | { | |
363 | UMADisableMenuItem( m_hMenu , CountMItems( m_hMenu ) ) ; | |
364 | } | |
365 | else | |
366 | { | |
367 | UMADisableMenuItem( m_hMenu , pos + 1 ) ; | |
368 | } | |
369 | } | |
370 | } | |
371 | } | |
372 | // if we're already attached to the menubar, we must update it | |
373 | if ( IsAttached() ) | |
374 | { | |
375 | m_menuBar->Refresh(); | |
376 | } | |
377 | return TRUE ; | |
378 | } | |
379 | ||
380 | bool wxMenu::DoAppend(wxMenuItem *item) | |
381 | { | |
382 | return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item); | |
383 | } | |
384 | ||
385 | bool wxMenu::DoInsert(size_t pos, wxMenuItem *item) | |
386 | { | |
387 | return wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos); | |
388 | } | |
389 | ||
390 | wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) | |
391 | { | |
392 | // we need to find the items position in the child list | |
393 | size_t pos; | |
394 | wxMenuItemList::Node *node = GetMenuItems().GetFirst(); | |
395 | for ( pos = 0; node; pos++ ) | |
396 | { | |
397 | if ( node->GetData() == item ) | |
398 | break; | |
399 | ||
400 | node = node->GetNext(); | |
401 | } | |
402 | ||
403 | // DoRemove() (unlike Remove) can only be called for existing item! | |
404 | wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") ); | |
405 | ||
406 | #if wxUSE_ACCEL | |
407 | // remove the corresponding accel from the accel table | |
408 | int n = FindAccel(item->GetId()); | |
409 | if ( n != wxNOT_FOUND ) | |
410 | { | |
411 | delete m_accels[n]; | |
412 | ||
413 | m_accels.Remove(n); | |
414 | } | |
415 | //else: this item doesn't have an accel, nothing to do | |
416 | #endif // wxUSE_ACCEL | |
417 | ||
418 | ::DeleteMenuItem( m_hMenu , pos + 1); | |
419 | ||
420 | if ( IsAttached() ) | |
421 | { | |
422 | // otherwise, the chane won't be visible | |
423 | m_menuBar->Refresh(); | |
424 | } | |
425 | ||
426 | // and from internal data structures | |
427 | return wxMenuBase::DoRemove(item); | |
428 | } | |
429 | ||
430 | // --------------------------------------------------------------------------- | |
431 | // accelerator helpers | |
432 | // --------------------------------------------------------------------------- | |
433 | ||
434 | #if wxUSE_ACCEL | |
435 | ||
436 | // create the wxAcceleratorEntries for our accels and put them into provided | |
437 | // array - return the number of accels we have | |
438 | size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const | |
439 | { | |
440 | size_t count = GetAccelCount(); | |
441 | for ( size_t n = 0; n < count; n++ ) | |
442 | { | |
443 | *accels++ = *m_accels[n]; | |
444 | } | |
445 | ||
446 | return count; | |
447 | } | |
448 | ||
449 | #endif // wxUSE_ACCEL | |
450 | ||
451 | void wxMenu::SetTitle(const wxString& label) | |
452 | { | |
453 | Str255 title ; | |
454 | m_title = label ; | |
455 | wxMacBuildMenuString( title, NULL , NULL , label , false ); | |
456 | UMASetMenuTitle( m_hMenu , title ) ; | |
457 | } | |
458 | ||
459 | /* | |
460 | ||
461 | void wxMenu::SetLabel(int id, const wxString& label) | |
462 | { | |
463 | Str255 maclabel ; | |
464 | int index ; | |
465 | wxMenuItem *item = FindItemForId(id) ; | |
466 | if (item==NULL) | |
467 | return; | |
468 | ||
469 | index = MacGetIndexFromItem( item ) ; | |
470 | if (index < 1) | |
471 | return; | |
472 | ||
473 | if (item->GetSubMenu()==NULL) | |
474 | { | |
475 | wxMacBuildMenuString( maclabel , NULL , NULL , label , false ); | |
476 | ::SetMenuItemText( m_hMenu , index , maclabel ) ; | |
477 | } | |
478 | else | |
479 | { | |
480 | wxMacBuildMenuString( maclabel , NULL , NULL , label , false ); | |
481 | ::SetMenuItemText( m_hMenu , index , maclabel ) ; | |
482 | } | |
483 | item->SetName(label); | |
484 | } | |
485 | ||
486 | wxString wxMenu::GetLabel(int Id) const | |
487 | { | |
488 | wxMenuItem *pItem = FindItemForId(Id) ; | |
489 | return pItem->GetName() ; | |
490 | } | |
491 | ||
492 | // Finds the item id matching the given string, -1 if not found. | |
493 | int wxMenu::FindItem (const wxString& itemString) const | |
494 | { | |
495 | char buf1[200]; | |
496 | char buf2[200]; | |
497 | wxStripMenuCodes ((char *)(const char *)itemString, buf1); | |
498 | ||
499 | for (wxNode * node = m_menuItems.First (); node; node = node->Next ()) | |
500 | { | |
501 | wxMenuItem *item = (wxMenuItem *) node->Data (); | |
502 | if (item->GetSubMenu()) | |
503 | { | |
504 | int ans = item->GetSubMenu()->FindItem(itemString); | |
505 | if (ans > -1) | |
506 | return ans; | |
507 | } | |
508 | if ( !item->IsSeparator() ) | |
509 | { | |
510 | wxStripMenuCodes((char *)item->GetName().c_str(), buf2); | |
511 | if (strcmp(buf1, buf2) == 0) | |
512 | return item->GetId(); | |
513 | } | |
514 | } | |
515 | ||
516 | return -1; | |
517 | } | |
518 | ||
519 | wxMenuItem *wxMenu::FindItemForId(int itemId, wxMenu ** itemMenu) const | |
520 | { | |
521 | if (itemMenu) | |
522 | *itemMenu = NULL; | |
523 | for (wxNode * node = m_menuItems.First (); node; node = node->Next ()) | |
524 | { | |
525 | wxMenuItem *item = (wxMenuItem *) node->Data (); | |
526 | ||
527 | if (item->GetId() == itemId) | |
528 | { | |
529 | if (itemMenu) | |
530 | *itemMenu = (wxMenu *) this; | |
531 | return item; | |
532 | } | |
533 | ||
534 | if (item->GetSubMenu()) | |
535 | { | |
536 | wxMenuItem *ans = item->GetSubMenu()->FindItemForId (itemId, itemMenu); | |
537 | if (ans) | |
538 | return ans; | |
539 | } | |
540 | } | |
541 | ||
542 | if (itemMenu) | |
543 | *itemMenu = NULL; | |
544 | return NULL; | |
545 | } | |
546 | ||
547 | void wxMenu::SetHelpString(int itemId, const wxString& helpString) | |
548 | { | |
549 | wxMenuItem *item = FindItemForId (itemId); | |
550 | if (item) | |
551 | item->SetHelp(helpString); | |
552 | } | |
553 | ||
554 | wxString wxMenu::GetHelpString (int itemId) const | |
555 | { | |
556 | wxMenuItem *item = FindItemForId (itemId); | |
557 | wxString str(""); | |
558 | return (item == NULL) ? str : item->GetHelp(); | |
559 | } | |
560 | */ | |
561 | ||
562 | bool wxMenu::ProcessCommand(wxCommandEvent & event) | |
563 | { | |
564 | bool processed = FALSE; | |
565 | ||
566 | #if WXWIN_COMPATIBILITY | |
567 | // Try a callback | |
568 | if (m_callback) | |
569 | { | |
570 | (void)(*(m_callback))(*this, event); | |
571 | processed = TRUE; | |
572 | } | |
573 | #endif WXWIN_COMPATIBILITY | |
574 | ||
575 | // Try the menu's event handler | |
576 | if ( !processed && GetEventHandler()) | |
577 | { | |
578 | processed = GetEventHandler()->ProcessEvent(event); | |
579 | } | |
580 | ||
581 | // Try the window the menu was popped up from (and up through the | |
582 | // hierarchy) | |
583 | wxWindow *win = GetInvokingWindow(); | |
584 | if ( !processed && win ) | |
585 | processed = win->GetEventHandler()->ProcessEvent(event); | |
586 | ||
587 | return processed; | |
588 | } | |
589 | ||
590 | ||
591 | // --------------------------------------------------------------------------- | |
592 | // other | |
593 | // --------------------------------------------------------------------------- | |
594 | ||
595 | void wxMenu::Attach(wxMenuBar *menubar) | |
596 | { | |
597 | // menu can be in at most one menubar because otherwise they would both | |
598 | // delete the menu pointer | |
599 | wxASSERT_MSG( !m_menuBar, wxT("menu belongs to 2 menubars, expect a crash") ); | |
600 | ||
601 | m_menuBar = menubar; | |
602 | } | |
603 | ||
604 | void wxMenu::Detach() | |
605 | { | |
606 | wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") ); | |
607 | ||
608 | m_menuBar = NULL; | |
609 | } | |
610 | ||
611 | wxWindow *wxMenu::GetWindow() const | |
612 | { | |
613 | if ( m_invokingWindow != NULL ) | |
614 | return m_invokingWindow; | |
615 | else if ( m_menuBar != NULL) | |
616 | return m_menuBar->GetFrame(); | |
617 | ||
618 | return NULL; | |
619 | } | |
620 | ||
621 | // helper functions returning the mac menu position for a certain item, note that this is | |
622 | // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0 | |
623 | ||
624 | int wxMenu::MacGetIndexFromId( int id ) | |
625 | { | |
626 | size_t pos; | |
627 | wxMenuItemList::Node *node = GetMenuItems().GetFirst(); | |
628 | for ( pos = 0; node; pos++ ) | |
629 | { | |
630 | if ( node->GetData()->GetId() == id ) | |
631 | break; | |
632 | ||
633 | node = node->GetNext(); | |
634 | } | |
635 | ||
636 | if (!node) | |
637 | return 0; | |
638 | ||
639 | return pos + 1 ; | |
640 | } | |
641 | ||
642 | int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem ) | |
643 | { | |
644 | size_t pos; | |
645 | wxMenuItemList::Node *node = GetMenuItems().GetFirst(); | |
646 | for ( pos = 0; node; pos++ ) | |
647 | { | |
648 | if ( node->GetData() == pItem ) | |
649 | break; | |
650 | ||
651 | node = node->GetNext(); | |
652 | } | |
653 | ||
654 | if (!node) | |
655 | return 0; | |
656 | ||
657 | return pos + 1 ; | |
658 | } | |
659 | ||
660 | void wxMenu::MacEnableMenu( bool bDoEnable ) | |
661 | { | |
662 | if ( bDoEnable ) | |
663 | UMAEnableMenuItem( m_hMenu , 0 ) ; | |
664 | else | |
665 | UMADisableMenuItem( m_hMenu , 0 ) ; | |
666 | ||
667 | ::DrawMenuBar() ; | |
668 | } | |
669 | ||
670 | bool wxMenu::MacMenuSelect( wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum ) | |
671 | { | |
672 | int pos; | |
673 | wxNode *node; | |
674 | ||
675 | if ( m_macMenuId == macMenuId ) | |
676 | { | |
677 | node = GetMenuItems().Nth(macMenuItemNum-1); | |
678 | if (node) | |
679 | { | |
680 | wxMenuItem *pItem = (wxMenuItem*)node->Data(); | |
681 | ||
682 | wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId()); | |
683 | event.m_timeStamp = when; | |
684 | event.SetEventObject(handler); | |
685 | event.SetInt( pItem->GetId() ); | |
686 | { | |
687 | bool processed = false ; | |
688 | ||
689 | #if WXWIN_COMPATIBILITY | |
690 | // Try a callback | |
691 | if (m_callback) | |
692 | { | |
693 | (void) (*(m_callback)) (*this, event); | |
694 | processed = TRUE; | |
695 | } | |
696 | #endif | |
697 | // Try the menu's event handler | |
698 | if ( !processed && handler) | |
699 | { | |
700 | processed = handler->ProcessEvent(event); | |
701 | } | |
702 | ||
703 | // Try the window the menu was popped up from (and up | |
704 | // through the hierarchy) | |
705 | if ( !processed && GetInvokingWindow()) | |
706 | processed = GetInvokingWindow()->GetEventHandler()->ProcessEvent(event); | |
707 | } | |
708 | return true ; | |
709 | } | |
710 | } | |
711 | else if ( macMenuId == kHMHelpMenuID ) | |
712 | { | |
713 | int menuItem = formerHelpMenuItems ; | |
714 | for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++) | |
715 | { | |
716 | wxMenuItem * pItem = (wxMenuItem *) node->Data() ; | |
717 | ||
718 | wxMenu *pSubMenu = pItem->GetSubMenu() ; | |
719 | if ( pSubMenu != NULL ) | |
720 | { | |
721 | } | |
722 | else | |
723 | { | |
724 | if ( pItem->GetId() != wxApp::s_macAboutMenuItemId ) | |
725 | ++menuItem ; | |
726 | ||
727 | if ( menuItem == macMenuItemNum ) | |
728 | { | |
729 | wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, pItem->GetId()); | |
730 | event.m_timeStamp = when; | |
731 | event.SetEventObject(handler); | |
732 | event.SetInt( pItem->GetId() ); | |
733 | { | |
734 | bool processed = false ; | |
735 | #if WXWIN_COMPATIBILITY | |
736 | // Try a callback | |
737 | if (m_callback) | |
738 | { | |
739 | (void) (*(m_callback)) (*this, event); | |
740 | processed = TRUE; | |
741 | } | |
742 | #endif | |
743 | // Try the menu's event handler | |
744 | if ( !processed && handler) | |
745 | { | |
746 | processed = handler->ProcessEvent(event); | |
747 | } | |
748 | ||
749 | // Try the window the menu was popped up from (and up | |
750 | // through the hierarchy) | |
751 | if ( !processed && GetInvokingWindow()) | |
752 | processed = GetInvokingWindow()->GetEventHandler()->ProcessEvent(event); | |
753 | } | |
754 | return true ; | |
755 | } | |
756 | } | |
757 | } | |
758 | } | |
759 | ||
760 | for (pos = 0, node = GetMenuItems().First(); node; node = node->Next(), pos++) | |
761 | { | |
762 | wxMenuItem * pItem = (wxMenuItem *) node->Data() ; | |
763 | ||
764 | wxMenu *pSubMenu = pItem->GetSubMenu() ; | |
765 | if ( pSubMenu != NULL ) | |
766 | { | |
767 | if ( pSubMenu->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) ) | |
768 | return true ; | |
769 | } | |
770 | } | |
771 | ||
772 | return false ; | |
773 | } | |
774 | ||
775 | // Menu Bar | |
776 | ||
777 | /* | |
778 | ||
779 | Mac Implementation note : | |
780 | ||
781 | The Mac has only one global menubar, so we attempt to install the currently | |
782 | active menubar from a frame, we currently don't take into account mdi-frames | |
783 | which would ask for menu-merging | |
784 | ||
785 | Secondly there is no mac api for changing a menubar that is not the current | |
786 | menubar, so we have to wait for preparing the actual menubar until the | |
787 | wxMenubar is to be used | |
788 | ||
789 | We can in subsequent versions use MacInstallMenuBar to provide some sort of | |
790 | auto-merge for MDI in case this will be necessary | |
791 | ||
792 | */ | |
793 | ||
794 | wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ; | |
795 | ||
796 | void wxMenuBar::Init() | |
797 | { | |
798 | m_eventHandler = this; | |
799 | m_menuBarFrame = NULL; | |
800 | } | |
801 | ||
802 | wxMenuBar::wxMenuBar() | |
803 | { | |
804 | Init(); | |
805 | } | |
806 | ||
807 | wxMenuBar::wxMenuBar( long WXUNUSED(style) ) | |
808 | { | |
809 | Init(); | |
810 | } | |
811 | ||
812 | ||
813 | wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[]) | |
814 | { | |
815 | Init(); | |
816 | ||
817 | m_titles.Alloc(count); | |
818 | ||
819 | for ( int i = 0; i < count; i++ ) | |
820 | { | |
821 | m_menus.Append(menus[i]); | |
822 | m_titles.Add(titles[i]); | |
823 | ||
824 | menus[i]->Attach(this); | |
825 | } | |
826 | } | |
827 | ||
828 | wxMenuBar::~wxMenuBar() | |
829 | { | |
830 | if (s_macInstalledMenuBar == this) | |
831 | { | |
832 | ::ClearMenuBar(); | |
833 | s_macInstalledMenuBar = NULL; | |
834 | } | |
835 | ||
836 | } | |
837 | ||
838 | void wxMenuBar::Refresh() | |
839 | { | |
840 | wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") ); | |
841 | ||
842 | DrawMenuBar(); | |
843 | } | |
844 | ||
845 | #if wxUSE_ACCEL | |
846 | ||
847 | void wxMenuBar::RebuildAccelTable() | |
848 | { | |
849 | // merge the accelerators of all menus into one accel table | |
850 | size_t nAccelCount = 0; | |
851 | size_t i, count = GetMenuCount(); | |
852 | for ( i = 0; i < count; i++ ) | |
853 | { | |
854 | nAccelCount += m_menus[i]->GetAccelCount(); | |
855 | } | |
856 | ||
857 | if ( nAccelCount ) | |
858 | { | |
859 | wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount]; | |
860 | ||
861 | nAccelCount = 0; | |
862 | for ( i = 0; i < count; i++ ) | |
863 | { | |
864 | nAccelCount += m_menus[i]->CopyAccels(&accelEntries[nAccelCount]); | |
865 | } | |
866 | ||
867 | m_accelTable = wxAcceleratorTable(nAccelCount, accelEntries); | |
868 | ||
869 | delete [] accelEntries; | |
870 | } | |
871 | } | |
872 | ||
873 | #endif // wxUSE_ACCEL | |
874 | ||
875 | ||
876 | void wxMenuBar::MacInstallMenuBar() | |
877 | { | |
878 | Handle menubar = ::GetNewMBar( kwxMacMenuBarResource ) ; | |
879 | wxString message ; | |
880 | wxCHECK_RET( menubar != NULL, "can't read MBAR resource" ); | |
881 | ::SetMenuBar( menubar ) ; | |
882 | ::DisposeHandle( menubar ) ; | |
883 | ||
884 | MenuHandle menu = ::GetMenuHandle( kwxMacAppleMenuId ) ; | |
885 | ::AppendResMenu(menu, 'DRVR'); | |
886 | ||
887 | for (int i = 0; i < m_menus.GetCount(); i++) | |
888 | { | |
889 | Str255 label; | |
890 | wxNode *node; | |
891 | wxMenuItem *item; | |
892 | int pos ; | |
893 | wxMenu* menu = m_menus[i] , *subMenu = NULL ; | |
894 | ||
895 | ||
896 | if( m_titles[i] == "?" || m_titles[i] == "&?" || m_titles[i] == wxApp::s_macHelpMenuTitleName ) | |
897 | { | |
898 | MenuHandle mh = NULL ; | |
899 | if ( HMGetHelpMenuHandle( &mh ) != noErr ) | |
900 | { | |
901 | continue ; | |
902 | } | |
903 | if ( formerHelpMenuItems == 0 ) | |
904 | { | |
905 | if( mh ) | |
906 | formerHelpMenuItems = CountMenuItems( mh ) ; | |
907 | } | |
908 | ||
909 | for (pos = 0 , node = menu->GetMenuItems().First(); node; node = node->Next(), pos++) | |
910 | { | |
911 | item = (wxMenuItem *)node->Data(); | |
912 | subMenu = item->GetSubMenu() ; | |
913 | if (subMenu) | |
914 | { | |
915 | // we don't support hierarchical menus in the help menu yet | |
916 | } | |
917 | else | |
918 | { | |
919 | if ( item->IsSeparator() ) | |
920 | { | |
921 | if ( mh ) | |
922 | ::AppendMenu(mh, "\p-" ); | |
923 | } | |
924 | else | |
925 | { | |
926 | Str255 label ; | |
927 | wxMacBuildMenuString( label , NULL , NULL , item->GetText(), item->GetId() != wxApp::s_macAboutMenuItemId); // no shortcut in about menu | |
928 | if ( label[0] == 0 ) | |
929 | { | |
930 | // we cannot add empty menus on mac | |
931 | label[0] = 1 ; | |
932 | label[1] = ' ' ; | |
933 | } | |
934 | if ( item->GetId() == wxApp::s_macAboutMenuItemId ) | |
935 | { | |
936 | ::SetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , label ); | |
937 | // ::EnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 ); | |
938 | ::EnableItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 ); | |
939 | } | |
940 | else | |
941 | { | |
942 | if ( mh ) | |
943 | ::AppendMenu(mh, label ); | |
944 | } | |
945 | } | |
946 | } | |
947 | } | |
948 | } | |
949 | else | |
950 | { | |
951 | wxMacBuildMenuString( label, NULL , NULL , m_titles[i] , false ); | |
952 | UMASetMenuTitle( menu->GetHMenu() , label ) ; | |
953 | for (pos = 0, node = menu->GetMenuItems().First(); node; node = node->Next(), pos++) | |
954 | { | |
955 | item = (wxMenuItem *)node->Data(); | |
956 | subMenu = item->GetSubMenu() ; | |
957 | if (subMenu) | |
958 | { | |
959 | ::InsertMenu( subMenu->GetHMenu() , -1 ) ; | |
960 | } | |
961 | } | |
962 | ::InsertMenu(m_menus[i]->GetHMenu(), 0); | |
963 | } | |
964 | } | |
965 | ::DrawMenuBar() ; | |
966 | ||
967 | s_macInstalledMenuBar = this; | |
968 | } | |
969 | ||
970 | void wxMenuBar::EnableTop(size_t pos, bool enable) | |
971 | { | |
972 | wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") ); | |
973 | m_menus[pos]->MacEnableMenu( enable ) ; | |
974 | Refresh(); | |
975 | } | |
976 | ||
977 | void wxMenuBar::SetLabelTop(size_t pos, const wxString& label) | |
978 | { | |
979 | wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") ); | |
980 | ||
981 | m_titles[pos] = label; | |
982 | ||
983 | if ( !IsAttached() ) | |
984 | { | |
985 | return; | |
986 | } | |
987 | ||
988 | m_menus[pos]->SetTitle( label ) ; | |
989 | if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ? | |
990 | { | |
991 | ::SetMenuBar( GetMenuBar() ) ; | |
992 | ::InvalMenuBar() ; | |
993 | } | |
994 | } | |
995 | ||
996 | wxString wxMenuBar::GetLabelTop(size_t pos) const | |
997 | { | |
998 | wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString, | |
999 | wxT("invalid menu index in wxMenuBar::GetLabelTop") ); | |
1000 | ||
1001 | return m_titles[pos]; | |
1002 | } | |
1003 | ||
1004 | int wxMenuBar::FindMenu(const wxString& title) | |
1005 | { | |
1006 | wxString menuTitle = wxStripMenuCodes(title); | |
1007 | ||
1008 | size_t count = GetMenuCount(); | |
1009 | for ( size_t i = 0; i < count; i++ ) | |
1010 | { | |
1011 | wxString title = wxStripMenuCodes(m_titles[i]); | |
1012 | if ( menuTitle == title ) | |
1013 | return i; | |
1014 | } | |
1015 | ||
1016 | return wxNOT_FOUND; | |
1017 | ||
1018 | } | |
1019 | ||
1020 | ||
1021 | // --------------------------------------------------------------------------- | |
1022 | // wxMenuBar construction | |
1023 | // --------------------------------------------------------------------------- | |
1024 | ||
1025 | // --------------------------------------------------------------------------- | |
1026 | // wxMenuBar construction | |
1027 | // --------------------------------------------------------------------------- | |
1028 | ||
1029 | wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) | |
1030 | { | |
1031 | wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title); | |
1032 | if ( !menuOld ) | |
1033 | return FALSE; | |
1034 | m_titles[pos] = title; | |
1035 | ||
1036 | if ( IsAttached() ) | |
1037 | { | |
1038 | if (s_macInstalledMenuBar == this) | |
1039 | { | |
1040 | ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ; | |
1041 | { | |
1042 | Str255 label; | |
1043 | wxMacBuildMenuString( label, NULL , NULL , title , false ); | |
1044 | UMASetMenuTitle( menu->GetHMenu() , label ) ; | |
1045 | if ( pos == m_menus.GetCount() - 1) | |
1046 | { | |
1047 | ::InsertMenu( menu->GetHMenu() , 0 ) ; | |
1048 | } | |
1049 | else | |
1050 | { | |
1051 | ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ; | |
1052 | } | |
1053 | } | |
1054 | } | |
1055 | ||
1056 | ||
1057 | #if wxUSE_ACCEL | |
1058 | if ( menuOld->HasAccels() || menu->HasAccels() ) | |
1059 | { | |
1060 | // need to rebuild accell table | |
1061 | RebuildAccelTable(); | |
1062 | } | |
1063 | #endif // wxUSE_ACCEL | |
1064 | ||
1065 | Refresh(); | |
1066 | } | |
1067 | ||
1068 | return menuOld; | |
1069 | } | |
1070 | ||
1071 | bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title) | |
1072 | { | |
1073 | if ( !wxMenuBarBase::Insert(pos, menu, title) ) | |
1074 | return FALSE; | |
1075 | ||
1076 | m_titles.Insert(title, pos); | |
1077 | ||
1078 | menu->Attach(this); | |
1079 | ||
1080 | if ( IsAttached() ) | |
1081 | { | |
1082 | if ( pos == (size_t) -1 ) | |
1083 | { | |
1084 | ::InsertMenu( menu->GetHMenu() , 0 ) ; | |
1085 | } | |
1086 | else | |
1087 | { | |
1088 | ::InsertMenu( menu->GetHMenu() , m_menus[pos+1]->MacGetMenuId() ) ; | |
1089 | } | |
1090 | ||
1091 | #if wxUSE_ACCEL | |
1092 | if ( menu->HasAccels() ) | |
1093 | { | |
1094 | // need to rebuild accell table | |
1095 | RebuildAccelTable(); | |
1096 | } | |
1097 | #endif // wxUSE_ACCEL | |
1098 | ||
1099 | Refresh(); | |
1100 | } | |
1101 | ||
1102 | return TRUE; | |
1103 | } | |
1104 | ||
1105 | void wxMenuBar::MacMenuSelect(wxEvtHandler* handler, long when , int macMenuId, int macMenuItemNum) | |
1106 | { | |
1107 | // first scan fast for direct commands, i.e. menus which have these commands directly in their own list | |
1108 | ||
1109 | if ( macMenuId == kwxMacAppleMenuId && macMenuItemNum == 1 ) | |
1110 | { | |
1111 | wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, wxApp::s_macAboutMenuItemId ); | |
1112 | event.m_timeStamp = when; | |
1113 | event.SetEventObject(handler); | |
1114 | event.SetInt( wxApp::s_macAboutMenuItemId ); | |
1115 | handler->ProcessEvent(event); | |
1116 | } | |
1117 | else | |
1118 | { | |
1119 | for (int i = 0; i < m_menus.GetCount() ; i++) | |
1120 | { | |
1121 | if ( m_menus[i]->MacGetMenuId() == macMenuId || | |
1122 | ( macMenuId == kHMHelpMenuID && ( m_titles[i] == "?" || m_titles[i] == "&?" || m_titles[i] == wxApp::s_macHelpMenuTitleName ) ) | |
1123 | ) | |
1124 | { | |
1125 | if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) ) | |
1126 | return ; | |
1127 | else | |
1128 | { | |
1129 | //TODO flag this as an error since it must contain the item | |
1130 | return ; | |
1131 | } | |
1132 | } | |
1133 | } | |
1134 | ||
1135 | for (int i = 0; i < m_menus.GetCount(); i++) | |
1136 | { | |
1137 | if ( m_menus[i]->MacMenuSelect( handler , when , macMenuId , macMenuItemNum ) ) | |
1138 | { | |
1139 | break ; | |
1140 | } | |
1141 | } | |
1142 | } | |
1143 | } | |
1144 | ||
1145 | wxMenu *wxMenuBar::Remove(size_t pos) | |
1146 | { | |
1147 | wxMenu *menu = wxMenuBarBase::Remove(pos); | |
1148 | if ( !menu ) | |
1149 | return NULL; | |
1150 | ||
1151 | if ( IsAttached() ) | |
1152 | { | |
1153 | if (s_macInstalledMenuBar == this) | |
1154 | { | |
1155 | ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ; | |
1156 | } | |
1157 | ||
1158 | menu->Detach(); | |
1159 | ||
1160 | #if wxUSE_ACCEL | |
1161 | if ( menu->HasAccels() ) | |
1162 | { | |
1163 | // need to rebuild accell table | |
1164 | RebuildAccelTable(); | |
1165 | } | |
1166 | #endif // wxUSE_ACCEL | |
1167 | ||
1168 | Refresh(); | |
1169 | } | |
1170 | ||
1171 | m_titles.Remove(pos); | |
1172 | ||
1173 | return menu; | |
1174 | } | |
1175 | ||
1176 | bool wxMenuBar::Append(wxMenu *menu, const wxString& title) | |
1177 | { | |
1178 | WXHMENU submenu = menu ? menu->GetHMenu() : 0; | |
1179 | wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") ); | |
1180 | ||
1181 | if ( !wxMenuBarBase::Append(menu, title) ) | |
1182 | return FALSE; | |
1183 | ||
1184 | menu->Attach(this); | |
1185 | ||
1186 | m_titles.Add(title); | |
1187 | ||
1188 | if ( IsAttached() ) | |
1189 | { | |
1190 | if (s_macInstalledMenuBar == this) | |
1191 | { | |
1192 | ::InsertMenu( menu->GetHMenu() , 0 ) ; | |
1193 | } | |
1194 | ||
1195 | #if wxUSE_ACCEL | |
1196 | if ( menu->HasAccels() ) | |
1197 | { | |
1198 | // need to rebuild accell table | |
1199 | RebuildAccelTable(); | |
1200 | } | |
1201 | #endif // wxUSE_ACCEL | |
1202 | ||
1203 | Refresh(); | |
1204 | } | |
1205 | ||
1206 | return TRUE; | |
1207 | } | |
1208 | ||
1209 | // --------------------------------------------------------------------------- | |
1210 | // wxMenuBar searching for menu items | |
1211 | // --------------------------------------------------------------------------- | |
1212 | ||
1213 | // Find the itemString in menuString, and return the item id or wxNOT_FOUND | |
1214 | int wxMenuBar::FindMenuItem(const wxString& menuString, | |
1215 | const wxString& itemString) const | |
1216 | { | |
1217 | wxString menuLabel = wxStripMenuCodes(menuString); | |
1218 | size_t count = GetMenuCount(); | |
1219 | for ( size_t i = 0; i < count; i++ ) | |
1220 | { | |
1221 | wxString title = wxStripMenuCodes(m_titles[i]); | |
1222 | if ( menuString == title ) | |
1223 | return m_menus[i]->FindItem(itemString); | |
1224 | } | |
1225 | ||
1226 | return wxNOT_FOUND; | |
1227 | } | |
1228 | ||
1229 | wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const | |
1230 | { | |
1231 | if ( itemMenu ) | |
1232 | *itemMenu = NULL; | |
1233 | ||
1234 | wxMenuItem *item = NULL; | |
1235 | size_t count = GetMenuCount(); | |
1236 | for ( size_t i = 0; !item && (i < count); i++ ) | |
1237 | { | |
1238 | item = m_menus[i]->FindItem(id, itemMenu); | |
1239 | } | |
1240 | ||
1241 | return item; | |
1242 | } | |
1243 | ||
1244 |