Merge branch 'extra_accels' of https://github.com/vadz/wxWidgets

Allow defining additional accelerators for the menu items.

See https://github.com/wxWidgets/wxWidgets/pull/2588
This commit is contained in:
Vadim Zeitlin
2021-11-18 15:29:56 +01:00
18 changed files with 478 additions and 90 deletions

View File

@@ -518,6 +518,28 @@ Example:
@endcode
@subsection overview_xrcformat_type_extra_accels Accelerators List
Defines a list of wxMenuItem's extra accelerators.
The extra-accels property element is a "composite" element:
it contains one or more @c \<accel\> "sub-properties":
@beginTable
@hdr3col{property, type, description}
@row3col{accel, @ref overview_xrcformat_type_text_notrans,
wxMenuItem's accelerator (default: none).}
@endTable
Example:
@code
<extra-accels>
<accel>Ctrl-W</accel>
<accel>Shift-Ctrl-W</accel>
</extra-accels>
@endcode
@section overview_xrcformat_windows Controls and Windows
This section describes support wxWindow-derived classes in XRC format.
@@ -1496,6 +1518,10 @@ wxMenuItem objects support the following properties:
Item's label (may be omitted if stock ID is used).}
@row3col{accel, @ref overview_xrcformat_type_text_notrans,
Item's accelerator (default: none).}
@row3col{extra-accels, @ref overview_xrcformat_type_extra_accels,
List of item's extra accelerators. Such accelerators will not be shown
in item's label, but still will work. (default: none).
This property is only supported since wxWidgets 3.1.6.}
@row3col{radio, @ref overview_xrcformat_type_bool,
Item's kind is wxITEM_RADIO (default: 0)?}
@row3col{checkable, @ref overview_xrcformat_type_bool,
@@ -1520,6 +1546,10 @@ Example:
<object class="wxMenuItem" name="wxID_FIND">
<label>_Find...</label>
<accel>Ctrl-F</accel>
<extra-accels>
<accel>Ctrl-W</accel>
<accel>Shift-Ctrl-W</accel>
</extra-accels>
</object>
<object class="separator"/>
<object class="wxMenuItem" name="menu_fuzzy">

View File

@@ -34,11 +34,20 @@ public:
virtual void SetBitmap(const wxBitmap& bitmap);
virtual const wxBitmap& GetBitmap() const { return m_bitmap; }
#if wxUSE_ACCEL
virtual void AddExtraAccel(const wxAcceleratorEntry& accel) wxOVERRIDE;
virtual void ClearExtraAccels() wxOVERRIDE;
#endif // wxUSE_ACCEL
// implementation
void SetMenuItem(GtkWidget *menuItem);
GtkWidget *GetMenuItem() const { return m_menuItem; }
void SetGtkLabel();
#if wxUSE_ACCEL
void GTKSetExtraAccels();
#endif // wxUSE_ACCEL
#if WXWIN_COMPATIBILITY_2_8
// compatibility only, don't use in new code
wxDEPRECATED_CONSTRUCTOR(

View File

@@ -23,6 +23,8 @@
#include "wx/windowid.h"
#include "wx/vector.h"
// ----------------------------------------------------------------------------
// forward declarations
// ----------------------------------------------------------------------------
@@ -122,6 +124,14 @@ public:
// set the accel for this item - this may also be done indirectly with
// SetText()
virtual void SetAccel(wxAcceleratorEntry *accel);
// add the accel to extra accels list
virtual void AddExtraAccel(const wxAcceleratorEntry& accel);
// return vector of extra accels. Implementation only.
const wxVector<wxAcceleratorEntry>& GetExtraAccels() const { return m_extraAccels; }
virtual void ClearExtraAccels() { m_extraAccels.clear(); }
#endif // wxUSE_ACCEL
#if WXWIN_COMPATIBILITY_2_8
@@ -163,6 +173,10 @@ protected:
bool m_isChecked; // is checked?
bool m_isEnabled; // is enabled?
#if wxUSE_ACCEL
wxVector<wxAcceleratorEntry> m_extraAccels; // extra accels will work, but won't be shown in wxMenuItem title
#endif // wxUSE_ACCEL
// this ctor is for the derived classes only, we're never created directly
wxMenuItemBase(wxMenu *parentMenu = NULL,
int itemid = wxID_SEPARATOR,

View File

@@ -165,6 +165,7 @@ public :
virtual void Check( bool check ) = 0;
virtual void SetLabel( const wxString& text, wxAcceleratorEntry *entry ) = 0;
virtual void Hide( bool hide = true ) = 0;
virtual void SetAllowsKeyEquivalentWhenHidden( bool ) {}
virtual void * GetHMenuItem() = 0;

View File

@@ -17,6 +17,7 @@
#include "wx/defs.h"
#include "wx/bitmap.h"
#include "wx/vector.h"
// ----------------------------------------------------------------------------
// wxMenuItem: an item in the menu, optionally implements owner-drawn behaviour
@@ -42,6 +43,12 @@ public:
virtual void Enable(bool bDoEnable = true) wxOVERRIDE;
virtual void Check(bool bDoCheck = true) wxOVERRIDE;
#if wxUSE_ACCEL
virtual void AddExtraAccel(const wxAcceleratorEntry& accel) wxOVERRIDE;
virtual void ClearExtraAccels() wxOVERRIDE;
void RemoveHiddenItems();
#endif // wxUSE_ACCEL
virtual void SetBitmap(const wxBitmap& bitmap) ;
virtual const wxBitmap& GetBitmap() const { return m_bitmap; }
@@ -61,6 +68,10 @@ private:
wxMenuItemImpl* m_peer;
#if wxUSE_ACCEL
wxVector<wxMenuItem*> m_hiddenMenuItems;
#endif // wxUSE_ACCEL
wxDECLARE_DYNAMIC_CLASS(wxMenuItem);
};

View File

@@ -567,6 +567,30 @@ public:
*/
virtual void SetAccel(wxAcceleratorEntry *accel);
/**
Add an extra accelerator for this menu item.
Additional accelerators are not shown in the item's label,
but still will trigger the menu command when pressed.
They can be useful to let multiple keys be used as accelerators
for the same command, e.g. @c WXK_ADD and @c WXK_NUMPAD_ADD.
@onlyfor{wxmsw,wxgtk}
@since 3.1.6
*/
void AddExtraAccel(wxAcceleratorEntry *accel);
/**
Clear the extra accelerators list.
This doesn't affect the main item accelerator (if any).
@since 3.1.6
*/
void ClearExtraAccels();
//@}
};

View File

@@ -501,6 +501,10 @@ t_list_of_numbers = xsd:string { pattern = "\d+(,\d+)*" }
t_list_of_numbers_with_weights = xsd:string { pattern = "\d+(:\d+)?(,\d+(:\d+)?)*" }
t_extra_accels = (
[xrc:p="o"] element accel {_, t_text }+
)
#
# Handlers for non-<object> content:
@@ -1195,6 +1199,7 @@ wxMenuItem =
stdObjectNodeAttributes &
[xrc:p="o"] element label {_, t_text }* &
[xrc:p="o"] element accel {_, t_text }* &
[xrc:p="o"] element extra-accels {_, t_extra_accels }* &
[xrc:p="o"] element radio {_, t_bool }* &
[xrc:p="o"] element checkable {_, t_bool }* &
[xrc:p="o"] element bitmap {_, t_bitmap }* &

View File

@@ -84,6 +84,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(XRCID("derived_tool_or_menuitem"), MyFrame::OnDerivedDialogToolOrMenuCommand)
EVT_MENU(XRCID("controls_tool_or_menuitem"), MyFrame::OnControlsToolOrMenuCommand)
EVT_MENU(XRCID("uncentered_tool_or_menuitem"), MyFrame::OnUncenteredToolOrMenuCommand)
EVT_MENU(XRCID("multiple_accels"), MyFrame::OnMultipleAccels)
EVT_MENU(XRCID("aui_demo_tool_or_menuitem"), MyFrame::OnAuiDemoToolOrMenuCommand)
EVT_MENU(XRCID("obj_ref_tool_or_menuitem"), MyFrame::OnObjRefToolOrMenuCommand)
EVT_MENU(XRCID("custom_class_tool_or_menuitem"), MyFrame::OnCustomClassToolOrMenuCommand)
@@ -285,6 +286,26 @@ void MyFrame::OnUncenteredToolOrMenuCommand(wxCommandEvent& WXUNUSED(event))
dlg.ShowModal();
}
void MyFrame::OnMultipleAccels(wxCommandEvent& WXUNUSED(event))
{
wxString msg;
#if defined(__WXOSX_COCOA__)
wxString main = "Cmd-W";
wxString extra1 = "Cmd-T";
wxString extra2 = "Shift-Cmd-W";
#else
wxString main = "Ctrl-W";
wxString extra1 = "Ctrl-T";
wxString extra2 = "Shift-Ctrl-W";
#endif
msg.Printf(
"You can open this dialog with any of '%s' (main), '%s' or '%s' (extra) accelerators.",
main, extra1, extra2
);
wxMessageBox(msg, _("Multiple accelerators demo"), wxOK | wxICON_INFORMATION, this);
}
void MyFrame::OnAuiDemoToolOrMenuCommand(wxCommandEvent& WXUNUSED(event))
{
#if wxUSE_AUI

View File

@@ -43,6 +43,7 @@ private:
void OnDerivedDialogToolOrMenuCommand(wxCommandEvent& event);
void OnControlsToolOrMenuCommand(wxCommandEvent& event);
void OnUncenteredToolOrMenuCommand(wxCommandEvent& event);
void OnMultipleAccels(wxCommandEvent& event);
void OnAuiDemoToolOrMenuCommand(wxCommandEvent& event);
void OnObjRefToolOrMenuCommand(wxCommandEvent& event);
void OnCustomClassToolOrMenuCommand(wxCommandEvent& event);

View File

@@ -45,6 +45,15 @@
<bitmap>uncenter.xpm</bitmap>
<help>Disable autocentering of a dialog on its parent</help>
</object>
<object class="wxMenuItem" name="multiple_accels">
<label>_Multiple accelerators</label>
<help>You can open the dialog with multiple accelerators</help>
<accel>Ctrl-W</accel>
<extra-accels>
<accel>Ctrl-T</accel>
<accel>Shift-Ctrl-W</accel>
</extra-accels>
</object>
</object>
<object class="wxMenu" name="advanced_demos_menu">
<label>_Advanced</label>

View File

@@ -296,6 +296,11 @@ void wxMenuItemBase::SetAccel(wxAcceleratorEntry *accel)
SetItemLabel(text);
}
void wxMenuItemBase::AddExtraAccel(const wxAcceleratorEntry& accel)
{
m_extraAccels.push_back(accel);
}
#endif // wxUSE_ACCEL
void wxMenuItemBase::SetItemLabel(const wxString& str)

View File

@@ -36,8 +36,38 @@ extern int wxOpenModalDialogsCount;
static const int wxGTK_TITLE_ID = -3;
#if wxUSE_ACCEL
static bool wxGetGtkAccel(const wxMenuItem*, guint*, GdkModifierType*);
#endif
namespace
{
// Simple struct encapsulating a GTK accelerator with wrappers for just the
// operations that we need.
class GtkAccel
{
public:
// Can be constructed from any menu item (must be valid, but doesn't have
// to be an accelerator) or an already existing accelerator entry.
explicit GtkAccel(const wxMenuItem* item);
explicit GtkAccel(const wxAcceleratorEntry& entry) { Init(&entry); }
// Default copy ctor, assignment operator and dtor are all OK.
bool IsOk() const { return m_key != 0; }
void Add(GtkWidget* widget, GtkAccelGroup* accelGroup, GtkAccelFlags accelFlags);
void Remove(GtkWidget* widget, GtkAccelGroup* accelGroup);
private:
static wxString GetGtkHotKey(const wxAcceleratorEntry*);
void Init(const wxAcceleratorEntry* entry);
guint m_key;
GdkModifierType m_mods;
};
} // anonymous namespace
#endif // wxUSE_ACCEL
// 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
@@ -642,12 +672,10 @@ void wxMenuItem::SetItemLabel( const wxString& str )
if (m_menuItem)
{
// remove old accelerator
guint accel_key;
GdkModifierType accel_mods;
if ( wxGetGtkAccel(this, &accel_key, &accel_mods) )
GtkAccel gtkAccel(this);
if ( gtkAccel.IsOk() )
{
gtk_widget_remove_accelerator(
m_menuItem, GetRootParentMenu(m_parentMenu)->m_accel, accel_key, accel_mods);
gtkAccel.Remove(m_menuItem, GetRootParentMenu(m_parentMenu)->m_accel);
}
}
#endif // wxUSE_ACCEL
@@ -662,13 +690,11 @@ void wxMenuItem::SetGtkLabel()
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;
if ( wxGetGtkAccel(this, &accel_key, &accel_mods) )
GtkAccel gtkAccel(this);
if ( gtkAccel.IsOk() )
{
gtk_widget_add_accelerator(
m_menuItem, "activate", GetRootParentMenu(m_parentMenu)->m_accel,
accel_key, accel_mods, GTK_ACCEL_VISIBLE);
gtkAccel.Add(m_menuItem, GetRootParentMenu(m_parentMenu)->m_accel,
GTK_ACCEL_VISIBLE);
}
else
{
@@ -678,6 +704,54 @@ void wxMenuItem::SetGtkLabel()
#endif // wxUSE_ACCEL
}
#if wxUSE_ACCEL
void wxMenuItem::GTKSetExtraAccels()
{
GtkAccelGroup* const accelGroup = GetRootParentMenu(m_parentMenu)->m_accel;
const size_t extraAccelsSize = m_extraAccels.size();
for (size_t i = 0; i < extraAccelsSize; ++i)
{
GtkAccel(m_extraAccels[i]).Add(m_menuItem, accelGroup, GTK_ACCEL_MASK);
}
}
void wxMenuItem::AddExtraAccel(const wxAcceleratorEntry& accel)
{
wxMenuItemBase::AddExtraAccel(accel);
// If the item is not yet part of the menu, all its extra accelerators will
// be registered with GTK in GTKSetExtraAccels() once it is added to the
// menu, but if it had already been added to it, we need to let GTK know
// about the new extra accelerator.
if (m_menuItem)
{
GtkAccelGroup* const accelGroup = GetRootParentMenu(m_parentMenu)->m_accel;
GtkAccel(accel).Add(m_menuItem, accelGroup, GTK_ACCEL_MASK);
}
}
void wxMenuItem::ClearExtraAccels()
{
// Tell GTK not to use the existing accelerators any longer if they're
// already in use.
if (m_menuItem)
{
GtkAccelGroup* const accelGroup = GetRootParentMenu(m_parentMenu)->m_accel;
const size_t extraAccelsSize = m_extraAccels.size();
for (size_t i = 0; i < extraAccelsSize; ++i)
{
GtkAccel(m_extraAccels[i]).Remove(m_menuItem, accelGroup);
}
}
wxMenuItemBase::ClearExtraAccels();
}
#endif // wxUSE_ACCEL
void wxMenuItem::SetBitmap(const wxBitmap& bitmap)
{
if (m_kind == wxITEM_NORMAL)
@@ -961,6 +1035,10 @@ void wxMenu::GtkAppend(wxMenuItem* mitem, int pos)
if ( mitem->IsSubMenu() )
UpdateSubMenuItemLabels(mitem);
#if wxUSE_ACCEL
mitem->GTKSetExtraAccels();
#endif
g_signal_connect (menuItem, "select",
G_CALLBACK(menuitem_select), mitem);
g_signal_connect (menuItem, "deselect",
@@ -1038,11 +1116,11 @@ void wxMenu::Attach(wxMenuBarBase *menubar)
#if wxUSE_ACCEL
static wxString GetGtkHotKey( const wxMenuItem& item )
/* static */
wxString GtkAccel::GetGtkHotKey(const wxAcceleratorEntry *accel)
{
wxString hotkey;
wxAcceleratorEntry *accel = item.GetAccel();
if ( accel )
{
int flags = accel->GetFlags();
@@ -1313,33 +1391,44 @@ static wxString GetGtkHotKey( const wxMenuItem& item )
}
break;
}
delete accel;
}
return hotkey;
}
static bool
wxGetGtkAccel(const wxMenuItem* item, guint* accel_key, GdkModifierType* accel_mods)
void GtkAccel::Init(const wxAcceleratorEntry* entry)
{
const wxString string = GetGtkHotKey(*item);
const wxString string = GetGtkHotKey(entry);
if (!string.empty())
{
gtk_accelerator_parse(wxGTK_CONV_SYS(string), accel_key, accel_mods);
gtk_accelerator_parse(wxGTK_CONV_SYS(string), &m_key, &m_mods);
// Normally, we detect all the keys considered invalid by GTK in
// GetGtkHotKey(), but just in case GTK decides to add more invalid
// keys in the future versions, recheck once again using its function.
if ( gtk_accelerator_valid(*accel_key, *accel_mods) )
return true;
if ( !gtk_accelerator_valid(m_key, m_mods) )
{
wxLogDebug("\"%s\" is not a valid keyboard accelerator "
"for this GTK version",
string);
m_key = 0;
}
}
#ifndef __WXGTK4__
else
{
// Key with this code can never be a valid accelerator, so we use
// it to indicate that it's invalid.
m_key = 0;
}
}
GtkAccel::GtkAccel(const wxMenuItem* item)
{
wxScopedPtr<wxAcceleratorEntry> accel(item->GetAccel());
Init(accel.get());
#ifndef __WXGTK4__
if ( !IsOk() )
{
wxGCC_WARNING_SUPPRESS(deprecated-declarations)
@@ -1351,17 +1440,33 @@ wxGetGtkAccel(const wxMenuItem* item, guint* accel_key, GdkModifierType* accel_m
gtk_stock_lookup(stockid, &stock_item) &&
stock_item.keyval )
{
*accel_key = stock_item.keyval;
*accel_mods = stock_item.modifier;
return true;
m_key = stock_item.keyval;
m_mods = stock_item.modifier;
}
wxGCC_WARNING_RESTORE()
}
#endif
return false;
#endif // !__WXGTK4__
}
void
GtkAccel::Add(GtkWidget* widget, GtkAccelGroup* accelGroup, GtkAccelFlags accelFlags)
{
if ( IsOk() )
{
gtk_widget_add_accelerator(
widget, "activate", accelGroup,
m_key, m_mods, accelFlags);
}
}
void GtkAccel::Remove(GtkWidget* widget, GtkAccelGroup* accelGroup)
{
if ( IsOk() )
{
gtk_widget_remove_accelerator(widget, accelGroup, m_key, m_mods);
}
}
#endif // wxUSE_ACCEL
#ifndef __WXGTK4__

View File

@@ -225,29 +225,30 @@ void wxMenu::UpdateAccel(wxMenuItem *item)
return;
}
// find the (new) accel for this item
// remove old accels
int n = wxNOT_FOUND;
while ( (n = FindAccel(item->GetId())) != wxNOT_FOUND )
{
delete m_accels[n];
m_accels.RemoveAt(n);
}
// find the (new) accel for this item and add it if any
wxAcceleratorEntry *accel = wxAcceleratorEntry::Create(item->GetItemLabel());
if ( accel )
{
accel->m_command = item->GetId();
// find the old one
int n = FindAccel(item->GetId());
if ( n == wxNOT_FOUND )
{
// no old, add new if any
if ( accel )
m_accels.Add(accel);
else
return; // skipping RebuildAccelTable() below
}
else
// add extra accels
const wxVector<wxAcceleratorEntry>& extraAccelsVector = item->GetExtraAccels();
const int extraAccelsSize = extraAccelsVector.size();
for (int i = 0; i < extraAccelsSize; ++i)
{
// replace old with new or just remove the old one if no new
delete m_accels[n];
if ( accel )
m_accels[n] = accel;
else
m_accels.RemoveAt(n);
wxAcceleratorEntry *extraAccel = new wxAcceleratorEntry(extraAccelsVector[i]);
extraAccel->m_command = item->GetId();
m_accels.Add(extraAccel);
}
if ( IsAttached() )
@@ -274,19 +275,22 @@ void wxMenu::RemoveAccel(wxMenuItem *item)
return;
}
// remove the corresponding accel from the accel table
int n = FindAccel(item->GetId());
if ( n != wxNOT_FOUND )
// remove the corresponding accels from the accel table
int n = wxNOT_FOUND;
bool accels_found = false;
while ( (n = FindAccel(item->GetId())) != wxNOT_FOUND )
{
delete m_accels[n];
m_accels.RemoveAt(n);
accels_found = true;
}
#if wxUSE_OWNER_DRAWN
if ( accels_found )
{
ResetMaxAccelWidth();
#endif
}
//else: this item doesn't have an accel, nothing to do
#endif
}
#endif // wxUSE_ACCEL

View File

@@ -20,6 +20,7 @@
#endif // WX_PRECOMP
#include "wx/osx/private.h"
#include "wx/osx/private/available.h"
// a mapping from wx ids to standard osx actions in order to support the native menu item handling
// if a new mapping is added, make sure the wxNonOwnedWindowController has a handler for this action as well
@@ -270,6 +271,15 @@ public :
wxLogDebug("wxMenuItemCocoaImpl::Hide not yet supported under OS X < 10.5");
}
void SetAllowsKeyEquivalentWhenHidden( bool allow ) wxOVERRIDE
{
// setAllowsKeyEquivalentWhenHidden is available since macOS 10.13
if (WX_IS_MACOS_AVAILABLE(10, 13))
[m_osxMenuItem setAllowsKeyEquivalentWhenHidden:allow ];
else
wxLogDebug("wxMenuItemCocoaImpl::setAllowsKeyEquivalentWhenHidden not supported under OS X < 10.13");
}
void SetLabel( const wxString& text, wxAcceleratorEntry *entry ) wxOVERRIDE
{
wxCFStringRef cfText(text);

View File

@@ -211,6 +211,10 @@ wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
wxOSXMenuRemoveItem(m_hMenu , pos );
*/
#if wxUSE_ACCEL
// we need to remove all hidden menu items related to this one
item->RemoveHiddenItems();
#endif
GetPeer()->Remove( item );
// and from internal data structures
return wxMenuBase::DoRemove(item);

View File

@@ -21,6 +21,7 @@
#endif // WX_PRECOMP
#include "wx/osx/private.h"
#include "wx/osx/private/available.h"
wxIMPLEMENT_ABSTRACT_CLASS(wxMenuItemImpl, wxObject);
@@ -207,6 +208,45 @@ void wxMenuItem::UpdateItemText()
#endif // wxUSE_ACCEL/!wxUSE_ACCEL
}
#if wxUSE_ACCEL
void wxMenuItem::AddExtraAccel(const wxAcceleratorEntry& accel)
{
if (WX_IS_MACOS_AVAILABLE(10, 13))
{
wxMenuItemBase::AddExtraAccel(accel);
// create the same wxMenuItem but hidden and with different accelerator.
wxMenuItem* hiddenMenuItem = new wxMenuItem(m_parentMenu, GetId(), m_text, m_help, GetKind(), m_subMenu);
hiddenMenuItem->SetAccel(&(m_extraAccels.back()));
hiddenMenuItem->GetPeer()->Hide(true);
hiddenMenuItem->GetPeer()->SetAllowsKeyEquivalentWhenHidden(true);
m_parentMenu->GetPeer()->InsertOrAppend( hiddenMenuItem, -1 );
hiddenMenuItem->SetMenu(m_parentMenu);
m_hiddenMenuItems.push_back(hiddenMenuItem);
}
else
{
wxLogDebug("Extra accelerators not being supported under macOS < 10.13");
}
}
void wxMenuItem::ClearExtraAccels()
{
wxMenuItemBase::ClearExtraAccels();
RemoveHiddenItems();
}
void wxMenuItem::RemoveHiddenItems()
{
for (size_t i = 0; i < m_hiddenMenuItems.size(); ++i)
{
m_parentMenu->GetPeer()->Remove( m_hiddenMenuItems[i] );
}
}
#endif // wxUSE_ACCEL
// ----------------------------------------------------------------------------
// wxMenuItemBase
// ----------------------------------------------------------------------------

