-wxMenu::wxMenu( const wxString &title )
-{
- m_title = title;
- m_items.DeleteContents( TRUE );
- m_invokingWindow = NULL;
- m_menu = gtk_menu_new(); // Do not show!
-};
-
-void wxMenu::AppendSeparator()
-{
- wxMenuItem *mitem = new wxMenuItem();
- mitem->SetId(ID_SEPARATOR);
-
- GtkWidget *menuItem = gtk_menu_item_new();
- gtk_menu_append( GTK_MENU(m_menu), menuItem );
- gtk_widget_show( menuItem );
- mitem->SetMenuItem(menuItem);
- m_items.Append( mitem );
-};
-
-void wxMenu::Append( int id, const wxString &item, const wxString &helpStr, bool checkable )
-{
- wxMenuItem *mitem = new wxMenuItem();
- mitem->SetId(id);
- mitem->SetText(item);
- mitem->SetHelpString(helpStr);
- mitem->SetCheckable(checkable);
- const char *text = mitem->GetText();
- GtkWidget *menuItem = checkable ? gtk_check_menu_item_new_with_label(text)
- : gtk_menu_item_new_with_label(text);
- mitem->SetMenuItem(menuItem);
-
- gtk_signal_connect( GTK_OBJECT(menuItem), "activate",
- GTK_SIGNAL_FUNC(gtk_menu_clicked_callback),
- (gpointer*)this );
-
- gtk_menu_append( GTK_MENU(m_menu), menuItem );
- gtk_widget_show( menuItem );
- m_items.Append( mitem );
-};
-
-void wxMenu::Append( int id, const wxString &text, wxMenu *subMenu, const wxString &helpStr )
-{
- wxMenuItem *mitem = new wxMenuItem();
- mitem->SetId(id);
- mitem->SetText(text);
-
- GtkWidget *menuItem = gtk_menu_item_new_with_label(mitem->GetText());
- mitem->SetHelpString(helpStr);
- mitem->SetMenuItem(menuItem);
- mitem->SetSubMenu(subMenu);
-
- gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), subMenu->m_menu );
- gtk_menu_append( GTK_MENU(m_menu), menuItem );
- gtk_widget_show( menuItem );
- m_items.Append( mitem );
-};
-
-int wxMenu::FindItem( const wxString itemString ) const
-{
- wxString s( itemString );
-
- int pos;
- do {
- pos = s.First( '&' );
- if (pos != -1) s.Remove( pos, 1 );
- } while (pos != -1);
-
- wxNode *node = m_items.First();
- while (node)
- {
- wxMenuItem *item = (wxMenuItem*)node->Data();
- if (item->GetText() == s)
- return item->GetId();
- node = node->Next();
- };
-
- return -1;
-};
-
-void wxMenu::Enable( int id, bool enable )
-{
- wxMenuItem *item = FindItem(id);
- if ( item )
- item->Enable(enable);
-};
-
-bool wxMenu::IsEnabled( int id ) const
-{
- wxMenuItem *item = FindItem(id);
- if ( item )
- return item->IsEnabled();
- else
- return FALSE;
-};
-
-void wxMenu::Check( int id, bool enable )
-{
- wxMenuItem *item = FindItem(id);
- if ( item )
- item->Check(enable);
-};
-
-bool wxMenu::IsChecked( int id ) const
-{
- wxMenuItem *item = FindItem(id);
- if ( item )
- return item->IsChecked();
- else
- return FALSE;
-};
-
-void wxMenu::SetLabel( int id, const wxString &label )
-{
- wxMenuItem *item = FindItem(id);
- if ( item )
- item->SetText(label);
-};
+void wxMenu::Init()
+{
+ m_accel = gtk_accel_group_new();
+ m_menu = gtk_menu_new();
+ // NB: keep reference to the menu so that it is not destroyed behind
+ // our back by GTK+ e.g. when it is removed from menubar:
+ gtk_widget_ref(m_menu);
+
+ m_owner = (GtkWidget*) NULL;
+
+ // Tearoffs are entries, just like separators. So if we want this
+ // menu to be a tear-off one, we just append a tearoff entry
+ // immediately.
+ if ( m_style & wxMENU_TEAROFF )
+ {
+ GtkWidget *tearoff = gtk_tearoff_menu_item_new();
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_menu), tearoff);
+ }
+
+ m_prevRadio = NULL;
+
+ // append the title as the very first entry if we have it
+ if ( !m_title.empty() )
+ {
+ Append(wxGTK_TITLE_ID, m_title);
+ AppendSeparator();
+ }
+}
+
+wxMenu::~wxMenu()
+{
+ if ( GTK_IS_WIDGET( m_menu ))
+ {
+ // see wxMenu::Init
+ gtk_widget_unref( m_menu );
+ g_object_unref( m_accel );
+
+ // if the menu is inserted in another menu at this time, there was
+ // one more reference to it:
+ if ( m_owner )
+ gtk_widget_destroy( m_menu );
+ }
+
+ // This must come after we release GTK resources above. Otherwise, GTK will
+ // give warnings/errors when attempting to free accelerator resources from
+ // child items that just were destroyed (the m_menu widget can contain
+ // references to accelerators in child items. Problem detected when removing
+ // a menu from a wxMenuBar, and the removed menu had submenus with accelerators.)
+ WX_CLEAR_LIST(wxMenuItemList, m_items);
+}
+
+void wxMenu::SetLayoutDirection(const wxLayoutDirection dir)
+{
+ if ( m_owner )
+ wxWindow::GTKSetLayout(m_owner, dir);
+ //else: will be called later by wxMenuBar again
+}
+
+wxLayoutDirection wxMenu::GetLayoutDirection() const
+{
+ return wxWindow::GTKGetLayout(m_owner);
+}
+
+bool wxMenu::GtkAppend(wxMenuItem *mitem, int pos)
+{
+ GtkWidget *menuItem;
+
+ // cache some data used later
+ wxString text = mitem->wxMenuItemBase::GetItemLabel();
+ int id = mitem->GetId();
+ bool isstock = wxIsStockID(id);
+ const char *stockid = NULL;
+ if (isstock)
+ stockid = wxGetStockGtkID(mitem->GetId());
+
+ // stock menu items can have an empty label
+ if (text.IsEmpty() && !mitem->IsSeparator())
+ {
+ wxASSERT_MSG(isstock, wxT("A non-stock menu item with an empty label?"));
+ text = wxGetStockLabel(id);
+
+ // need & => _ conversion
+ text = wxMenuItem::GTKProcessMenuItemLabel(text, NULL);
+ }
+
+ if ( mitem->IsSeparator() )
+ {
+ menuItem = gtk_separator_menu_item_new();
+ }
+ else if ( mitem->GetBitmap().Ok() ||
+ (mitem->GetKind() == wxITEM_NORMAL && isstock) )
+ {
+ wxBitmap bitmap(mitem->GetBitmap());
+
+ menuItem = gtk_image_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
+
+ GtkWidget *image;
+ if ( !bitmap.Ok() )
+ {
+ // use stock bitmap for this item if available on the assumption
+ // that it never hurts to follow GTK+ conventions more closely
+ image = stockid ? gtk_image_new_from_stock(stockid, GTK_ICON_SIZE_MENU)
+ : NULL;
+ }
+ else // we have a custom bitmap
+ {
+ wxASSERT_MSG( mitem->GetKind() == wxITEM_NORMAL,
+ _T("only normal menu items can have bitmaps") );
+
+ if ( bitmap.HasPixbuf() )
+ {
+ image = gtk_image_new_from_pixbuf(bitmap.GetPixbuf());
+ }
+ else
+ {
+ GdkPixmap *gdk_pixmap = bitmap.GetPixmap();
+ GdkBitmap *gdk_bitmap = bitmap.GetMask() ?
+ bitmap.GetMask()->GetBitmap() :
+ (GdkBitmap*) NULL;
+ image = gtk_image_new_from_pixmap( gdk_pixmap, gdk_bitmap );
+ }
+ }
+
+ if ( image )
+ {
+ gtk_widget_show(image);
+
+ gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(menuItem), image );
+ }
+
+ m_prevRadio = NULL;
+ }
+ else // a normal item
+ {
+ // NB: 'text' variable has "_" instead of "&" after mitem->SetItemLabel()
+ // so don't use it
+
+ switch ( mitem->GetKind() )
+ {
+ case wxITEM_CHECK:
+ {
+ menuItem = gtk_check_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
+ m_prevRadio = NULL;
+ break;
+ }
+
+ case wxITEM_RADIO:
+ {
+ GSList *group = NULL;
+ if ( m_prevRadio == NULL )
+ {
+ // start of a new radio group
+ m_prevRadio = menuItem =
+ gtk_radio_menu_item_new_with_mnemonic( group, wxGTK_CONV_SYS( text ) );
+ }
+ else // continue the radio group
+ {
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (m_prevRadio));
+ m_prevRadio = menuItem =
+ gtk_radio_menu_item_new_with_mnemonic( group, wxGTK_CONV_SYS( text ) );
+ }
+ break;
+ }
+
+ default:
+ wxFAIL_MSG( _T("unexpected menu item kind") );
+ // fall through
+
+ case wxITEM_NORMAL:
+ {
+ menuItem = gtk_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text ) );
+ m_prevRadio = NULL;
+ break;
+ }
+ }
+
+ }
+
+#if wxUSE_ACCEL
+ guint accel_key;
+ GdkModifierType accel_mods;
+ wxCharBuffer buf = wxGTK_CONV_SYS( GetGtkHotKey(*mitem) );
+
+ if (buf[(size_t)0] != '\0')
+ {
+ gtk_accelerator_parse( (const char*) buf, &accel_key, &accel_mods);
+ if (accel_key != 0)
+ {
+ gtk_widget_add_accelerator (menuItem,
+ "activate",
+ m_accel,
+ accel_key,
+ accel_mods,
+ GTK_ACCEL_VISIBLE);
+ }
+ }
+ else if (isstock)
+ {
+ // if the accelerator was taken from a stock ID, just get it back from GTK+ stock
+ if (wxGetStockGtkAccelerator(stockid, &accel_mods, &accel_key))
+ gtk_widget_add_accelerator( menuItem,
+ "activate",
+ m_accel,
+ accel_key,
+ accel_mods,
+ GTK_ACCEL_VISIBLE);
+ }
+#endif // wxUSE_ACCEL
+
+ if (pos == -1)
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_menu), menuItem);
+ else
+ gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), menuItem, pos);
+
+ gtk_widget_show( menuItem );
+
+ if ( !mitem->IsSeparator() )
+ {
+ wxASSERT_MSG( menuItem, wxT("invalid menuitem") );
+
+ g_signal_connect (menuItem, "select",
+ G_CALLBACK (gtk_menu_hilight_callback), this);
+ g_signal_connect (menuItem, "deselect",
+ G_CALLBACK (gtk_menu_nolight_callback), this);
+
+ if ( mitem->IsSubMenu() && mitem->GetKind() != wxITEM_RADIO && mitem->GetKind() != wxITEM_CHECK )
+ {
+ gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), mitem->GetSubMenu()->m_menu );
+
+ gtk_widget_show( mitem->GetSubMenu()->m_menu );
+
+ // if adding a submenu to a menu already existing in the menu bar, we
+ // must set invoking window to allow processing events from this
+ // submenu
+ if ( m_invokingWindow )
+ wxMenubarSetInvokingWindow(mitem->GetSubMenu(), m_invokingWindow);
+ }
+ else
+ {
+ g_signal_connect (menuItem, "activate",
+ G_CALLBACK (gtk_menu_clicked_callback),
+ this);
+ }
+ }
+
+ mitem->SetMenuItem(menuItem);
+
+ if (ms_locked)
+ {
+ // This doesn't even exist!
+ // gtk_widget_lock_accelerators(mitem->GetMenuItem());
+ }
+
+ return true;
+}
+
+wxMenuItem* wxMenu::DoAppend(wxMenuItem *mitem)
+{
+ if (!GtkAppend(mitem))
+ return NULL;
+
+ return wxMenuBase::DoAppend(mitem);
+}
+
+wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
+{
+ if ( !wxMenuBase::DoInsert(pos, item) )
+ return NULL;
+
+ // TODO
+ if ( !GtkAppend(item, (int)pos) )
+ return NULL;
+
+ return item;
+}
+
+wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
+{
+ if ( !wxMenuBase::DoRemove(item) )
+ return NULL;
+
+ GtkWidget * const mitem = item->GetMenuItem();
+ if ( m_prevRadio == mitem )
+ {
+ // deleting an item starts a new radio group (has to as we shouldn't
+ // keep a deleted pointer anyhow)
+ m_prevRadio = NULL;
+ }
+
+ // TODO: this code doesn't delete the item factory item and this seems
+ // impossible as of GTK 1.2.6.
+ gtk_widget_destroy( mitem );
+
+ return item;
+}