X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7bc0ff86728ce5ebecefa7120611ae9a50144c34..ddc8faa9e1f9d7dc99ef11e2294ad33915092024:/src/gtk/menu.cpp diff --git a/src/gtk/menu.cpp b/src/gtk/menu.cpp index 848e2f5ae4..54856e2c7d 100644 --- a/src/gtk/menu.cpp +++ b/src/gtk/menu.cpp @@ -24,7 +24,10 @@ #include "wx/accel.h" #include "wx/stockitem.h" + +#include #include "wx/gtk/private.h" +#include "wx/gtk/private/gtk2-compat.h" #include "wx/gtk/private/mnemonics.h" // we use normal item but with a special id for the menu title @@ -58,7 +61,19 @@ static void DoCommonMenuCallbackCode(wxMenu *menu, wxMenuEvent& event) // wxMenuBar //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxMenuBar,wxWindow) +wxMenuBar::~wxMenuBar() +{ + if (m_widget && IsAttached()) + { + // Work around a probable bug in Ubuntu 12.04 which causes a warning if + // gtk_widget_destroy() is called on a wxMenuBar attached to a frame + GtkWidget* widget = m_widget; + m_focusWidget = + m_widget = NULL; + GTKDisconnect(widget); + g_object_unref(widget); + } +} void wxMenuBar::Init(size_t n, wxMenu *menus[], const wxString titles[], long style) { @@ -93,7 +108,7 @@ void wxMenuBar::Init(size_t n, wxMenu *menus[], const wxString titles[], long st GTKApplyWidgetStyle(); #endif // wxUSE_LIBHILDON || wxUSE_LIBHILDON2/!wxUSE_LIBHILDON && !wxUSE_LIBHILDON2 - g_object_ref(m_widget); + g_object_ref_sink(m_widget); for (size_t i = 0; i < n; ++i ) Append(menus[i], titles[i]); @@ -128,7 +143,7 @@ DetachFromFrame(wxMenu* menu, wxFrame* frame) // Note that wxGetTopLevelParent() is really needed because this frame // can be an MDI child frame which is a fake frame and not a TLW at all GtkWindow * const tlw = GTK_WINDOW(wxGetTopLevelParent(frame)->m_widget); - if (g_slist_find(menu->m_accel->acceleratables, tlw)) + if (g_slist_find(gtk_accel_groups_from_object(G_OBJECT(tlw)), menu->m_accel)) gtk_window_remove_accel_group(tlw, menu->m_accel); } @@ -149,7 +164,7 @@ AttachToFrame(wxMenu* menu, wxFrame* frame) if (menu->m_accel) { GtkWindow * const tlw = GTK_WINDOW(wxGetTopLevelParent(frame)->m_widget); - if (!g_slist_find(menu->m_accel->acceleratables, tlw)) + if (!g_slist_find(gtk_accel_groups_from_object(G_OBJECT(tlw)), menu->m_accel)) gtk_window_add_accel_group(tlw, menu->m_accel); } @@ -232,13 +247,15 @@ void wxMenuBar::Detach() bool wxMenuBar::Append( wxMenu *menu, const wxString &title ) { - if ( !wxMenuBarBase::Append( menu, title ) ) - return false; - - return GtkAppend(menu, title); + if (wxMenuBarBase::Append(menu, title)) + { + GtkAppend(menu, title); + return true; + } + return false; } -bool wxMenuBar::GtkAppend(wxMenu *menu, const wxString& title, int pos) +void wxMenuBar::GtkAppend(wxMenu* menu, const wxString& title, int pos) { menu->SetLayoutDirection(GetLayoutDirection()); @@ -253,7 +270,10 @@ bool wxMenuBar::GtkAppend(wxMenu *menu, const wxString& title, int pos) const wxString str(wxStripMenuCodes(item->GetItemLabel())); if ( item->IsSubMenu() ) - return GtkAppend(item->GetSubMenu(), str, pos); + { + GtkAppend(item->GetSubMenu(), str, pos); + return; + } menu->m_owner = gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str ) ); @@ -284,21 +304,16 @@ bool wxMenuBar::GtkAppend(wxMenu *menu, const wxString& title, int pos) if ( m_menuBarFrame ) AttachToFrame( menu, m_menuBarFrame ); - - return true; } bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title) { - if ( !wxMenuBarBase::Insert(pos, menu, title) ) - return false; - - // TODO - - if ( !GtkAppend(menu, title, (int)pos) ) - return false; - - return true; + if (wxMenuBarBase::Insert(pos, menu, title)) + { + GtkAppend(menu, title, int(pos)); + return true; + } + return false; } wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) @@ -326,7 +341,8 @@ wxMenu *wxMenuBar::Remove(size_t pos) gtk_widget_destroy( menu->m_owner ); menu->m_owner = NULL; - DetachFromFrame( menu, m_menuBarFrame ); + if ( m_menuBarFrame ) + DetachFromFrame( menu, m_menuBarFrame ); return menu; } @@ -418,6 +434,15 @@ void wxMenuBar::EnableTop( size_t pos, bool flag ) gtk_widget_set_sensitive( menu->m_owner, flag ); } +bool wxMenuBar::IsEnabledTop(size_t pos) const +{ + wxMenuList::compatibility_iterator node = m_menus.Item( pos ); + wxCHECK_MSG( node, false, wxS("invalid index in IsEnabledTop") ); + wxMenu* const menu = node->GetData(); + wxCHECK_MSG( menu->m_owner, true, wxS("no menu owner?") ); + return gtk_widget_get_sensitive( menu->m_owner ) != 0; +} + wxString wxMenuBar::GetMenuLabel( size_t pos ) const { wxMenuList::compatibility_iterator node = m_menus.Item( pos ); @@ -442,7 +467,7 @@ void wxMenuBar::SetMenuLabel( size_t pos, const wxString& label ) menu->SetTitle( str ); if (menu->m_owner) - gtk_label_set_text_with_mnemonic( GTK_LABEL( GTK_BIN(menu->m_owner)->child), wxGTK_CONV(str) ); + gtk_label_set_text_with_mnemonic(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu->m_owner))), wxGTK_CONV(str)); } //----------------------------------------------------------------------------- @@ -519,8 +544,6 @@ static void menuitem_deselect(GtkWidget*, wxMenuItem* item) // wxMenuItem //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject) - wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu, int id, const wxString& name, @@ -585,7 +608,7 @@ void wxMenuItem::SetItemLabel( const wxString& str ) void wxMenuItem::SetGtkLabel() { const wxString text = wxConvertMnemonicsToGTK(m_text.BeforeFirst('\t')); - GtkLabel* label = GTK_LABEL(GTK_BIN(m_menuItem)->child); + GtkLabel* label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_menuItem))); gtk_label_set_text_with_mnemonic(label, wxGTK_CONV_SYS(text)); #if wxUSE_ACCEL guint accel_key; @@ -605,7 +628,9 @@ void wxMenuItem::SetBitmap(const wxBitmap& bitmap) if (m_kind == wxITEM_NORMAL) m_bitmap = bitmap; else + { wxFAIL_MSG("only normal menu items can have bitmaps"); + } } void wxMenuItem::Check( bool check ) @@ -644,7 +669,7 @@ bool wxMenuItem::IsChecked() const wxCHECK_MSG( IsCheckable(), false, wxT("can't get state of uncheckable item!") ); - return ((GtkCheckMenuItem*)m_menuItem)->active != 0; + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(m_menuItem)) != 0; } //----------------------------------------------------------------------------- @@ -662,6 +687,13 @@ static void menu_map(GtkWidget*, wxMenu* menu) // "hide" from m_menu static void menu_hide(GtkWidget*, wxMenu* menu) { + // When using Ubuntu Unity desktop environment we get "hide" signal even + // when the window is not shown yet because Unity hides all the menus to + // show them only in the global menu bar. Just ignore this even instead of + // crashing in DoCommonMenuCallbackCode(). + if ( !menu->GetWindow() ) + return; + wxMenuEvent event(wxEVT_MENU_CLOSE, menu->m_popupShown ? -1 : 0, menu); menu->m_popupShown = false; DoCommonMenuCallbackCode(menu, event); @@ -678,8 +710,6 @@ static gboolean can_activate_accel(GtkWidget*, guint, wxMenu* menu) } } -IMPLEMENT_DYNAMIC_CLASS(wxMenu,wxEvtHandler) - void wxMenu::Init() { m_popupShown = false; @@ -688,8 +718,7 @@ void wxMenu::Init() 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: - g_object_ref(m_menu); - gtk_object_sink(GTK_OBJECT(m_menu)); + g_object_ref_sink(m_menu); m_owner = NULL; @@ -703,8 +732,6 @@ void wxMenu::Init() 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() ) { @@ -722,7 +749,8 @@ wxMenu::~wxMenu() // Destroying a menu generates a "hide" signal even if it's not shown // currently, so disconnect it to avoid dummy wxEVT_MENU_CLOSE events // generation. - g_signal_handlers_disconnect_by_func(m_menu, (gpointer)menu_hide, this); + g_signal_handlers_disconnect_matched(m_menu, + GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, this); // see wxMenu::Init g_object_unref(m_menu); @@ -747,16 +775,14 @@ wxLayoutDirection wxMenu::GetLayoutDirection() const return wxWindow::GTKGetLayout(m_owner); } -const wxString wxMenu::GetTitle() const +wxString wxMenu::GetTitle() const { return wxConvertMnemonicsFromGTK(wxMenuBase::GetTitle()); } -bool wxMenu::GtkAppend(wxMenuItem *mitem, int pos) +void wxMenu::GtkAppend(wxMenuItem* mitem, int pos) { GtkWidget *menuItem; - GtkWidget* prevRadio = m_prevRadio; - m_prevRadio = NULL; switch (mitem->GetKind()) { case wxITEM_SEPARATOR: @@ -767,11 +793,44 @@ bool wxMenu::GtkAppend(wxMenuItem *mitem, int pos) break; case wxITEM_RADIO: { + // See if we need to create a new radio group for this item or + // add it to an existing one. + wxMenuItem* radioGroupItem = NULL; + + const size_t numItems = GetMenuItemCount(); + const size_t n = pos == -1 ? numItems - 1 : size_t(pos); + + if (n != 0) + { + wxMenuItem* const itemPrev = FindItemByPosition(n - 1); + if ( itemPrev->GetKind() == wxITEM_RADIO ) + { + // Appending an item after an existing radio item puts + // it into the same radio group. + radioGroupItem = itemPrev; + } + } + + if (radioGroupItem == NULL && n != numItems - 1) + { + wxMenuItem* const itemNext = FindItemByPosition(n + 1); + if ( itemNext->GetKind() == wxITEM_RADIO ) + { + // Inserting an item before an existing radio item + // also puts it into the existing radio group. + radioGroupItem = itemNext; + } + } + GSList* group = NULL; - if (prevRadio) - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(prevRadio)); + if ( radioGroupItem ) + { + group = gtk_radio_menu_item_get_group( + GTK_RADIO_MENU_ITEM(radioGroupItem->GetMenuItem()) + ); + } + menuItem = gtk_radio_menu_item_new_with_label(group, ""); - m_prevRadio = menuItem; } break; default: @@ -826,28 +885,26 @@ bool wxMenu::GtkAppend(wxMenuItem *mitem, int pos) mitem); } } - - return true; } wxMenuItem* wxMenu::DoAppend(wxMenuItem *mitem) { - if (!GtkAppend(mitem)) - return NULL; - - return wxMenuBase::DoAppend(mitem); + if (wxMenuBase::DoAppend(mitem)) + { + GtkAppend(mitem); + return mitem; + } + return NULL; } 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; + if (wxMenuBase::DoInsert(pos, item)) + { + GtkAppend(item, int(pos)); + return item; + } + return NULL; } wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) @@ -856,14 +913,29 @@ wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) return NULL; GtkWidget * const mitem = item->GetMenuItem(); - if ( m_prevRadio == mitem ) + + g_signal_handlers_disconnect_matched(mitem, + GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, item); + +#ifdef __WXGTK3__ + gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), NULL); +#else + if (!gtk_check_version(2,12,0)) { - // deleting an item starts a new radio group (has to as we shouldn't - // keep a deleted pointer anyhow) - m_prevRadio = NULL; + // gtk_menu_item_remove_submenu() is deprecated since 2.12, but + // gtk_menu_item_set_submenu() can now be used with NULL submenu now so + // just do use it. + gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), NULL); } + else // GTK+ < 2.12 + { + // In 2.10 calling gtk_menu_item_set_submenu() with NULL submenu + // results in critical GTK+ error messages so use the old function + // instead. + gtk_menu_item_remove_submenu(GTK_MENU_ITEM(mitem)); + } +#endif - gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), NULL); gtk_widget_destroy(mitem); item->SetMenuItem(NULL); @@ -1174,12 +1246,6 @@ const char *wxGetStockGtkID(wxWindowID id) case wx: \ return gtk; - #if GTK_CHECK_VERSION(2,6,0) - #define STOCKITEM_26(wx,gtk) STOCKITEM(wx,gtk) - #else - #define STOCKITEM_26(wx,gtk) - #endif - #if GTK_CHECK_VERSION(2,8,0) #define STOCKITEM_28(wx,gtk) STOCKITEM(wx,gtk) #else @@ -1195,7 +1261,7 @@ const char *wxGetStockGtkID(wxWindowID id) switch (id) { - STOCKITEM_26(wxID_ABOUT, GTK_STOCK_ABOUT) + STOCKITEM(wxID_ABOUT, GTK_STOCK_ABOUT) STOCKITEM(wxID_ADD, GTK_STOCK_ADD) STOCKITEM(wxID_APPLY, GTK_STOCK_APPLY) STOCKITEM(wxID_BACKWARD, GTK_STOCK_GO_BACK) @@ -1210,10 +1276,10 @@ const char *wxGetStockGtkID(wxWindowID id) STOCKITEM(wxID_CUT, GTK_STOCK_CUT) STOCKITEM(wxID_DELETE, GTK_STOCK_DELETE) STOCKITEM(wxID_DOWN, GTK_STOCK_GO_DOWN) - STOCKITEM_26(wxID_EDIT, GTK_STOCK_EDIT) + STOCKITEM(wxID_EDIT, GTK_STOCK_EDIT) STOCKITEM(wxID_EXECUTE, GTK_STOCK_EXECUTE) STOCKITEM(wxID_EXIT, GTK_STOCK_QUIT) - STOCKITEM_26(wxID_FILE, GTK_STOCK_FILE) + STOCKITEM(wxID_FILE, GTK_STOCK_FILE) STOCKITEM(wxID_FIND, GTK_STOCK_FIND) STOCKITEM(wxID_FIRST, GTK_STOCK_GOTO_FIRST) STOCKITEM(wxID_FLOPPY, GTK_STOCK_FLOPPY)