View File

@@ -10,6 +10,7 @@
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include <wx/xml/xml.h>
#if wxUSE_XRC && wxUSE_MENUS
@@ -79,6 +80,18 @@ wxObject *wxMenuXmlHandler::DoCreateResource()
wxString label = GetText(wxT("label"));
#if wxUSE_ACCEL
wxString accel = GetText(wxT("accel"), false);
wxVector<wxString> extraAccels;
if (HasParam(wxT("extra-accels")))
{
wxXmlNode* const extraAccelsNode = GetParamNode(wxT("extra-accels"));
wxXmlNode* node = extraAccelsNode->GetChildren();
while (node)
{
if (node->GetName() == wxT("accel"))
extraAccels.push_back(node->GetChildren()->GetContent());
node = node->GetNext();
}
}
#endif // wxUSE_ACCEL
wxItemKind kind = wxITEM_NORMAL;
@@ -101,11 +114,34 @@ wxObject *wxMenuXmlHandler::DoCreateResource()
wxMenuItem *mitem = new wxMenuItem(p_menu, id, label,
GetText(wxT("help")), kind);
#if wxUSE_ACCEL
if (!extraAccels.empty())
{
const int entriesSize = extraAccels.size();
for (int i = 0; i < entriesSize; ++i)
{
wxAcceleratorEntry entry;
if (entry.FromString(extraAccels[i]))
mitem->AddExtraAccel(entry);
else
ReportParamError
(
"extra-accels",
wxString::Format("cannot create accel from '%s\'", extraAccels[i])
);
}
}
if (!accel.empty())
{
wxAcceleratorEntry entry;
if (entry.FromString(accel))
mitem->SetAccel(&entry);
else
ReportParamError
(
"accel",
wxString::Format("cannot create accel from '%s'", accel)
);
}
#endif // wxUSE_ACCEL

