dolphin/Externals/wxWidgets3/src/gtk/menu.cpp
EmptyChaos 822326eea9 Update wxWidgets to 3.1.0
From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7.

include/wx/defs.h -- __w64 warning disable patch by comex brought forward.

include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code.

src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux.

src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue.

src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text).

Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
2016-06-26 15:25:29 +10:00

1389 lines
42 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/menu.cpp
// Purpose: implementation of wxMenuBar and wxMenu classes for wxGTK
// Author: Robert Roebling
// Copyright: (c) 1998 Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_MENUS
#include "wx/menu.h"
#ifndef WX_PRECOMP
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/dialog.h"
#include "wx/frame.h"
#include "wx/bitmap.h"
#include "wx/app.h"
#endif
#include "wx/accel.h"
#include "wx/stockitem.h"
#include <gtk/gtk.h>
#include "wx/gtk/private.h"
#include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/mnemonics.h"
// Number of currently open modal dialogs, defined in src/gtk/toplevel.cpp.
extern int wxOpenModalDialogsCount;
// we use normal item but with a special id for the menu title
static const int wxGTK_TITLE_ID = -3;
#if wxUSE_ACCEL
static void wxGetGtkAccel(const wxMenuItem*, guint*, GdkModifierType*);
#endif
// Unity hack: under Ubuntu Unity the global menu bar is not affected by a
// modal dialog being shown, so the user can select a menu item before hiding
// the dialog and, in particular, a new instance of the same dialog can be
// shown again, breaking a lot of programs not expecting this.
//
// So explicitly ignore any menu events generated while any modal dialogs
// are opened except for the events generated by a context menu within the
// modal dialog itself that should have a dialog as their invoking window.
static bool IsMenuEventAllowed(wxMenu* menu)
{
if ( wxOpenModalDialogsCount )
{
wxWindow* tlw = wxGetTopLevelParent(menu->GetWindow());
if ( !tlw || !wxDynamicCast(tlw, wxDialog) )
{
// This must be an event from a menu bar of one of the frames.
return false;
}
}
return true;
}
static void DoCommonMenuCallbackCode(wxMenu *menu, wxMenuEvent& event)
{
if ( !IsMenuEventAllowed(menu) )
return;
wxMenu::ProcessMenuEvent(menu, event, menu->GetWindow());
}
// Return the top level menu containing this menu (possibly this menu itself).
static wxMenu* GetRootParentMenu(wxMenu* menu)
{
while ( menu->GetParent() )
menu = menu->GetParent();
return menu;
}
// Call SetGtkLabel() to update the labels of all the items in this items sub
// menu, recursively.
static void UpdateSubMenuItemLabels(wxMenuItem* itemMenu)
{
wxMenu* const menu = itemMenu->GetSubMenu();
wxCHECK_RET( menu, "should only be called for sub menus" );
const wxMenuItemList& items = menu->GetMenuItems();
for ( wxMenuItemList::const_iterator
it = items.begin(); it != items.end(); ++it )
{
wxMenuItem* const item = *it;
if ( !item->IsSeparator() )
{
item->SetGtkLabel();
if ( item->IsSubMenu() )
UpdateSubMenuItemLabels(item);
}
}
}
//-----------------------------------------------------------------------------
// wxMenuBar
//-----------------------------------------------------------------------------
wxMenuBar::~wxMenuBar()
{
}
void wxMenuBar::Init(size_t n, wxMenu *menus[], const wxString titles[], long style)
{
if (!PreCreation( NULL, wxDefaultPosition, wxDefaultSize ) ||
!CreateBase( NULL, -1, wxDefaultPosition, wxDefaultSize, style, wxDefaultValidator, wxT("menubar") ))
{
wxFAIL_MSG( wxT("wxMenuBar creation failed") );
return;
}
m_menubar = gtk_menu_bar_new();
if (style & wxMB_DOCKABLE)
{
m_widget = gtk_handle_box_new();
gtk_container_add(GTK_CONTAINER(m_widget), m_menubar);
gtk_widget_show(m_menubar);
}
else
{
m_widget = m_menubar;
}
PostCreation();
g_object_ref_sink(m_widget);
for (size_t i = 0; i < n; ++i )
Append(menus[i], titles[i]);
}
wxMenuBar::wxMenuBar(size_t n, wxMenu *menus[], const wxString titles[], long style)
{
Init(n, menus, titles, style);
}
wxMenuBar::wxMenuBar(long style)
{
Init(0, NULL, NULL, style);
}
wxMenuBar::wxMenuBar()
{
Init(0, NULL, NULL, 0);
}
// recursive helpers for wxMenuBar::Attach() and Detach(): they are called to
// associate the menus with the frame they belong to or dissociate them from it
namespace
{
// This should be called when detaching menus to ensure that they don't keep
// focus grab, because if they do, they continue getting all GTK+ messages
// which they can't process any more in their (soon to be) unrealized state.
void
EnsureNoGrab(GtkWidget* widget)
{
gtk_widget_hide(widget);
gtk_grab_remove(widget);
}
void
DetachFromFrame(wxMenu* menu, wxFrame* frame)
{
// support for native hot keys
if (menu->m_accel)
{
// 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(gtk_accel_groups_from_object(G_OBJECT(tlw)), menu->m_accel))
gtk_window_remove_accel_group(tlw, menu->m_accel);
}
wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
while (node)
{
wxMenuItem *menuitem = node->GetData();
if (menuitem->IsSubMenu())
DetachFromFrame(menuitem->GetSubMenu(), frame);
node = node->GetNext();
}
EnsureNoGrab(menu->m_menu);
}
void
AttachToFrame(wxMenu* menu, wxFrame* frame)
{
// support for native hot keys
if (menu->m_accel)
{
GtkWindow * const tlw = GTK_WINDOW(wxGetTopLevelParent(frame)->m_widget);
if (!g_slist_find(gtk_accel_groups_from_object(G_OBJECT(tlw)), menu->m_accel))
gtk_window_add_accel_group(tlw, menu->m_accel);
}
wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
while (node)
{
wxMenuItem *menuitem = node->GetData();
if (menuitem->IsSubMenu())
AttachToFrame(menuitem->GetSubMenu(), frame);
node = node->GetNext();
}
}
} // anonymous namespace
void wxMenuBar::SetLayoutDirection(wxLayoutDirection dir)
{
if ( dir == wxLayout_Default )
{
const wxWindow *const frame = GetFrame();
if ( frame )
{
// inherit layout from frame.
dir = frame->GetLayoutDirection();
}
else // use global layout
{
dir = wxTheApp->GetLayoutDirection();
}
}
if ( dir == wxLayout_Default )
return;
GTKSetLayout(m_menubar, dir);
// also set the layout of all menus we already have (new ones will inherit
// the current layout)
for ( wxMenuList::compatibility_iterator node = m_menus.GetFirst();
node;
node = node->GetNext() )
{
wxMenu *const menu = node->GetData();
menu->SetLayoutDirection(dir);
}
}
wxLayoutDirection wxMenuBar::GetLayoutDirection() const
{
return GTKGetLayout(m_menubar);
}
void wxMenuBar::Attach(wxFrame *frame)
{
wxMenuBarBase::Attach(frame);
wxMenuList::compatibility_iterator node = m_menus.GetFirst();
while (node)
{
wxMenu *menu = node->GetData();
AttachToFrame( menu, frame );
node = node->GetNext();
}
SetLayoutDirection(wxLayout_Default);
}
void wxMenuBar::Detach()
{
wxMenuList::compatibility_iterator node = m_menus.GetFirst();
while (node)
{
wxMenu *menu = node->GetData();
DetachFromFrame( menu, m_menuBarFrame );
node = node->GetNext();
}
EnsureNoGrab(m_widget);
wxMenuBarBase::Detach();
}
bool wxMenuBar::Append( wxMenu *menu, const wxString &title )
{
if (wxMenuBarBase::Append(menu, title))
{
GtkAppend(menu, title);
return true;
}
return false;
}
void wxMenuBar::GtkAppend(wxMenu* menu, const wxString& title, int pos)
{
menu->SetLayoutDirection(GetLayoutDirection());
{
// This doesn't have much effect right now.
menu->SetTitle( title );
const wxString str(wxConvertMnemonicsToGTK(title));
// The "m_owner" is the "menu item"
menu->m_owner = gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str ) );
gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu->m_owner), menu->m_menu );
}
g_object_ref(menu->m_owner);
gtk_widget_show( menu->m_owner );
if (pos == -1)
gtk_menu_shell_append( GTK_MENU_SHELL(m_menubar), menu->m_owner );
else
gtk_menu_shell_insert( GTK_MENU_SHELL(m_menubar), menu->m_owner, pos );
if ( m_menuBarFrame )
AttachToFrame( menu, m_menuBarFrame );
}
bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
{
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)
{
// remove the old item and insert a new one
wxMenu *menuOld = Remove(pos);
if ( menuOld && !Insert(pos, menu, title) )
{
return NULL;
}
// either Insert() succeeded or Remove() failed and menuOld is NULL
return menuOld;
}
wxMenu *wxMenuBar::Remove(size_t pos)
{
wxMenu *menu = wxMenuBarBase::Remove(pos);
if ( !menu )
return NULL;
// remove item from menubar before destroying item to avoid spurious
// warnings from Ubuntu libdbusmenu
gtk_container_remove(GTK_CONTAINER(m_menubar), menu->m_owner);
// remove submenu to avoid destroying it when item is destroyed
#ifdef __WXGTK3__
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->m_owner), NULL);
#else
gtk_menu_item_remove_submenu(GTK_MENU_ITEM(menu->m_owner));
#endif
gtk_widget_destroy( menu->m_owner );
g_object_unref(menu->m_owner);
menu->m_owner = NULL;
if ( m_menuBarFrame )
DetachFromFrame( menu, m_menuBarFrame );
return menu;
}
static int FindMenuItemRecursive( const wxMenu *menu, const wxString &menuString, const wxString &itemString )
{
if (wxMenuItem::GetLabelText(menu->GetTitle()) == wxMenuItem::GetLabelText(menuString))
{
int res = menu->FindItem( itemString );
if (res != wxNOT_FOUND)
return res;
}
wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
while (node)
{
wxMenuItem *item = node->GetData();
if (item->IsSubMenu())
return FindMenuItemRecursive(item->GetSubMenu(), menuString, itemString);
node = node->GetNext();
}
return wxNOT_FOUND;
}
int wxMenuBar::FindMenuItem( const wxString &menuString, const wxString &itemString ) const
{
wxMenuList::compatibility_iterator node = m_menus.GetFirst();
while (node)
{
wxMenu *menu = node->GetData();
int res = FindMenuItemRecursive( menu, menuString, itemString);
if (res != -1)
return res;
node = node->GetNext();
}
return wxNOT_FOUND;
}
// Find a wxMenuItem using its id. Recurses down into sub-menus
static wxMenuItem* FindMenuItemByIdRecursive(const wxMenu* menu, int id)
{
wxMenuItem* result = menu->FindChildItem(id);
wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
while ( node && result == NULL )
{
wxMenuItem *item = node->GetData();
if (item->IsSubMenu())
{
result = FindMenuItemByIdRecursive( item->GetSubMenu(), id );
}
node = node->GetNext();
}
return result;
}
wxMenuItem* wxMenuBar::FindItem( int id, wxMenu **menuForItem ) const
{
wxMenuItem* result = 0;
wxMenuList::compatibility_iterator node = m_menus.GetFirst();
while (node && result == 0)
{
wxMenu *menu = node->GetData();
result = FindMenuItemByIdRecursive( menu, id );
node = node->GetNext();
}
if ( menuForItem )
{
*menuForItem = result ? result->GetMenu() : NULL;
}
return result;
}
void wxMenuBar::EnableTop( size_t pos, bool flag )
{
wxMenuList::compatibility_iterator node = m_menus.Item( pos );
wxCHECK_RET( node, wxT("menu not found") );
wxMenu* menu = node->GetData();
if (menu->m_owner)
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 );
wxCHECK_MSG( node, wxT("invalid"), wxT("menu not found") );
wxMenu* menu = node->GetData();
return menu->GetTitle();
}
void wxMenuBar::SetMenuLabel( size_t pos, const wxString& label )
{
wxMenuList::compatibility_iterator node = m_menus.Item( pos );
wxCHECK_RET( node, wxT("menu not found") );
wxMenu* menu = node->GetData();
menu->SetTitle( label );
const wxString str(wxConvertMnemonicsToGTK(label));
if (menu->m_owner)
gtk_label_set_text_with_mnemonic(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu->m_owner))), wxGTK_CONV(str));
}
//-----------------------------------------------------------------------------
// "activate"
//-----------------------------------------------------------------------------
extern "C" {
static void menuitem_activate(GtkWidget*, wxMenuItem* item)
{
if (!item->IsEnabled())
return;
if ( !IsMenuEventAllowed(item->GetMenu()) )
return;
int id = item->GetId();
if (id == wxGTK_TITLE_ID)
{
// ignore events from the menu title
return;
}
if (item->IsCheckable())
{
bool isReallyChecked = item->IsChecked(),
isInternallyChecked = item->wxMenuItemBase::IsChecked();
// ensure that the internal state is always consistent with what is
// shown on the screen
item->wxMenuItemBase::Check(isReallyChecked);
// we must not report the events for the radio button going up nor the
// events resulting from the calls to wxMenuItem::Check()
if ( (item->GetKind() == wxITEM_RADIO && !isReallyChecked) ||
(isInternallyChecked == isReallyChecked) )
{
return;
}
}
wxMenu* menu = item->GetMenu();
menu->SendEvent(id, item->IsCheckable() ? item->IsChecked() : -1);
// A lot of existing code, including any program that closes its main
// window from a menu handler and expects the program to exit -- as our own
// minimal sample -- relies on getting an idle event after a menu event.
// But when using Ubuntu Unity detached menus, we get called from a DBUS
// handler called from g_timeout_dispatch() and Glib doesn't send us any
// idle events after it. So ask explicitly for an idle event to get one.
wxWakeUpIdle();
}
}
//-----------------------------------------------------------------------------
// "select"
//-----------------------------------------------------------------------------
extern "C" {
static void menuitem_select(GtkWidget*, wxMenuItem* item)
{
if (!item->IsEnabled())
return;
wxMenuEvent event(wxEVT_MENU_HIGHLIGHT, item->GetId());
DoCommonMenuCallbackCode(item->GetMenu(), event);
}
}
//-----------------------------------------------------------------------------
// "deselect"
//-----------------------------------------------------------------------------
extern "C" {
static void menuitem_deselect(GtkWidget*, wxMenuItem* item)
{
if (!item->IsEnabled())
return;
wxMenuEvent event( wxEVT_MENU_HIGHLIGHT, -1 );
DoCommonMenuCallbackCode(item->GetMenu(), event);
}
}
//-----------------------------------------------------------------------------
// wxMenuItem
//-----------------------------------------------------------------------------
wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
int id,
const wxString& name,
const wxString& help,
wxItemKind kind,
wxMenu *subMenu)
{
return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
}
wxMenuItem::wxMenuItem(wxMenu *parentMenu,
int id,
const wxString& text,
const wxString& help,
wxItemKind kind,
wxMenu *subMenu)
: wxMenuItemBase(parentMenu, id, text, help, kind, subMenu)
{
m_menuItem = NULL;
}
#if WXWIN_COMPATIBILITY_2_8
wxMenuItem::wxMenuItem(wxMenu *parentMenu,
int id,
const wxString& text,
const wxString& help,
bool isCheckable,
wxMenu *subMenu)
: wxMenuItemBase(parentMenu, id, text, help,
isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
{
m_menuItem = NULL;
}
#endif
wxMenuItem::~wxMenuItem()
{
if (m_menuItem)
g_object_unref(m_menuItem);
// don't delete menu items, the menus take care of that
}
void wxMenuItem::SetMenuItem(GtkWidget* menuItem)
{
if (m_menuItem)
g_object_unref(m_menuItem);
m_menuItem = menuItem;
if (menuItem)
g_object_ref(menuItem);
}
void wxMenuItem::SetItemLabel( const wxString& str )
{
#if wxUSE_ACCEL
if (m_menuItem)
{
// remove old accelerator
guint accel_key;
GdkModifierType accel_mods;
wxGetGtkAccel(this, &accel_key, &accel_mods);
if (accel_key)
{
gtk_widget_remove_accelerator(
m_menuItem, GetRootParentMenu(m_parentMenu)->m_accel, accel_key, accel_mods);
}
}
#endif // wxUSE_ACCEL
wxMenuItemBase::SetItemLabel(str);
if (m_menuItem)
SetGtkLabel();
}
void wxMenuItem::SetGtkLabel()
{
const wxString text = wxConvertMnemonicsToGTK(m_text.BeforeFirst('\t'));
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;
GdkModifierType accel_mods;
wxGetGtkAccel(this, &accel_key, &accel_mods);
if (accel_key)
{
gtk_widget_add_accelerator(
m_menuItem, "activate", GetRootParentMenu(m_parentMenu)->m_accel,
accel_key, accel_mods, GTK_ACCEL_VISIBLE);
}
#endif // wxUSE_ACCEL
}
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 )
{
wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
if (check == m_isChecked)
return;
switch ( GetKind() )
{
case wxITEM_RADIO:
// It doesn't make sense to uncheck a radio item.
if ( !check )
return;
wxFALLTHROUGH;
case wxITEM_CHECK:
wxMenuItemBase::Check( check );
gtk_check_menu_item_set_active( (GtkCheckMenuItem*)m_menuItem, (gint)check );
break;
default:
wxFAIL_MSG( wxT("can't check this item") );
}
}
void wxMenuItem::Enable( bool enable )
{
wxCHECK_RET( m_menuItem, wxT("invalid menu item") );
gtk_widget_set_sensitive( m_menuItem, enable );
wxMenuItemBase::Enable( enable );
}
bool wxMenuItem::IsChecked() const
{
wxCHECK_MSG( m_menuItem, false, wxT("invalid menu item") );
wxCHECK_MSG( IsCheckable(), false,
wxT("can't get state of uncheckable item!") );
return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(m_menuItem)) != 0;
}
//-----------------------------------------------------------------------------
// wxMenu
//-----------------------------------------------------------------------------
extern "C" {
// "map" from m_menu
static void menu_map(GtkWidget*, wxMenu* menu)
{
wxMenuEvent event(wxEVT_MENU_OPEN, menu->m_popupShown ? -1 : 0, menu);
DoCommonMenuCallbackCode(menu, event);
}
// "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);
}
}
// "can_activate_accel" from menu item
extern "C" {
static gboolean can_activate_accel(GtkWidget*, guint, wxMenu* menu)
{
menu->UpdateUI();
// always allow our "activate" handler to be called
return true;
}
}
void wxMenu::Init()
{
m_popupShown = false;
m_accel = gtk_accel_group_new();
m_menu = gtk_menu_new();
g_object_ref_sink(m_menu);
m_owner = 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);
}
// append the title as the very first entry if we have it
if ( !m_title.empty() )
{
Append(wxGTK_TITLE_ID, m_title);
AppendSeparator();
}
// "show" occurs for sub-menus which are not showing, so use "map" instead
g_signal_connect(m_menu, "map", G_CALLBACK(menu_map), this);
g_signal_connect(m_menu, "hide", G_CALLBACK(menu_hide), this);
}
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_matched(m_menu,
GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, this);
if (m_owner)
{
gtk_widget_destroy(m_owner);
g_object_unref(m_owner);
}
else
gtk_widget_destroy(m_menu);
g_object_unref(m_menu);
g_object_unref(m_accel);
}
void wxMenu::SetLayoutDirection(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);
}
wxString wxMenu::GetTitle() const
{
return wxConvertMnemonicsFromGTK(wxMenuBase::GetTitle());
}
void wxMenu::SetTitle(const wxString& title)
{
wxMenuBase::SetTitle(wxConvertMnemonicsToGTK(title));
}
void wxMenu::GtkAppend(wxMenuItem* mitem, int pos)
{
GtkWidget *menuItem;
switch (mitem->GetKind())
{
case wxITEM_SEPARATOR:
menuItem = gtk_separator_menu_item_new();
break;
case wxITEM_CHECK:
menuItem = gtk_check_menu_item_new_with_label("");
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 ( radioGroupItem )
{
group = gtk_radio_menu_item_get_group(
GTK_RADIO_MENU_ITEM(radioGroupItem->GetMenuItem())
);
}
menuItem = gtk_radio_menu_item_new_with_label(group, "");
}
break;
default:
wxFAIL_MSG("unexpected menu item kind");
// fall through
case wxITEM_NORMAL:
const wxBitmap& bitmap = mitem->GetBitmap();
const char* stockid;
if (bitmap.IsOk())
{
// always use pixbuf, because pixmap mask does not
// work with disabled images in some themes
GtkWidget* image = gtk_image_new_from_pixbuf(bitmap.GetPixbuf());
menuItem = gtk_image_menu_item_new_with_label("");
gtk_widget_show(image);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuItem), image);
}
else if ((stockid = wxGetStockGtkID(mitem->GetId())) != NULL)
// use stock bitmap for this item if available on the assumption
// that it never hurts to follow GTK+ conventions more closely
menuItem = gtk_image_menu_item_new_from_stock(stockid, NULL);
else
menuItem = gtk_menu_item_new_with_label("");
break;
}
mitem->SetMenuItem(menuItem);
gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu), menuItem, pos);
gtk_widget_show( menuItem );
if ( !mitem->IsSeparator() )
{
mitem->SetGtkLabel();
if ( mitem->IsSubMenu() )
UpdateSubMenuItemLabels(mitem);
g_signal_connect (menuItem, "select",
G_CALLBACK(menuitem_select), mitem);
g_signal_connect (menuItem, "deselect",
G_CALLBACK(menuitem_deselect), mitem);
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 );
}
else
{
g_signal_connect(menuItem, "can_activate_accel",
G_CALLBACK(can_activate_accel), this);
g_signal_connect (menuItem, "activate",
G_CALLBACK(menuitem_activate),
mitem);
}
}
}
wxMenuItem* wxMenu::DoAppend(wxMenuItem *mitem)
{
if (wxMenuBase::DoAppend(mitem))
{
GtkAppend(mitem);
return mitem;
}
return NULL;
}
wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
{
if (wxMenuBase::DoInsert(pos, item))
{
GtkAppend(item, int(pos));
return item;
}
return NULL;
}
wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
{
if ( !wxMenuBase::DoRemove(item) )
return NULL;
GtkWidget * const mitem = item->GetMenuItem();
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
gtk_menu_item_remove_submenu(GTK_MENU_ITEM(mitem));
#endif
gtk_widget_destroy(mitem);
item->SetMenuItem(NULL);
return item;
}
void wxMenu::Attach(wxMenuBarBase *menubar)
{
wxMenuBase::Attach(menubar);
// inherit layout direction from menubar.
SetLayoutDirection(menubar->GetLayoutDirection());
}
// ----------------------------------------------------------------------------
// helpers
// ----------------------------------------------------------------------------
#if wxUSE_ACCEL
static wxString GetGtkHotKey( const wxMenuItem& item )
{
wxString hotkey;
wxAcceleratorEntry *accel = item.GetAccel();
if ( accel )
{
int flags = accel->GetFlags();
if ( flags & wxACCEL_ALT )
hotkey += wxT("<alt>");
if ( flags & wxACCEL_CTRL )
hotkey += wxT("<control>");
if ( flags & wxACCEL_SHIFT )
hotkey += wxT("<shift>");
int code = accel->GetKeyCode();
switch ( code )
{
case WXK_F1:
case WXK_F2:
case WXK_F3:
case WXK_F4:
case WXK_F5:
case WXK_F6:
case WXK_F7:
case WXK_F8:
case WXK_F9:
case WXK_F10:
case WXK_F11:
case WXK_F12:
case WXK_F13:
case WXK_F14:
case WXK_F15:
case WXK_F16:
case WXK_F17:
case WXK_F18:
case WXK_F19:
case WXK_F20:
case WXK_F21:
case WXK_F22:
case WXK_F23:
case WXK_F24:
hotkey += wxString::Format(wxT("F%d"), code - WXK_F1 + 1);
break;
// TODO: we should use gdk_keyval_name() (a.k.a.
// XKeysymToString) here as well as hardcoding the keysym
// names this might be not portable
case WXK_INSERT:
hotkey << wxT("Insert" );
break;
case WXK_DELETE:
hotkey << wxT("Delete" );
break;
case WXK_UP:
hotkey << wxT("Up" );
break;
case WXK_DOWN:
hotkey << wxT("Down" );
break;
case WXK_PAGEUP:
hotkey << wxT("Page_Up" );
break;
case WXK_PAGEDOWN:
hotkey << wxT("Page_Down" );
break;
case WXK_LEFT:
hotkey << wxT("Left" );
break;
case WXK_RIGHT:
hotkey << wxT("Right" );
break;
case WXK_HOME:
hotkey << wxT("Home" );
break;
case WXK_END:
hotkey << wxT("End" );
break;
case WXK_RETURN:
hotkey << wxT("Return" );
break;
case WXK_BACK:
hotkey << wxT("BackSpace" );
break;
case WXK_TAB:
hotkey << wxT("Tab" );
break;
case WXK_ESCAPE:
hotkey << wxT("Esc" );
break;
case WXK_SPACE:
hotkey << wxT("space" );
break;
case WXK_MULTIPLY:
hotkey << wxT("Multiply" );
break;
case WXK_ADD:
hotkey << wxT("Add" );
break;
case WXK_SEPARATOR:
hotkey << wxT("Separator" );
break;
case WXK_SUBTRACT:
hotkey << wxT("Subtract" );
break;
case WXK_DECIMAL:
hotkey << wxT("Decimal" );
break;
case WXK_DIVIDE:
hotkey << wxT("Divide" );
break;
case WXK_CANCEL:
hotkey << wxT("Cancel" );
break;
case WXK_CLEAR:
hotkey << wxT("Clear" );
break;
case WXK_MENU:
hotkey << wxT("Menu" );
break;
case WXK_PAUSE:
hotkey << wxT("Pause" );
break;
case WXK_CAPITAL:
hotkey << wxT("Capital" );
break;
case WXK_SELECT:
hotkey << wxT("Select" );
break;
case WXK_PRINT:
hotkey << wxT("Print" );
break;
case WXK_EXECUTE:
hotkey << wxT("Execute" );
break;
case WXK_SNAPSHOT:
hotkey << wxT("Snapshot" );
break;
case WXK_HELP:
hotkey << wxT("Help" );
break;
case WXK_NUMLOCK:
hotkey << wxT("Num_Lock" );
break;
case WXK_SCROLL:
hotkey << wxT("Scroll_Lock" );
break;
case WXK_NUMPAD_INSERT:
hotkey << wxT("KP_Insert" );
break;
case WXK_NUMPAD_DELETE:
hotkey << wxT("KP_Delete" );
break;
case WXK_NUMPAD_SPACE:
hotkey << wxT("KP_Space" );
break;
case WXK_NUMPAD_TAB:
hotkey << wxT("KP_Tab" );
break;
case WXK_NUMPAD_ENTER:
hotkey << wxT("KP_Enter" );
break;
case WXK_NUMPAD_F1: case WXK_NUMPAD_F2: case WXK_NUMPAD_F3:
case WXK_NUMPAD_F4:
hotkey += wxString::Format(wxT("KP_F%d"), code - WXK_NUMPAD_F1 + 1);
break;
case WXK_NUMPAD_HOME:
hotkey << wxT("KP_Home" );
break;
case WXK_NUMPAD_LEFT:
hotkey << wxT("KP_Left" );
break;
case WXK_NUMPAD_UP:
hotkey << wxT("KP_Up" );
break;
case WXK_NUMPAD_RIGHT:
hotkey << wxT("KP_Right" );
break;
case WXK_NUMPAD_DOWN:
hotkey << wxT("KP_Down" );
break;
case WXK_NUMPAD_PAGEUP:
hotkey << wxT("KP_Page_Up" );
break;
case WXK_NUMPAD_PAGEDOWN:
hotkey << wxT("KP_Page_Down" );
break;
case WXK_NUMPAD_END:
hotkey << wxT("KP_End" );
break;
case WXK_NUMPAD_BEGIN:
hotkey << wxT("KP_Begin" );
break;
case WXK_NUMPAD_EQUAL:
hotkey << wxT("KP_Equal" );
break;
case WXK_NUMPAD_MULTIPLY:
hotkey << wxT("KP_Multiply" );
break;
case WXK_NUMPAD_ADD:
hotkey << wxT("KP_Add" );
break;
case WXK_NUMPAD_SEPARATOR:
hotkey << wxT("KP_Separator" );
break;
case WXK_NUMPAD_SUBTRACT:
hotkey << wxT("KP_Subtract" );
break;
case WXK_NUMPAD_DECIMAL:
hotkey << wxT("KP_Decimal" );
break;
case WXK_NUMPAD_DIVIDE:
hotkey << wxT("KP_Divide" );
break;
case WXK_NUMPAD0: case WXK_NUMPAD1: case WXK_NUMPAD2:
case WXK_NUMPAD3: case WXK_NUMPAD4: case WXK_NUMPAD5:
case WXK_NUMPAD6: case WXK_NUMPAD7: case WXK_NUMPAD8: case WXK_NUMPAD9:
hotkey += wxString::Format(wxT("KP_%d"), code - WXK_NUMPAD0);
break;
case WXK_WINDOWS_LEFT:
hotkey << wxT("Super_L" );
break;
case WXK_WINDOWS_RIGHT:
hotkey << wxT("Super_R" );
break;
case WXK_WINDOWS_MENU:
hotkey << wxT("Menu" );
break;
case WXK_COMMAND:
hotkey << wxT("Command" );
break;
/* These probably wouldn't work as there is no SpecialX in gdk/keynames.txt
case WXK_SPECIAL1: case WXK_SPECIAL2: case WXK_SPECIAL3: case WXK_SPECIAL4:
case WXK_SPECIAL5: case WXK_SPECIAL6: case WXK_SPECIAL7: case WXK_SPECIAL8:
case WXK_SPECIAL9: case WXK_SPECIAL10: case WXK_SPECIAL11: case WXK_SPECIAL12:
case WXK_SPECIAL13: case WXK_SPECIAL14: case WXK_SPECIAL15: case WXK_SPECIAL16:
case WXK_SPECIAL17: case WXK_SPECIAL18: case WXK_SPECIAL19: case WXK_SPECIAL20:
hotkey += wxString::Format(wxT("Special%d"), code - WXK_SPECIAL1 + 1);
break;
*/
// if there are any other keys wxAcceleratorEntry::Create() may
// return, we should process them here
default:
if ( code < 127 )
{
const wxString
name = wxGTK_CONV_BACK_SYS(gdk_keyval_name((guint)code));
if ( !name.empty() )
{
hotkey << name;
break;
}
}
wxFAIL_MSG( wxT("unknown keyboard accel") );
}
delete accel;
}
return hotkey;
}
static void
wxGetGtkAccel(const wxMenuItem* item, guint* accel_key, GdkModifierType* accel_mods)
{
*accel_key = 0;
const wxString string = GetGtkHotKey(*item);
if (!string.empty())
gtk_accelerator_parse(wxGTK_CONV_SYS(string), accel_key, accel_mods);
else
{
GtkStockItem stock_item;
const char* stockid = wxGetStockGtkID(item->GetId());
if (stockid && gtk_stock_lookup(stockid, &stock_item))
{
*accel_key = stock_item.keyval;
*accel_mods = stock_item.modifier;
}
}
}
#endif // wxUSE_ACCEL
const char *wxGetStockGtkID(wxWindowID id)
{
#define STOCKITEM(wx,gtk) \
case wx: \
return gtk;
#if GTK_CHECK_VERSION(2,8,0)
#define STOCKITEM_28(wx,gtk) STOCKITEM(wx,gtk)
#else
#define STOCKITEM_28(wx,gtk)
#endif
#if GTK_CHECK_VERSION(2,10,0)
#define STOCKITEM_210(wx,gtk) STOCKITEM(wx,gtk)
#else
#define STOCKITEM_210(wx,gtk)
#endif
switch (id)
{
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)
STOCKITEM(wxID_BOLD, GTK_STOCK_BOLD)
STOCKITEM(wxID_BOTTOM, GTK_STOCK_GOTO_BOTTOM)
STOCKITEM(wxID_CANCEL, GTK_STOCK_CANCEL)
STOCKITEM(wxID_CDROM, GTK_STOCK_CDROM)
STOCKITEM(wxID_CLEAR, GTK_STOCK_CLEAR)
STOCKITEM(wxID_CLOSE, GTK_STOCK_CLOSE)
STOCKITEM(wxID_CONVERT, GTK_STOCK_CONVERT)
STOCKITEM(wxID_COPY, GTK_STOCK_COPY)
STOCKITEM(wxID_CUT, GTK_STOCK_CUT)
STOCKITEM(wxID_DELETE, GTK_STOCK_DELETE)
STOCKITEM(wxID_DOWN, GTK_STOCK_GO_DOWN)
STOCKITEM(wxID_EDIT, GTK_STOCK_EDIT)
STOCKITEM(wxID_EXECUTE, GTK_STOCK_EXECUTE)
STOCKITEM(wxID_EXIT, GTK_STOCK_QUIT)
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)
STOCKITEM(wxID_FORWARD, GTK_STOCK_GO_FORWARD)
STOCKITEM(wxID_HARDDISK, GTK_STOCK_HARDDISK)
STOCKITEM(wxID_HELP, GTK_STOCK_HELP)
STOCKITEM(wxID_HOME, GTK_STOCK_HOME)
STOCKITEM(wxID_INDENT, GTK_STOCK_INDENT)
STOCKITEM(wxID_INDEX, GTK_STOCK_INDEX)
STOCKITEM_28(wxID_INFO, GTK_STOCK_INFO)
STOCKITEM(wxID_ITALIC, GTK_STOCK_ITALIC)
STOCKITEM(wxID_JUMP_TO, GTK_STOCK_JUMP_TO)
STOCKITEM(wxID_JUSTIFY_CENTER, GTK_STOCK_JUSTIFY_CENTER)
STOCKITEM(wxID_JUSTIFY_FILL, GTK_STOCK_JUSTIFY_FILL)
STOCKITEM(wxID_JUSTIFY_LEFT, GTK_STOCK_JUSTIFY_LEFT)
STOCKITEM(wxID_JUSTIFY_RIGHT, GTK_STOCK_JUSTIFY_RIGHT)
STOCKITEM(wxID_LAST, GTK_STOCK_GOTO_LAST)
STOCKITEM(wxID_NETWORK, GTK_STOCK_NETWORK)
STOCKITEM(wxID_NEW, GTK_STOCK_NEW)
STOCKITEM(wxID_NO, GTK_STOCK_NO)
STOCKITEM(wxID_OK, GTK_STOCK_OK)
STOCKITEM(wxID_OPEN, GTK_STOCK_OPEN)
STOCKITEM(wxID_PASTE, GTK_STOCK_PASTE)
STOCKITEM(wxID_PREFERENCES, GTK_STOCK_PREFERENCES)
STOCKITEM(wxID_PREVIEW, GTK_STOCK_PRINT_PREVIEW)
STOCKITEM(wxID_PRINT, GTK_STOCK_PRINT)
STOCKITEM(wxID_PROPERTIES, GTK_STOCK_PROPERTIES)
STOCKITEM(wxID_REDO, GTK_STOCK_REDO)
STOCKITEM(wxID_REFRESH, GTK_STOCK_REFRESH)
STOCKITEM(wxID_REMOVE, GTK_STOCK_REMOVE)
STOCKITEM(wxID_REPLACE, GTK_STOCK_FIND_AND_REPLACE)
STOCKITEM(wxID_REVERT_TO_SAVED, GTK_STOCK_REVERT_TO_SAVED)
STOCKITEM(wxID_SAVE, GTK_STOCK_SAVE)
STOCKITEM(wxID_SAVEAS, GTK_STOCK_SAVE_AS)
STOCKITEM_210(wxID_SELECTALL, GTK_STOCK_SELECT_ALL)
STOCKITEM(wxID_SELECT_COLOR, GTK_STOCK_SELECT_COLOR)
STOCKITEM(wxID_SELECT_FONT, GTK_STOCK_SELECT_FONT)
STOCKITEM(wxID_SORT_ASCENDING, GTK_STOCK_SORT_ASCENDING)
STOCKITEM(wxID_SORT_DESCENDING, GTK_STOCK_SORT_DESCENDING)
STOCKITEM(wxID_SPELL_CHECK, GTK_STOCK_SPELL_CHECK)
STOCKITEM(wxID_STOP, GTK_STOCK_STOP)
STOCKITEM(wxID_STRIKETHROUGH, GTK_STOCK_STRIKETHROUGH)
STOCKITEM(wxID_TOP, GTK_STOCK_GOTO_TOP)
STOCKITEM(wxID_UNDELETE, GTK_STOCK_UNDELETE)
STOCKITEM(wxID_UNDERLINE, GTK_STOCK_UNDERLINE)
STOCKITEM(wxID_UNDO, GTK_STOCK_UNDO)
STOCKITEM(wxID_UNINDENT, GTK_STOCK_UNINDENT)
STOCKITEM(wxID_UP, GTK_STOCK_GO_UP)
STOCKITEM(wxID_YES, GTK_STOCK_YES)
STOCKITEM(wxID_ZOOM_100, GTK_STOCK_ZOOM_100)
STOCKITEM(wxID_ZOOM_FIT, GTK_STOCK_ZOOM_FIT)
STOCKITEM(wxID_ZOOM_IN, GTK_STOCK_ZOOM_IN)
STOCKITEM(wxID_ZOOM_OUT, GTK_STOCK_ZOOM_OUT)
default:
break;
};
#undef STOCKITEM
return NULL;
}
#endif // wxUSE_MENUS