]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/menu.cpp
fixed VC++ warning
[wxWidgets.git] / src / gtk / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: menu.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "menu.h"
12 #pragma implementation "menuitem.h"
13 #endif
14
15 #include "wx/log.h"
16 #include "wx/intl.h"
17 #include "wx/app.h"
18 #include "wx/bitmap.h"
19 #include "wx/menu.h"
20
21 #if wxUSE_ACCEL
22 #include "wx/accel.h"
23 #endif // wxUSE_ACCEL
24
25 #include "wx/gtk/private.h"
26
27 #include <gdk/gdkkeysyms.h>
28
29 // FIXME: is this right? somehow I don't think so (VZ)
30 #ifdef __WXGTK20__
31 #include <glib-object.h>
32
33 #define gtk_accel_group_attach(g, o) _gtk_accel_group_attach((g), (o))
34 #define gtk_accel_group_detach(g, o) _gtk_accel_group_detach((g), (o))
35 #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m)
36
37 #define ACCEL_OBJECT GObject
38 #define ACCEL_OBJECTS(a) (a)->acceleratables
39 #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj)
40 #else // GTK+ 1.x
41 #define ACCEL_OBJECT GtkObject
42 #define ACCEL_OBJECTS(a) (a)->attach_objects
43 #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj)
44 #endif
45
46 //-----------------------------------------------------------------------------
47 // idle system
48 //-----------------------------------------------------------------------------
49
50 extern void wxapp_install_idle_handler();
51 extern bool g_isIdle;
52
53 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
54 static wxString GetHotKey( const wxMenuItem& item );
55 #endif
56
57 //-----------------------------------------------------------------------------
58 // substitute for missing GtkPixmapMenuItem
59 //-----------------------------------------------------------------------------
60
61 // FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ)
62 #ifndef __WXGTK20__
63 #define USE_MENU_BITMAPS
64 #endif
65
66 #ifdef USE_MENU_BITMAPS
67
68 #define GTK_TYPE_PIXMAP_MENU_ITEM (gtk_pixmap_menu_item_get_type ())
69 #define GTK_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem))
70 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass))
71 #define GTK_IS_PIXMAP_MENU_ITEM(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
72 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM))
73 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM))
74 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj) (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
75
76 #ifndef GTK_MENU_ITEM_GET_CLASS
77 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj)))
78 #endif
79
80 typedef struct _GtkPixmapMenuItem GtkPixmapMenuItem;
81 typedef struct _GtkPixmapMenuItemClass GtkPixmapMenuItemClass;
82
83 struct _GtkPixmapMenuItem
84 {
85 GtkMenuItem menu_item;
86
87 GtkWidget *pixmap;
88 };
89
90 struct _GtkPixmapMenuItemClass
91 {
92 GtkMenuItemClass parent_class;
93
94 guint orig_toggle_size;
95 guint have_pixmap_count;
96 };
97
98
99 GtkType gtk_pixmap_menu_item_get_type (void);
100 GtkWidget* gtk_pixmap_menu_item_new (void);
101 void gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem *menu_item,
102 GtkWidget *pixmap);
103
104 #endif // USE_MENU_BITMAPS
105
106 //-----------------------------------------------------------------------------
107 // idle system
108 //-----------------------------------------------------------------------------
109
110 static wxString wxReplaceUnderscore( const wxString& title )
111 {
112 const wxChar *pc;
113
114 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
115 wxString str;
116 pc = title;
117 while (*pc != wxT('\0'))
118 {
119 if ((*pc == wxT('&')) && (*(pc+1) == wxT('&')))
120 {
121 // "&" is doubled to indicate "&" instead of accelerator
122 ++pc;
123 str << wxT('&');
124 }
125 else if (*pc == wxT('&'))
126 {
127 #if GTK_CHECK_VERSION(1, 2, 0)
128 str << wxT('_');
129 #endif
130 }
131 #if GTK_CHECK_VERSION(2, 0, 0)
132 else if (*pc == wxT('/'))
133 {
134 str << wxT("\\/");
135 }
136 else if (*pc == wxT('\\'))
137 {
138 str << wxT("\\\\");
139 }
140 #elif GTK_CHECK_VERSION(1, 2, 0)
141 else if (*pc == wxT('/'))
142 {
143 str << wxT('\\');
144 }
145 #endif
146 else
147 {
148 #if __WXGTK12__
149 if ( *pc == wxT('_') )
150 {
151 // underscores must be doubled to prevent them from being
152 // interpreted as accelerator character prefix by GTK
153 str << *pc;
154 }
155 #endif // GTK+ 1.2
156
157 str << *pc;
158 }
159 ++pc;
160 }
161 return str;
162 }
163
164 //-----------------------------------------------------------------------------
165 // wxMenuBar
166 //-----------------------------------------------------------------------------
167
168 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow)
169
170 wxMenuBar::wxMenuBar( long style )
171 {
172 /* the parent window is known after wxFrame::SetMenu() */
173 m_needParent = FALSE;
174 m_style = style;
175 m_invokingWindow = (wxWindow*) NULL;
176
177 if (!PreCreation( (wxWindow*) NULL, wxDefaultPosition, wxDefaultSize ) ||
178 !CreateBase( (wxWindow*) NULL, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("menubar") ))
179 {
180 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
181 return;
182 }
183
184 m_menus.DeleteContents( TRUE );
185
186 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
187 #if GTK_CHECK_VERSION(1, 2, 1)
188 m_accel = gtk_accel_group_new();
189 m_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", m_accel );
190 m_menubar = gtk_item_factory_get_widget( m_factory, "<main>" );
191 #else
192 m_menubar = gtk_menu_bar_new();
193 #endif
194
195 if (style & wxMB_DOCKABLE)
196 {
197 m_widget = gtk_handle_box_new();
198 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_menubar) );
199 gtk_widget_show( GTK_WIDGET(m_menubar) );
200 }
201 else
202 {
203 m_widget = GTK_WIDGET(m_menubar);
204 }
205
206 PostCreation();
207
208 ApplyWidgetStyle();
209 }
210
211 wxMenuBar::wxMenuBar()
212 {
213 /* the parent window is known after wxFrame::SetMenu() */
214 m_needParent = FALSE;
215 m_style = 0;
216 m_invokingWindow = (wxWindow*) NULL;
217
218 if (!PreCreation( (wxWindow*) NULL, wxDefaultPosition, wxDefaultSize ) ||
219 !CreateBase( (wxWindow*) NULL, -1, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, wxT("menubar") ))
220 {
221 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
222 return;
223 }
224
225 m_menus.DeleteContents( TRUE );
226
227 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
228 #if GTK_CHECK_VERSION(1, 2, 1)
229 m_accel = gtk_accel_group_new();
230 m_factory = gtk_item_factory_new( GTK_TYPE_MENU_BAR, "<main>", m_accel );
231 m_menubar = gtk_item_factory_get_widget( m_factory, "<main>" );
232 #else
233 m_menubar = gtk_menu_bar_new();
234 #endif
235
236 m_widget = GTK_WIDGET(m_menubar);
237
238 PostCreation();
239
240 ApplyWidgetStyle();
241 }
242
243 wxMenuBar::~wxMenuBar()
244 {
245 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
246 }
247
248 static void wxMenubarUnsetInvokingWindow( wxMenu *menu, wxWindow *win )
249 {
250 menu->SetInvokingWindow( (wxWindow*) NULL );
251
252 wxWindow *top_frame = win;
253 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
254 top_frame = top_frame->GetParent();
255
256 /* support for native hot keys */
257 gtk_accel_group_detach( menu->m_accel, ACCEL_OBJ_CAST(top_frame->m_widget) );
258
259 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
260 while (node)
261 {
262 wxMenuItem *menuitem = node->GetData();
263 if (menuitem->IsSubMenu())
264 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu(), win );
265 node = node->GetNext();
266 }
267 }
268
269 static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win )
270 {
271 menu->SetInvokingWindow( win );
272
273 #if GTK_CHECK_VERSION(1, 2, 1)
274 wxWindow *top_frame = win;
275 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
276 top_frame = top_frame->GetParent();
277
278 /* support for native hot keys */
279 ACCEL_OBJECT *obj = ACCEL_OBJ_CAST(top_frame->m_widget);
280 if ( !g_slist_find( ACCEL_OBJECTS(menu->m_accel), obj ) )
281 gtk_accel_group_attach( menu->m_accel, obj );
282 #endif // GTK+ 1.2.1+
283
284 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
285 while (node)
286 {
287 wxMenuItem *menuitem = node->GetData();
288 if (menuitem->IsSubMenu())
289 wxMenubarSetInvokingWindow( menuitem->GetSubMenu(), win );
290 node = node->GetNext();
291 }
292 }
293
294 void wxMenuBar::SetInvokingWindow( wxWindow *win )
295 {
296 m_invokingWindow = win;
297 #if GTK_CHECK_VERSION(1, 2, 1)
298 wxWindow *top_frame = win;
299 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
300 top_frame = top_frame->GetParent();
301
302 /* support for native key accelerators indicated by underscroes */
303 ACCEL_OBJECT *obj = ACCEL_OBJ_CAST(top_frame->m_widget);
304 if ( !g_slist_find( ACCEL_OBJECTS(m_accel), obj ) )
305 gtk_accel_group_attach( m_accel, obj );
306 #endif // GTK+ 1.2.1+
307
308 wxMenuList::Node *node = m_menus.GetFirst();
309 while (node)
310 {
311 wxMenu *menu = node->GetData();
312 wxMenubarSetInvokingWindow( menu, win );
313 node = node->GetNext();
314 }
315 }
316
317 void wxMenuBar::UnsetInvokingWindow( wxWindow *win )
318 {
319 m_invokingWindow = (wxWindow*) NULL;
320 #if GTK_CHECK_VERSION(1, 2, 1)
321 wxWindow *top_frame = win;
322 while (top_frame->GetParent() && !(top_frame->IsTopLevel()))
323 top_frame = top_frame->GetParent();
324
325 /* support for native key accelerators indicated by underscroes */
326 gtk_accel_group_detach( m_accel, ACCEL_OBJ_CAST(top_frame->m_widget) );
327 #endif // GTK+ 1.2.1+
328
329 wxMenuList::Node *node = m_menus.GetFirst();
330 while (node)
331 {
332 wxMenu *menu = node->GetData();
333 wxMenubarUnsetInvokingWindow( menu, win );
334 node = node->GetNext();
335 }
336 }
337
338 bool wxMenuBar::Append( wxMenu *menu, const wxString &title )
339 {
340 if ( !wxMenuBarBase::Append( menu, title ) )
341 return FALSE;
342
343 return GtkAppend(menu, title);
344 }
345
346 bool wxMenuBar::GtkAppend(wxMenu *menu, const wxString& title)
347 {
348 wxString str( wxReplaceUnderscore( title ) );
349
350 /* this doesn't have much effect right now */
351 menu->SetTitle( str );
352
353 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
354 #if GTK_CHECK_VERSION(1, 2, 1)
355
356 wxString buf;
357 buf << wxT('/') << str.c_str();
358
359 /* local buffer in multibyte form */
360 char cbuf[400];
361 strcpy(cbuf, wxGTK_CONV(buf) );
362
363 GtkItemFactoryEntry entry;
364 entry.path = (gchar *)cbuf; // const_cast
365 entry.accelerator = (gchar*) NULL;
366 entry.callback = (GtkItemFactoryCallback) NULL;
367 entry.callback_action = 0;
368 entry.item_type = (char *)"<Branch>";
369
370 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
371 /* in order to get the pointer to the item we need the item text _without_ underscores */
372 wxString tmp = wxT("<main>/");
373 const wxChar *pc;
374 for ( pc = str; *pc != wxT('\0'); pc++ )
375 {
376 // contrary to the common sense, we must throw out _all_ underscores,
377 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
378 // might naively think). IMHO it's a bug in GTK+ (VZ)
379 while (*pc == wxT('_'))
380 pc++;
381 tmp << *pc;
382 }
383 menu->m_owner = gtk_item_factory_get_item( m_factory, wxGTK_CONV( tmp ) );
384 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
385 #else
386
387 menu->m_owner = gtk_menu_item_new_with_label( wxGTK_CONV( str ) );
388 gtk_widget_show( menu->m_owner );
389 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
390
391 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar), menu->m_owner );
392
393 #endif
394
395 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
396 // addings menu later on.
397 if (m_invokingWindow)
398 {
399 wxMenubarSetInvokingWindow( menu, m_invokingWindow );
400
401 // OPTIMISE ME: we should probably cache this, or pass it
402 // directly, but for now this is a minimal
403 // change to validate the new dynamic sizing.
404 // see (and refactor :) similar code in Remove
405 // below.
406
407 wxFrame *frame = wxDynamicCast( m_invokingWindow, wxFrame );
408
409 if( frame )
410 frame->UpdateMenuBarSize();
411 }
412
413 return TRUE;
414 }
415
416 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
417 {
418 if ( !wxMenuBarBase::Insert(pos, menu, title) )
419 return FALSE;
420
421 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
422 // of version 1.2.6), so we first append the item and then change its
423 // index
424 if ( !GtkAppend(menu, title) )
425 return FALSE;
426
427 if (pos+1 >= m_menus.GetCount())
428 return TRUE;
429
430 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
431 gpointer data = g_list_last(menu_shell->children)->data;
432 menu_shell->children = g_list_remove(menu_shell->children, data);
433 menu_shell->children = g_list_insert(menu_shell->children, data, pos);
434
435 return TRUE;
436 }
437
438 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
439 {
440 // remove the old item and insert a new one
441 wxMenu *menuOld = Remove(pos);
442 if ( menuOld && !Insert(pos, menu, title) )
443 {
444 return (wxMenu*) NULL;
445 }
446
447 // either Insert() succeeded or Remove() failed and menuOld is NULL
448 return menuOld;
449 }
450
451 wxMenu *wxMenuBar::Remove(size_t pos)
452 {
453 wxMenu *menu = wxMenuBarBase::Remove(pos);
454 if ( !menu )
455 return (wxMenu*) NULL;
456
457 /*
458 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
459
460 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
461 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
462 */
463
464 // unparent calls unref() and that would delete the widget so we raise
465 // the ref count to 2 artificially before invoking unparent.
466 gtk_widget_ref( menu->m_menu );
467 gtk_widget_unparent( menu->m_menu );
468
469 gtk_widget_destroy( menu->m_owner );
470
471 /*
472 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
473 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
474 */
475
476 if (m_invokingWindow)
477 {
478 // OPTIMISE ME: see comment in GtkAppend
479
480 wxFrame *frame = wxDynamicCast( m_invokingWindow, wxFrame );
481
482 if( frame )
483 frame->UpdateMenuBarSize();
484 }
485
486 return menu;
487 }
488
489 static int FindMenuItemRecursive( const wxMenu *menu, const wxString &menuString, const wxString &itemString )
490 {
491 if (wxMenuItem::GetLabelFromText(menu->GetTitle()) == wxMenuItem::GetLabelFromText(menuString))
492 {
493 int res = menu->FindItem( itemString );
494 if (res != wxNOT_FOUND)
495 return res;
496 }
497
498 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
499 while (node)
500 {
501 wxMenuItem *item = node->GetData();
502 if (item->IsSubMenu())
503 return FindMenuItemRecursive(item->GetSubMenu(), menuString, itemString);
504
505 node = node->GetNext();
506 }
507
508 return wxNOT_FOUND;
509 }
510
511 int wxMenuBar::FindMenuItem( const wxString &menuString, const wxString &itemString ) const
512 {
513 wxMenuList::Node *node = m_menus.GetFirst();
514 while (node)
515 {
516 wxMenu *menu = node->GetData();
517 int res = FindMenuItemRecursive( menu, menuString, itemString);
518 if (res != -1)
519 return res;
520 node = node->GetNext();
521 }
522
523 return wxNOT_FOUND;
524 }
525
526 // Find a wxMenuItem using its id. Recurses down into sub-menus
527 static wxMenuItem* FindMenuItemByIdRecursive(const wxMenu* menu, int id)
528 {
529 wxMenuItem* result = menu->FindChildItem(id);
530
531 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
532 while ( node && result == NULL )
533 {
534 wxMenuItem *item = node->GetData();
535 if (item->IsSubMenu())
536 {
537 result = FindMenuItemByIdRecursive( item->GetSubMenu(), id );
538 }
539 node = node->GetNext();
540 }
541
542 return result;
543 }
544
545 wxMenuItem* wxMenuBar::FindItem( int id, wxMenu **menuForItem ) const
546 {
547 wxMenuItem* result = 0;
548 wxMenuList::Node *node = m_menus.GetFirst();
549 while (node && result == 0)
550 {
551 wxMenu *menu = node->GetData();
552 result = FindMenuItemByIdRecursive( menu, id );
553 node = node->GetNext();
554 }
555
556 if ( menuForItem )
557 {
558 *menuForItem = result ? result->GetMenu() : (wxMenu *)NULL;
559 }
560
561 return result;
562 }
563
564 void wxMenuBar::EnableTop( size_t pos, bool flag )
565 {
566 wxMenuList::Node *node = m_menus.Item( pos );
567
568 wxCHECK_RET( node, wxT("menu not found") );
569
570 wxMenu* menu = node->GetData();
571
572 if (menu->m_owner)
573 gtk_widget_set_sensitive( menu->m_owner, flag );
574 }
575
576 wxString wxMenuBar::GetLabelTop( size_t pos ) const
577 {
578 wxMenuList::Node *node = m_menus.Item( pos );
579
580 wxCHECK_MSG( node, wxT("invalid"), wxT("menu not found") );
581
582 wxMenu* menu = node->GetData();
583
584 wxString label;
585 wxString text( menu->GetTitle() );
586 for ( const wxChar *pc = text.c_str(); *pc; pc++ )
587 {
588 if ( *pc == wxT('_') )
589 {
590 // '_' is the escape character for GTK+
591 continue;
592 }
593
594 label += *pc;
595 }
596
597 return label;
598 }
599
600 void wxMenuBar::SetLabelTop( size_t pos, const wxString& label )
601 {
602 wxMenuList::Node *node = m_menus.Item( pos );
603
604 wxCHECK_RET( node, wxT("menu not found") );
605
606 wxMenu* menu = node->GetData();
607
608 wxString str( wxReplaceUnderscore( label ) );
609
610 menu->SetTitle( str );
611
612 if (menu->m_owner)
613 {
614 GtkLabel *label = GTK_LABEL( GTK_BIN(menu->m_owner)->child );
615
616 /* set new text */
617 gtk_label_set( label, wxGTK_CONV( str ) );
618
619 /* reparse key accel */
620 (void)gtk_label_parse_uline (GTK_LABEL(label), wxGTK_CONV( str ) );
621 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label) );
622 }
623
624 }
625
626 //-----------------------------------------------------------------------------
627 // "activate"
628 //-----------------------------------------------------------------------------
629
630 static void gtk_menu_clicked_callback( GtkWidget *widget, wxMenu *menu )
631 {
632 if (g_isIdle)
633 wxapp_install_idle_handler();
634
635 int id = menu->FindMenuIdByMenuItem(widget);
636
637 /* should find it for normal (not popup) menu */
638 wxASSERT_MSG( (id != -1) || (menu->GetInvokingWindow() != NULL),
639 _T("menu item not found in gtk_menu_clicked_callback") );
640
641 if (!menu->IsEnabled(id))
642 return;
643
644 wxMenuItem* item = menu->FindChildItem( id );
645 wxCHECK_RET( item, wxT("error in menu item callback") );
646
647 if (item->IsCheckable())
648 {
649 bool isReallyChecked = item->IsChecked(),
650 isInternallyChecked = item->wxMenuItemBase::IsChecked();
651
652 // ensure that the internal state is always consistent with what is
653 // shown on the screen
654 item->wxMenuItemBase::Check(isReallyChecked);
655
656 // we must not report the events for the radio button going up nor the
657 // events resulting from the calls to wxMenuItem::Check()
658 if ( (item->GetKind() == wxITEM_RADIO && !isReallyChecked) ||
659 (isInternallyChecked == isReallyChecked) )
660 {
661 return;
662 }
663
664 // the user pressed on the menu item: report the event below
665 }
666
667 menu->SendEvent(id, item->IsCheckable() ? item->IsChecked() : -1);
668 }
669
670 //-----------------------------------------------------------------------------
671 // "select"
672 //-----------------------------------------------------------------------------
673
674 static void gtk_menu_hilight_callback( GtkWidget *widget, wxMenu *menu )
675 {
676 if (g_isIdle) wxapp_install_idle_handler();
677
678 int id = menu->FindMenuIdByMenuItem(widget);
679
680 wxASSERT( id != -1 ); // should find it!
681
682 if (!menu->IsEnabled(id))
683 return;
684
685 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, id );
686 event.SetEventObject( menu );
687
688 if (menu->GetEventHandler()->ProcessEvent(event))
689 return;
690
691 wxWindow *win = menu->GetInvokingWindow();
692 if (win) win->GetEventHandler()->ProcessEvent( event );
693 }
694
695 //-----------------------------------------------------------------------------
696 // "deselect"
697 //-----------------------------------------------------------------------------
698
699 static void gtk_menu_nolight_callback( GtkWidget *widget, wxMenu *menu )
700 {
701 if (g_isIdle) wxapp_install_idle_handler();
702
703 int id = menu->FindMenuIdByMenuItem(widget);
704
705 wxASSERT( id != -1 ); // should find it!
706
707 if (!menu->IsEnabled(id))
708 return;
709
710 wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, -1 );
711 event.SetEventObject( menu );
712
713 if (menu->GetEventHandler()->ProcessEvent(event))
714 return;
715
716 wxWindow *win = menu->GetInvokingWindow();
717 if (win)
718 win->GetEventHandler()->ProcessEvent( event );
719 }
720
721 //-----------------------------------------------------------------------------
722 // wxMenuItem
723 //-----------------------------------------------------------------------------
724
725 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
726
727 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
728 int id,
729 const wxString& name,
730 const wxString& help,
731 wxItemKind kind,
732 wxMenu *subMenu)
733 {
734 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
735 }
736
737 wxMenuItem::wxMenuItem(wxMenu *parentMenu,
738 int id,
739 const wxString& text,
740 const wxString& help,
741 wxItemKind kind,
742 wxMenu *subMenu)
743 : wxMenuItemBase(parentMenu, id, text, help, kind, subMenu)
744 {
745 Init(text);
746 }
747
748 wxMenuItem::wxMenuItem(wxMenu *parentMenu,
749 int id,
750 const wxString& text,
751 const wxString& help,
752 bool isCheckable,
753 wxMenu *subMenu)
754 : wxMenuItemBase(parentMenu, id, text, help,
755 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
756 {
757 Init(text);
758 }
759
760 void wxMenuItem::Init(const wxString& text)
761 {
762 m_labelWidget = (GtkWidget *) NULL;
763 m_menuItem = (GtkWidget *) NULL;
764
765 DoSetText(text);
766 }
767
768 wxMenuItem::~wxMenuItem()
769 {
770 // don't delete menu items, the menus take care of that
771 }
772
773 // return the menu item text without any menu accels
774 /* static */
775 wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
776 {
777 wxString label;
778
779 for ( const wxChar *pc = text.c_str(); *pc; pc++ )
780 {
781 if ( *pc == wxT('_') )
782 {
783 // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx"
784 pc++;
785 label += *pc;
786 continue;
787 }
788
789 #if GTK_CHECK_VERSION(2, 0, 0)
790 if ( *pc == wxT('\\') )
791 {
792 // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx"
793 pc++;
794 label += *pc;
795 continue;
796 }
797 #endif
798
799 label += *pc;
800 }
801
802 return label;
803 }
804
805 void wxMenuItem::SetText( const wxString& str )
806 {
807 // Some optimization to avoid flicker
808 wxString oldLabel = m_text;
809 oldLabel = wxStripMenuCodes(oldLabel.BeforeFirst('\t'));
810 oldLabel.Replace(wxT("_"), wxT(""));
811 wxString label1 = wxStripMenuCodes(str.BeforeFirst('\t'));
812 if (oldLabel == label1)
813 return;
814
815 DoSetText(str);
816
817 if (m_menuItem)
818 {
819 GtkLabel *label;
820 if (m_labelWidget)
821 label = (GtkLabel*) m_labelWidget;
822 else
823 label = GTK_LABEL( GTK_BIN(m_menuItem)->child );
824
825 #if GTK_CHECK_VERSION(2, 0, 0)
826 // We have to imitate item_factory_unescape_label here
827 wxString tmp;
828 for (size_t n = 0; n < m_text.Len(); n++)
829 {
830 if (m_text[n] != wxT('\\'))
831 tmp += m_text[n];
832 }
833
834 gtk_label_set_text_with_mnemonic( GTK_LABEL(label), wxGTK_CONV(tmp) );
835 #else
836 // set new text
837 gtk_label_set( label, wxGTK_CONV( m_text ) );
838
839 // reparse key accel
840 (void)gtk_label_parse_uline (GTK_LABEL(label), wxGTK_CONV(m_text) );
841 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label) );
842 #endif
843 }
844 }
845
846 // it's valid for this function to be called even if m_menuItem == NULL
847 void wxMenuItem::DoSetText( const wxString& str )
848 {
849 // '\t' is the deliminator indicating a hot key
850 m_text.Empty();
851 const wxChar *pc = str;
852 while ( (*pc != wxT('\0')) && (*pc != wxT('\t')) )
853 {
854 if ((*pc == wxT('&')) && (*(pc+1) == wxT('&')))
855 {
856 // "&" is doubled to indicate "&" instead of accelerator
857 ++pc;
858 m_text << wxT('&');
859 }
860 else if (*pc == wxT('&'))
861 {
862 m_text << wxT('_');
863 }
864 #if GTK_CHECK_VERSION(2, 0, 0)
865 else if ( *pc == wxT('_') ) // escape underscores
866 {
867 // m_text << wxT("__"); doesn't work
868 }
869 else if (*pc == wxT('/')) // we have to escape slashes
870 {
871 m_text << wxT("\\/");
872 }
873 else if (*pc == wxT('\\')) // we have to double backslashes
874 {
875 m_text << wxT("\\\\");
876 }
877 #else
878 else if ( *pc == wxT('_') ) // escape underscores
879 {
880 m_text << wxT("__");
881 }
882 else if (*pc == wxT('/')) /* we have to filter out slashes ... */
883 {
884 m_text << wxT('\\'); /* ... and replace them with back slashes */
885 }
886 #endif
887 else {
888 m_text << *pc;
889 }
890 ++pc;
891 }
892
893 m_hotKey = wxT("");
894
895 if(*pc == wxT('\t'))
896 {
897 pc++;
898 m_hotKey = pc;
899 }
900 }
901
902 #if wxUSE_ACCEL
903
904 wxAcceleratorEntry *wxMenuItem::GetAccel() const
905 {
906 if ( !GetHotKey() )
907 {
908 // nothing
909 return (wxAcceleratorEntry *)NULL;
910 }
911
912 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
913 wxString label;
914 label << wxT('\t') << GetHotKey();
915
916 return wxGetAccelFromString(label);
917 }
918
919 #endif // wxUSE_ACCEL
920
921 void wxMenuItem::Check( bool check )
922 {
923 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
924
925 if (check == m_isChecked)
926 return;
927
928 wxMenuItemBase::Check( check );
929
930 switch ( GetKind() )
931 {
932 case wxITEM_CHECK:
933 case wxITEM_RADIO:
934 gtk_check_menu_item_set_state( (GtkCheckMenuItem*)m_menuItem, (gint)check );
935 break;
936
937 default:
938 wxFAIL_MSG( _T("can't check this item") );
939 }
940 }
941
942 void wxMenuItem::Enable( bool enable )
943 {
944 wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
945
946 gtk_widget_set_sensitive( m_menuItem, enable );
947 wxMenuItemBase::Enable( enable );
948 }
949
950 bool wxMenuItem::IsChecked() const
951 {
952 wxCHECK_MSG( m_menuItem, FALSE, wxT("invalid menu item") );
953
954 wxCHECK_MSG( IsCheckable(), FALSE,
955 wxT("can't get state of uncheckable item!") );
956
957 return ((GtkCheckMenuItem*)m_menuItem)->active != 0;
958 }
959
960 wxString wxMenuItem::GetFactoryPath() const
961 {
962 /* in order to get the pointer to the item we need the item text
963 _without_ underscores */
964 wxString path( wxT("<main>/") );
965
966 for ( const wxChar *pc = m_text.c_str(); *pc; pc++ )
967 {
968 if ( *pc == wxT('_') )
969 {
970 // remove '_' unconditionally
971 continue;
972 }
973
974 path += *pc;
975 }
976
977 return path;
978 }
979
980 //-----------------------------------------------------------------------------
981 // wxMenu
982 //-----------------------------------------------------------------------------
983
984 IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler)
985
986 void wxMenu::Init()
987 {
988 m_accel = gtk_accel_group_new();
989 m_factory = gtk_item_factory_new( GTK_TYPE_MENU, "<main>", m_accel );
990 m_menu = gtk_item_factory_get_widget( m_factory, "<main>" );
991
992 m_owner = (GtkWidget*) NULL;
993
994 /* Tearoffs are entries, just like separators. So if we want this
995 menu to be a tear-off one, we just append a tearoff entry
996 immediately. */
997 if(m_style & wxMENU_TEAROFF)
998 {
999 GtkItemFactoryEntry entry;
1000 entry.path = (char *)"/tearoff";
1001 entry.callback = (GtkItemFactoryCallback) NULL;
1002 entry.callback_action = 0;
1003 entry.item_type = (char *)"<Tearoff>";
1004 entry.accelerator = (gchar*) NULL;
1005 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
1006 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
1007 }
1008
1009 // append the title as the very first entry if we have it
1010 if ( !!m_title )
1011 {
1012 Append(-2, m_title);
1013 AppendSeparator();
1014 }
1015 }
1016
1017 wxMenu::~wxMenu()
1018 {
1019 m_items.Clear();
1020
1021 gtk_widget_destroy( m_menu );
1022
1023 gtk_object_unref( GTK_OBJECT(m_factory) );
1024 }
1025
1026 bool wxMenu::GtkAppend(wxMenuItem *mitem)
1027 {
1028 GtkWidget *menuItem;
1029
1030 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1031 bool appended = FALSE;
1032 #endif
1033
1034 // does this item terminate the current radio group?
1035 bool endOfRadioGroup = TRUE;
1036
1037 if ( mitem->IsSeparator() )
1038 {
1039 GtkItemFactoryEntry entry;
1040 entry.path = (char *)"/sep";
1041 entry.callback = (GtkItemFactoryCallback) NULL;
1042 entry.callback_action = 0;
1043 entry.item_type = (char *)"<Separator>";
1044 entry.accelerator = (gchar*) NULL;
1045
1046 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
1047
1048 /* this will be wrong for more than one separator. do we care? */
1049 menuItem = gtk_item_factory_get_widget( m_factory, "<main>/sep" );
1050
1051 // we might have a separator inside a radio group
1052 endOfRadioGroup = FALSE;
1053 }
1054 else if ( mitem->IsSubMenu() )
1055 {
1056 /* text has "_" instead of "&" after mitem->SetText() */
1057 wxString text( mitem->GetText() );
1058
1059 /* local buffer in multibyte form */
1060 char buf[200];
1061 strcpy( buf, "/" );
1062 strcat( buf, wxGTK_CONV( text ) );
1063
1064 GtkItemFactoryEntry entry;
1065 entry.path = buf;
1066 entry.callback = (GtkItemFactoryCallback) 0;
1067 entry.callback_action = 0;
1068 entry.item_type = (char *)"<Branch>";
1069 entry.accelerator = (gchar*) NULL;
1070
1071 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
1072
1073 wxString path( mitem->GetFactoryPath() );
1074 menuItem = gtk_item_factory_get_item( m_factory, wxGTK_CONV( path ) );
1075
1076 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), mitem->GetSubMenu()->m_menu );
1077
1078 // if adding a submenu to a menu already existing in the menu bar, we
1079 // must set invoking window to allow processing events from this
1080 // submenu
1081 if ( m_invokingWindow )
1082 wxMenubarSetInvokingWindow(mitem->GetSubMenu(), m_invokingWindow);
1083 }
1084 #ifdef USE_MENU_BITMAPS
1085 else if (mitem->GetBitmap().Ok()) // An item with bitmap
1086 {
1087 wxString text( mitem->GetText() );
1088 const wxBitmap *bitmap = &mitem->GetBitmap();
1089
1090 menuItem = gtk_pixmap_menu_item_new ();
1091 GtkWidget *label = gtk_accel_label_new ( wxGTK_CONV( text ) );
1092 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1093 gtk_container_add (GTK_CONTAINER (menuItem), label);
1094 guint accel_key = gtk_label_parse_uline (GTK_LABEL(label), wxGTK_CONV( text ) );
1095 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), menuItem);
1096 if (accel_key != GDK_VoidSymbol)
1097 {
1098 gtk_widget_add_accelerator (menuItem,
1099 "activate_item",
1100 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu)),
1101 accel_key, 0,
1102 GTK_ACCEL_LOCKED);
1103 }
1104 gtk_widget_show (label);
1105
1106 mitem->SetLabelWidget(label);
1107
1108 GtkWidget* pixmap = gtk_pixmap_new( bitmap->GetPixmap(), bitmap->GetMask() ? bitmap->GetMask()->GetBitmap() : (GdkBitmap* )NULL);
1109 gtk_widget_show(pixmap);
1110 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem ), pixmap);
1111
1112 gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
1113 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
1114 (gpointer)this );
1115
1116 gtk_menu_append( GTK_MENU(m_menu), menuItem );
1117 gtk_widget_show( menuItem );
1118
1119 appended = TRUE; // We've done this, don't do it again
1120 }
1121 #endif // USE_MENU_BITMAPS
1122 else // a normal item
1123 {
1124 // text has "_" instead of "&" after mitem->SetText() so don't use it
1125 wxString text( mitem->GetText() );
1126
1127 // buffers containing the menu item path and type in multibyte form
1128 char bufPath[256],
1129 bufType[256];
1130
1131 strcpy( bufPath, "/" );
1132 strncat( bufPath, wxGTK_CONV(text), WXSIZEOF(bufPath) - 2 );
1133 bufPath[WXSIZEOF(bufPath) - 1] = '\0';
1134
1135 GtkItemFactoryEntry entry;
1136 entry.path = bufPath;
1137 entry.callback = (GtkItemFactoryCallback) gtk_menu_clicked_callback;
1138 entry.callback_action = 0;
1139
1140 wxString pathRadio;
1141 const char *item_type;
1142 switch ( mitem->GetKind() )
1143 {
1144 case wxITEM_CHECK:
1145 item_type = "<CheckItem>";
1146 break;
1147
1148 case wxITEM_RADIO:
1149 if ( m_pathLastRadio.empty() )
1150 {
1151 // start of a new radio group
1152 item_type = "<RadioItem>";
1153 wxString tmp( wxGTK_CONV_BACK( bufPath ) );
1154 tmp.Remove(0,1);
1155 m_pathLastRadio = tmp;
1156 }
1157 else // continue the radio group
1158 {
1159 pathRadio = m_pathLastRadio;
1160 pathRadio.Replace(wxT("_"), wxT(""));
1161 pathRadio.Prepend(wxT("<main>/"));
1162
1163 strncpy(bufType, wxGTK_CONV(pathRadio), WXSIZEOF(bufType));
1164 bufType[WXSIZEOF(bufType) - 1] = '\0';
1165 item_type = bufType;
1166 }
1167
1168 // continue the existing radio group, if any
1169 endOfRadioGroup = FALSE;
1170 break;
1171
1172 default:
1173 wxFAIL_MSG( _T("unexpected menu item kind") );
1174 // fall through
1175
1176 case wxITEM_NORMAL:
1177 item_type = "<Item>";
1178 break;
1179 }
1180
1181 entry.item_type = (char *)item_type; // cast needed for GTK+
1182 entry.accelerator = (gchar*) NULL;
1183
1184 #if wxUSE_ACCEL
1185 // due to an apparent bug in GTK+, we have to use a static buffer here -
1186 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1187 // somehow! (VZ)
1188 char s_accel[50]; // should be big enough, we check for overruns
1189 wxString tmp( GetHotKey(*mitem) );
1190 strncpy(s_accel, wxGTK_CONV( tmp ), WXSIZEOF(s_accel));
1191 s_accel[WXSIZEOF(s_accel) - 1] = '\0';
1192 entry.accelerator = s_accel;
1193 #else // !wxUSE_ACCEL
1194 entry.accelerator = (char*) NULL;
1195 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1196
1197 gtk_item_factory_create_item( m_factory, &entry, (gpointer) this, 2 ); /* what is 2 ? */
1198
1199 wxString path( mitem->GetFactoryPath() );
1200 menuItem = gtk_item_factory_get_widget( m_factory, wxGTK_CONV( path ) );
1201
1202 if (!menuItem)
1203 wxLogError( wxT("Wrong menu path: %s\n"), path.c_str() );
1204 }
1205
1206 if ( !mitem->IsSeparator() )
1207 {
1208 wxASSERT_MSG( menuItem, wxT("invalid menuitem") );
1209
1210 gtk_signal_connect( GTK_OBJECT(menuItem), "select",
1211 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback),
1212 (gpointer)this );
1213
1214 gtk_signal_connect( GTK_OBJECT(menuItem), "deselect",
1215 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback),
1216 (gpointer)this );
1217 }
1218
1219 mitem->SetMenuItem(menuItem);
1220
1221 if ( endOfRadioGroup )
1222 {
1223 m_pathLastRadio.clear();
1224 }
1225
1226 return TRUE;
1227 }
1228
1229 bool wxMenu::DoAppend(wxMenuItem *mitem)
1230 {
1231 return GtkAppend(mitem) && wxMenuBase::DoAppend(mitem);
1232 }
1233
1234 bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
1235 {
1236 if ( !wxMenuBase::DoInsert(pos, item) )
1237 return FALSE;
1238
1239 #ifdef __WXGTK12__
1240 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1241 // of version 1.2.6), so we first append the item and then change its
1242 // index
1243 if ( !GtkAppend(item) )
1244 return FALSE;
1245
1246 if ( m_style & wxMENU_TEAROFF )
1247 {
1248 // change the position as the first item is the tear-off marker
1249 pos++;
1250 }
1251
1252 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
1253 gpointer data = g_list_last(menu_shell->children)->data;
1254 menu_shell->children = g_list_remove(menu_shell->children, data);
1255 menu_shell->children = g_list_insert(menu_shell->children, data, pos);
1256
1257 return TRUE;
1258 #else // GTK < 1.2
1259 // this should be easy to do...
1260 wxFAIL_MSG( wxT("not implemented") );
1261
1262 return FALSE;
1263 #endif // GTK 1.2/1.0
1264 }
1265
1266 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
1267 {
1268 if ( !wxMenuBase::DoRemove(item) )
1269 return (wxMenuItem *)NULL;
1270
1271 // TODO: this code doesn't delete the item factory item and this seems
1272 // impossible as of GTK 1.2.6.
1273 gtk_widget_destroy( item->GetMenuItem() );
1274
1275 return item;
1276 }
1277
1278 int wxMenu::FindMenuIdByMenuItem( GtkWidget *menuItem ) const
1279 {
1280 wxNode *node = m_items.First();
1281 while (node)
1282 {
1283 wxMenuItem *item = (wxMenuItem*)node->Data();
1284 if (item->GetMenuItem() == menuItem)
1285 return item->GetId();
1286 node = node->Next();
1287 }
1288
1289 return wxNOT_FOUND;
1290 }
1291
1292 // ----------------------------------------------------------------------------
1293 // helpers
1294 // ----------------------------------------------------------------------------
1295
1296 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1297
1298 static wxString GetHotKey( const wxMenuItem& item )
1299 {
1300 wxString hotkey;
1301
1302 wxAcceleratorEntry *accel = item.GetAccel();
1303 if ( accel )
1304 {
1305 int flags = accel->GetFlags();
1306 if ( flags & wxACCEL_ALT )
1307 hotkey += wxT("<alt>");
1308 if ( flags & wxACCEL_CTRL )
1309 hotkey += wxT("<control>");
1310 if ( flags & wxACCEL_SHIFT )
1311 hotkey += wxT("<shift>");
1312
1313 int code = accel->GetKeyCode();
1314 switch ( code )
1315 {
1316 case WXK_F1:
1317 case WXK_F2:
1318 case WXK_F3:
1319 case WXK_F4:
1320 case WXK_F5:
1321 case WXK_F6:
1322 case WXK_F7:
1323 case WXK_F8:
1324 case WXK_F9:
1325 case WXK_F10:
1326 case WXK_F11:
1327 case WXK_F12:
1328 hotkey << wxT('F') << code - WXK_F1 + 1;
1329 break;
1330
1331 // TODO: we should use gdk_keyval_name() (a.k.a.
1332 // XKeysymToString) here as well as hardcoding the keysym
1333 // names this might be not portable
1334 case WXK_NUMPAD_INSERT:
1335 hotkey << wxT("KP_Insert" );
1336 break;
1337 case WXK_NUMPAD_DELETE:
1338 hotkey << wxT("KP_Delete" );
1339 break;
1340 case WXK_INSERT:
1341 hotkey << wxT("Insert" );
1342 break;
1343 case WXK_DELETE:
1344 hotkey << wxT("Delete" );
1345 break;
1346 case WXK_UP:
1347 hotkey << wxT("Up" );
1348 break;
1349 case WXK_DOWN:
1350 hotkey << wxT("Down" );
1351 break;
1352 case WXK_PAGEUP:
1353 hotkey << wxT("Prior" );
1354 break;
1355 case WXK_PAGEDOWN:
1356 hotkey << wxT("Next" );
1357 break;
1358 case WXK_LEFT:
1359 hotkey << wxT("Left" );
1360 break;
1361 case WXK_RIGHT:
1362 hotkey << wxT("Right" );
1363 break;
1364 case WXK_HOME:
1365 hotkey << wxT("Home" );
1366 break;
1367 case WXK_END:
1368 hotkey << wxT("End" );
1369 break;
1370 case WXK_RETURN:
1371 hotkey << wxT("Return" );
1372 break;
1373
1374 // if there are any other keys wxGetAccelFromString() may
1375 // return, we should process them here
1376
1377 default:
1378 if ( code < 127 )
1379 {
1380 wxString name = wxGTK_CONV_BACK( gdk_keyval_name((guint)code) );
1381 if ( name )
1382 {
1383 hotkey << name;
1384 break;
1385 }
1386 }
1387
1388 wxFAIL_MSG( wxT("unknown keyboard accel") );
1389 }
1390
1391 delete accel;
1392 }
1393
1394 return hotkey;
1395 }
1396
1397 #endif // wxUSE_ACCEL
1398
1399
1400 //-----------------------------------------------------------------------------
1401 // substitute for missing GtkPixmapMenuItem
1402 //-----------------------------------------------------------------------------
1403
1404 #ifdef USE_MENU_BITMAPS
1405
1406 /*
1407 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1408 * All rights reserved.
1409 *
1410 * This file is part of the Gnome Library.
1411 *
1412 * The Gnome Library is free software; you can redistribute it and/or
1413 * modify it under the terms of the GNU Library General Public License as
1414 * published by the Free Software Foundation; either version 2 of the
1415 * License, or (at your option) any later version.
1416 *
1417 * The Gnome Library is distributed in the hope that it will be useful,
1418 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1419 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1420 * Library General Public License for more details.
1421 *
1422 * You should have received a copy of the GNU Library General Public
1423 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1424 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1425 * Boston, MA 02111-1307, USA.
1426 */
1427 /*
1428 @NOTATION@
1429 */
1430
1431 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1432
1433 #include <gtk/gtkaccellabel.h>
1434 #include <gtk/gtksignal.h>
1435 #include <gtk/gtkmenuitem.h>
1436 #include <gtk/gtkmenu.h>
1437 #include <gtk/gtkcontainer.h>
1438
1439 extern "C"
1440 {
1441
1442 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass *klass);
1443 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem *menu_item);
1444 static void gtk_pixmap_menu_item_draw (GtkWidget *widget,
1445 GdkRectangle *area);
1446 static gint gtk_pixmap_menu_item_expose (GtkWidget *widget,
1447 GdkEventExpose *event);
1448
1449 /* we must override the following functions */
1450
1451 static void gtk_pixmap_menu_item_map (GtkWidget *widget);
1452 static void gtk_pixmap_menu_item_size_allocate (GtkWidget *widget,
1453 GtkAllocation *allocation);
1454 static void gtk_pixmap_menu_item_forall (GtkContainer *container,
1455 gboolean include_internals,
1456 GtkCallback callback,
1457 gpointer callback_data);
1458 static void gtk_pixmap_menu_item_size_request (GtkWidget *widget,
1459 GtkRequisition *requisition);
1460 static void gtk_pixmap_menu_item_remove (GtkContainer *container,
1461 GtkWidget *child);
1462
1463 static void changed_have_pixmap_status (GtkPixmapMenuItem *menu_item);
1464
1465 static GtkMenuItemClass *parent_class = NULL;
1466
1467 }
1468
1469 #define BORDER_SPACING 3
1470 #define PMAP_WIDTH 20
1471
1472 GtkType
1473 gtk_pixmap_menu_item_get_type (void)
1474 {
1475 static GtkType pixmap_menu_item_type = 0;
1476
1477 if (!pixmap_menu_item_type)
1478 {
1479 GtkTypeInfo pixmap_menu_item_info =
1480 {
1481 (char *)"GtkPixmapMenuItem",
1482 sizeof (GtkPixmapMenuItem),
1483 sizeof (GtkPixmapMenuItemClass),
1484 (GtkClassInitFunc) gtk_pixmap_menu_item_class_init,
1485 (GtkObjectInitFunc) gtk_pixmap_menu_item_init,
1486 /* reserved_1 */ NULL,
1487 /* reserved_2 */ NULL,
1488 (GtkClassInitFunc) NULL,
1489 };
1490
1491 pixmap_menu_item_type = gtk_type_unique (gtk_menu_item_get_type (),
1492 &pixmap_menu_item_info);
1493 }
1494
1495 return pixmap_menu_item_type;
1496 }
1497
1498 /**
1499 * gtk_pixmap_menu_item_new
1500 *
1501 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1502 * to set the pixmap wich is displayed at the left side.
1503 *
1504 * Returns:
1505 * &GtkWidget pointer to new menu item
1506 **/
1507
1508 GtkWidget*
1509 gtk_pixmap_menu_item_new (void)
1510 {
1511 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1512 }
1513
1514 static void
1515 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass *klass)
1516 {
1517 GtkObjectClass *object_class;
1518 GtkWidgetClass *widget_class;
1519 GtkMenuItemClass *menu_item_class;
1520 GtkContainerClass *container_class;
1521
1522 object_class = (GtkObjectClass*) klass;
1523 widget_class = (GtkWidgetClass*) klass;
1524 menu_item_class = (GtkMenuItemClass*) klass;
1525 container_class = (GtkContainerClass*) klass;
1526
1527 parent_class = (GtkMenuItemClass*) gtk_type_class (gtk_menu_item_get_type ());
1528
1529 widget_class->draw = gtk_pixmap_menu_item_draw;
1530 widget_class->expose_event = gtk_pixmap_menu_item_expose;
1531 widget_class->map = gtk_pixmap_menu_item_map;
1532 widget_class->size_allocate = gtk_pixmap_menu_item_size_allocate;
1533 widget_class->size_request = gtk_pixmap_menu_item_size_request;
1534
1535 container_class->forall = gtk_pixmap_menu_item_forall;
1536 container_class->remove = gtk_pixmap_menu_item_remove;
1537
1538 klass->orig_toggle_size = menu_item_class->toggle_size;
1539 klass->have_pixmap_count = 0;
1540 }
1541
1542 static void
1543 gtk_pixmap_menu_item_init (GtkPixmapMenuItem *menu_item)
1544 {
1545 GtkMenuItem *mi;
1546
1547 mi = GTK_MENU_ITEM (menu_item);
1548
1549 menu_item->pixmap = NULL;
1550 }
1551
1552 static void
1553 gtk_pixmap_menu_item_draw (GtkWidget *widget,
1554 GdkRectangle *area)
1555 {
1556 g_return_if_fail (widget != NULL);
1557 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget));
1558 g_return_if_fail (area != NULL);
1559
1560 if (GTK_WIDGET_CLASS (parent_class)->draw)
1561 (* GTK_WIDGET_CLASS (parent_class)->draw) (widget, area);
1562
1563 if (GTK_WIDGET_DRAWABLE (widget) &&
1564 GTK_PIXMAP_MENU_ITEM(widget)->pixmap) {
1565 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget)->pixmap),NULL);
1566 }
1567 }
1568
1569 static gint
1570 gtk_pixmap_menu_item_expose (GtkWidget *widget,
1571 GdkEventExpose *event)
1572 {
1573 g_return_val_if_fail (widget != NULL, FALSE);
1574 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget), FALSE);
1575 g_return_val_if_fail (event != NULL, FALSE);
1576
1577 if (GTK_WIDGET_CLASS (parent_class)->expose_event)
1578 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
1579
1580 if (GTK_WIDGET_DRAWABLE (widget) &&
1581 GTK_PIXMAP_MENU_ITEM(widget)->pixmap) {
1582 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget)->pixmap),NULL);
1583 }
1584
1585 return FALSE;
1586 }
1587
1588 /**
1589 * gtk_pixmap_menu_item_set_pixmap
1590 * @menu_item: Pointer to the pixmap menu item
1591 * @pixmap: Pointer to a pixmap widget
1592 *
1593 * Set the pixmap of the menu item.
1594 *
1595 **/
1596
1597 void
1598 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem *menu_item,
1599 GtkWidget *pixmap)
1600 {
1601 g_return_if_fail (menu_item != NULL);
1602 g_return_if_fail (pixmap != NULL);
1603 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item));
1604 g_return_if_fail (GTK_IS_WIDGET (pixmap));
1605 g_return_if_fail (menu_item->pixmap == NULL);
1606
1607 gtk_widget_set_parent (pixmap, GTK_WIDGET (menu_item));
1608 menu_item->pixmap = pixmap;
1609
1610 if (GTK_WIDGET_REALIZED (pixmap->parent) &&
1611 !GTK_WIDGET_REALIZED (pixmap))
1612 gtk_widget_realize (pixmap);
1613
1614 if (GTK_WIDGET_VISIBLE (pixmap->parent)) {
1615 if (GTK_WIDGET_MAPPED (pixmap->parent) &&
1616 GTK_WIDGET_VISIBLE(pixmap) &&
1617 !GTK_WIDGET_MAPPED (pixmap))
1618 gtk_widget_map (pixmap);
1619 }
1620
1621 changed_have_pixmap_status(menu_item);
1622
1623 if (GTK_WIDGET_VISIBLE (pixmap) && GTK_WIDGET_VISIBLE (menu_item))
1624 gtk_widget_queue_resize (pixmap);
1625 }
1626
1627 static void
1628 gtk_pixmap_menu_item_map (GtkWidget *widget)
1629 {
1630 GtkPixmapMenuItem *menu_item;
1631
1632 g_return_if_fail (widget != NULL);
1633 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget));
1634
1635 menu_item = GTK_PIXMAP_MENU_ITEM(widget);
1636
1637 GTK_WIDGET_CLASS(parent_class)->map(widget);
1638
1639 if (menu_item->pixmap &&
1640 GTK_WIDGET_VISIBLE (menu_item->pixmap) &&
1641 !GTK_WIDGET_MAPPED (menu_item->pixmap))
1642 gtk_widget_map (menu_item->pixmap);
1643 }
1644
1645 static void
1646 gtk_pixmap_menu_item_size_allocate (GtkWidget *widget,
1647 GtkAllocation *allocation)
1648 {
1649 GtkPixmapMenuItem *pmenu_item;
1650
1651 pmenu_item = GTK_PIXMAP_MENU_ITEM(widget);
1652
1653 if (pmenu_item->pixmap && GTK_WIDGET_VISIBLE(pmenu_item))
1654 {
1655 GtkAllocation child_allocation;
1656 int border_width;
1657
1658 border_width = GTK_CONTAINER (widget)->border_width;
1659
1660 child_allocation.width = pmenu_item->pixmap->requisition.width;
1661 child_allocation.height = pmenu_item->pixmap->requisition.height;
1662 child_allocation.x = border_width + BORDER_SPACING;
1663 child_allocation.y = (border_width + BORDER_SPACING
1664 + (((allocation->height - child_allocation.height) - child_allocation.x)
1665 / 2)); /* center pixmaps vertically */
1666 gtk_widget_size_allocate (pmenu_item->pixmap, &child_allocation);
1667 }
1668
1669 if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
1670 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
1671 }
1672
1673 static void
1674 gtk_pixmap_menu_item_forall (GtkContainer *container,
1675 gboolean include_internals,
1676 GtkCallback callback,
1677 gpointer callback_data)
1678 {
1679 GtkPixmapMenuItem *menu_item;
1680
1681 g_return_if_fail (container != NULL);
1682 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container));
1683 g_return_if_fail (callback != NULL);
1684
1685 menu_item = GTK_PIXMAP_MENU_ITEM (container);
1686
1687 if (menu_item->pixmap)
1688 (* callback) (menu_item->pixmap, callback_data);
1689
1690 GTK_CONTAINER_CLASS(parent_class)->forall(container,include_internals,
1691 callback,callback_data);
1692 }
1693
1694 static void
1695 gtk_pixmap_menu_item_size_request (GtkWidget *widget,
1696 GtkRequisition *requisition)
1697 {
1698 GtkPixmapMenuItem *menu_item;
1699 GtkRequisition req = {0, 0};
1700
1701 g_return_if_fail (widget != NULL);
1702 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1703 g_return_if_fail (requisition != NULL);
1704
1705 GTK_WIDGET_CLASS(parent_class)->size_request(widget,requisition);
1706
1707 menu_item = GTK_PIXMAP_MENU_ITEM (widget);
1708
1709 if (menu_item->pixmap)
1710 gtk_widget_size_request(menu_item->pixmap, &req);
1711
1712 requisition->height = MAX(req.height + GTK_CONTAINER(widget)->border_width + BORDER_SPACING, (unsigned int) requisition->height);
1713 requisition->width += (req.width + GTK_CONTAINER(widget)->border_width + BORDER_SPACING);
1714 }
1715
1716 static void
1717 gtk_pixmap_menu_item_remove (GtkContainer *container,
1718 GtkWidget *child)
1719 {
1720 GtkBin *bin;
1721 gboolean widget_was_visible;
1722
1723 g_return_if_fail (container != NULL);
1724 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container));
1725 g_return_if_fail (child != NULL);
1726 g_return_if_fail (GTK_IS_WIDGET (child));
1727
1728 bin = GTK_BIN (container);
1729 g_return_if_fail ((bin->child == child ||
1730 (GTK_PIXMAP_MENU_ITEM(container)->pixmap == child)));
1731
1732 widget_was_visible = GTK_WIDGET_VISIBLE (child);
1733
1734 gtk_widget_unparent (child);
1735 if (bin->child == child)
1736 bin->child = NULL;
1737 else {
1738 GTK_PIXMAP_MENU_ITEM(container)->pixmap = NULL;
1739 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container));
1740 }
1741
1742 if (widget_was_visible)
1743 gtk_widget_queue_resize (GTK_WIDGET (container));
1744 }
1745
1746
1747 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1748 static void
1749 changed_have_pixmap_status (GtkPixmapMenuItem *menu_item)
1750 {
1751 if (menu_item->pixmap != NULL) {
1752 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count += 1;
1753
1754 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count == 1) {
1755 /* Install pixmap toggle size */
1756 GTK_MENU_ITEM_GET_CLASS(menu_item)->toggle_size = MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->orig_toggle_size, PMAP_WIDTH);
1757 }
1758 } else {
1759 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count -= 1;
1760
1761 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->have_pixmap_count == 0) {
1762 /* Install normal toggle size */
1763 GTK_MENU_ITEM_GET_CLASS(menu_item)->toggle_size = GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item)->orig_toggle_size;
1764 }
1765 }
1766
1767 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1768 whenever the klass->toggle_size changes; but by doing it anytime
1769 this function is called, we get the same effect, just because of
1770 how the preferences option to show pixmaps works. Bogus, broken.
1771 */
1772 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item)))
1773 gtk_widget_queue_resize(GTK_WIDGET(menu_item));
1774 }
1775
1776 #endif // USE_MENU_BITMAPS
1777