View File

@@ -38,6 +38,8 @@ enum
MenuTestCase_Foo = 10000,
MenuTestCase_SelectAll,
MenuTestCase_Bar,
MenuTestCase_ExtraAccel,
MenuTestCase_ExtraAccels,
MenuTestCase_First
};
@@ -173,6 +175,34 @@ void MenuTestCase::CreateFrame()
"Accelerator conflicting with wxTextCtrl");
m_itemCount++;
// Test adding an extra accelerator to the item before adding it to the menu.
wxAcceleratorEntry entry;
wxMenuItem* const
extraAccel = new wxMenuItem(fileMenu, MenuTestCase_ExtraAccel, "Extra accels");
CHECK( entry.FromString("Ctrl-U") );
extraAccel->SetAccel(&entry);
CHECK( entry.FromString("Ctrl-V") );
extraAccel->AddExtraAccel(entry);
fileMenu->Append(extraAccel);
m_itemCount++;
// And now also test adding 2 of them after creating the initial menu item.
wxMenuItem* const
extraAccels = fileMenu->Append(MenuTestCase_ExtraAccels, "Extra accels 2");
m_itemCount++;
CHECK( entry.FromString("Ctrl-T") );
extraAccels->AddExtraAccel(entry);
CHECK(entry.FromString("Shift-Ctrl-W"));
extraAccels->AddExtraAccel(entry);
CHECK(entry.FromString("Ctrl-W"));
extraAccels->SetAccel(&entry);
PopulateMenu(helpMenu, "Helpmenu item ", m_itemCount);
helpMenu->Append(MenuTestCase_Bar, "Bar\tF1");
@@ -532,7 +562,6 @@ public:
{
m_win->Bind(wxEVT_MENU, &MenuEventHandler::OnMenu, this);
m_gotEvent = false;
m_event = NULL;
}
@@ -543,33 +572,41 @@ public:
delete m_event;
}
const wxCommandEvent& GetEvent()
// Check that we received an event with the given ID and return the event
// object if we did (otherwise fail the test and return NULL).
const wxObject* CheckGot(int expectedId)
{
CPPUNIT_ASSERT( m_gotEvent );
if ( !m_event )
{
FAIL("Event not generated");
return NULL;
}
m_gotEvent = false;
CHECK( m_event->GetId() == expectedId );
return *m_event;
const wxObject* const src = m_event->GetEventObject();
delete m_event;
m_event = NULL;
return src;
}
bool GotEvent() const
{
return m_gotEvent;
return m_event != NULL;
}
private:
void OnMenu(wxCommandEvent& event)
{
CPPUNIT_ASSERT( !m_gotEvent );
CHECK( !m_event );
delete m_event;
m_event = static_cast<wxCommandEvent*>(event.Clone());
m_gotEvent = true;
}
wxWindow* const m_win;
wxCommandEvent* m_event;
bool m_gotEvent;
};
#endif // wxUSE_UIACTIONSIMULATOR
@@ -589,23 +626,45 @@ void MenuTestCase::Events()
sim.KeyUp(WXK_F1);
wxYield();
const wxCommandEvent& ev = handler.GetEvent();
CPPUNIT_ASSERT_EQUAL( static_cast<int>(MenuTestCase_Bar), ev.GetId() );
wxObject* const src = ev.GetEventObject();
CPPUNIT_ASSERT( src );
CPPUNIT_ASSERT_EQUAL( "wxMenu",
wxString(src->GetClassInfo()->GetClassName()) );
CPPUNIT_ASSERT_EQUAL( static_cast<wxObject*>(m_menuWithBar),
src );
INFO("Expecting event for F1");
if ( const wxObject* const src = handler.CheckGot(MenuTestCase_Bar) )
{
CHECK( wxString(src->GetClassInfo()->GetClassName()) == "wxMenu" );
CHECK( src == m_menuWithBar );
}
// Invoke another accelerator, it should also work.
sim.Char('A', wxMOD_CONTROL);
wxYield();
const wxCommandEvent& ev2 = handler.GetEvent();
CHECK( ev2.GetId() == static_cast<int>(MenuTestCase_SelectAll) );
INFO("Expecting event for Ctrl-A");
handler.CheckGot(MenuTestCase_SelectAll);
// Invoke accelerator and extra accelerators, all of them should work.
sim.Char('U', wxMOD_CONTROL);
wxYield();
INFO("Expecting event for Ctrl-U");
handler.CheckGot(MenuTestCase_ExtraAccel);
sim.Char('V', wxMOD_CONTROL);
wxYield();
INFO("Expecting event for Ctrl-V");
handler.CheckGot(MenuTestCase_ExtraAccel);
sim.Char('W', wxMOD_CONTROL);
wxYield();
INFO("Expecting event for Ctrl-W");
handler.CheckGot(MenuTestCase_ExtraAccels);
sim.Char('T', wxMOD_CONTROL);
wxYield();
INFO("Expecting event for Ctrl-T");
handler.CheckGot(MenuTestCase_ExtraAccels);
sim.Char('W', wxMOD_CONTROL | wxMOD_SHIFT);
wxYield();
INFO("Expecting event for Ctrl-Shift-W");
handler.CheckGot(MenuTestCase_ExtraAccels);
// Now create a text control which uses the same accelerator for itself and
// check that when the text control has focus, the accelerator does _not_