Rework: custom/default key bindings

Issue: macro definitions had to be synchronized for
custom key bindings. That's not possible for readonly
macros and breaks the architecture.

Now, there is a default binding and a custom binding:
the macros provide a default binding only and the custom
key binding can override this. This scheme is implemented
consistently, so now the "reset" function of the key
binding editor simply clears the custom binding.

Side effect: reset of individual key bindings is possible.

Another side effect: removing a key binding from an
item with a default one is not possible. Instead, redefine
it.
This commit is contained in:
Matthias Koefferlein 2017-10-29 13:34:57 +01:00
parent 8a8fc18bb0
commit 26ecc6e1a6
9 changed files with 461 additions and 201 deletions

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KeyBindingsConfigPage</class>
<widget class="QFrame" name="KeyBindingsConfigPage" >
<property name="geometry" >
<widget class="QFrame" name="KeyBindingsConfigPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,78 +10,92 @@
<height>495</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>7</vsizetype>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QTreeWidget" name="bindings_list" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>7</vsizetype>
<widget class="QTreeWidget" name="bindings_list">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="rootIsDecorated" >
<property name="rootIsDecorated">
<bool>true</bool>
</property>
<column>
<property name="text" >
<property name="text">
<string>Path</string>
</property>
</column>
<column>
<property name="text" >
<property name="text">
<string>Title</string>
</property>
</column>
<column>
<property name="text" >
<property name="text">
<string>Shortcut</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@ -89,24 +104,24 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<widget class="QLabel" name="label">
<property name="text">
<string>Keyboard Shortcut</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="binding_le" />
<widget class="lay::DecoratedLineEdit" name="binding_le"/>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
@ -115,30 +130,30 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<widget class="QLabel" name="label_2">
<property name="text">
<string>Examples:
- "Ctrl+A"
- "Shift+F2"
- "M"
- &quot;Ctrl+A&quot;
- &quot;Shift+F2&quot;
- &quot;M&quot;
For special keys:
- "PgUp"
- "Left"
- "Enter"
- "Esc" or "Escape"
- "Ins" or "Insert"
- "Backspace"
- &quot;PgUp&quot;
- &quot;Left&quot;
- &quot;Enter&quot;
- &quot;Esc&quot; or &quot;Escape&quot;
- &quot;Ins&quot; or &quot;Insert&quot;
- &quot;Backspace&quot;
</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
@ -147,8 +162,8 @@ For special keys:
</spacer>
</item>
<item>
<widget class="QPushButton" name="reset_pb" >
<property name="text" >
<widget class="QPushButton" name="reset_pb">
<property name="text">
<string>Reset To Default</string>
</property>
</widget>
@ -158,7 +173,14 @@ For special keys:
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>lay::DecoratedLineEdit</class>
<extends>QLineEdit</extends>
<header>layWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -202,21 +202,9 @@ MacroController::uninitialize (lay::PluginRoot * /*root*/)
bool
MacroController::configure (const std::string &key, const std::string &value)
{
if (key == cfg_key_bindings && mp_mw) {
// Update the shortcuts of the menu item if they have been edited in the configuration editor
std::vector<std::pair<std::string, std::string> > key_bindings = unpack_key_binding (value);
for (std::vector<std::pair<std::string, std::string> >::const_iterator kb = key_bindings.begin (); kb != key_bindings.end (); ++kb) {
if (mp_mw->menu ()->is_valid (kb->first)) {
lay::Action a = mp_mw->menu ()->action (kb->first);
if (m_action_to_macro.find (a.qaction ()) != m_action_to_macro.end ()) {
m_action_to_macro [a.qaction ()]->set_shortcut (kb->second);
}
}
}
if (key == cfg_key_bindings) {
m_key_bindings = unpack_key_binding (value);
}
return false;
}
@ -639,7 +627,7 @@ static std::string menu_name (std::set<std::string> &used_names, const std::stri
}
void
MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std::set<std::string> &used_names, std::set<std::string> &groups, const lay::Technology *tech, std::vector<std::pair<std::string, std::string> > *key_bindings)
MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std::set<std::string> &used_names, std::set<std::string> &groups, const lay::Technology *tech)
{
for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) {
@ -655,7 +643,7 @@ MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std:
}
if (consider) {
add_macro_items_to_menu (*c->second, used_names, groups, 0 /*don't check 2nd level and below*/, key_bindings);
add_macro_items_to_menu (*c->second, used_names, groups, 0 /*don't check 2nd level and below*/);
}
}
@ -686,7 +674,7 @@ MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std:
} else {
a.set_title (c->second->description ());
}
a.set_shortcut (sc);
a.set_default_shortcut (sc);
m_macro_actions.push_back (a);
mp_mw->menu ()->insert_item (mp, menu_name (used_names, c->second->name ()), a);
@ -695,11 +683,6 @@ MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std:
lym::MacroSignalAdaptor *adaptor = new lym::MacroSignalAdaptor (a.qaction (), c->second);
QObject::connect (a.qaction (), SIGNAL (triggered ()), adaptor, SLOT (run ()));
// store the key bindings in the array
if (!sc.empty () && key_bindings) {
key_bindings->push_back (std::make_pair (mp, sc));
}
} else if (! sc.empty ()) {
// Create actions for shortcut-only actions too and add them to the main window
@ -772,19 +755,6 @@ MacroController::do_update_menu_with_macros ()
tech = lay::TechnologyController::instance ()->active_technology ();
}
std::vector<std::pair<std::string, std::string> > key_bindings = unpack_key_binding (mp_mw->config_get (cfg_key_bindings));
std::sort (key_bindings.begin (), key_bindings.end ());
std::vector<std::pair<std::string, std::string> > new_key_bindings;
for (std::vector<std::pair<std::string, std::string> >::const_iterator kb = key_bindings.begin (); kb != key_bindings.end (); ++kb) {
if (mp_mw->menu ()->is_valid (kb->first)) {
lay::Action a = mp_mw->menu ()->action (kb->first);
if (m_action_to_macro.find (a.qaction ()) == m_action_to_macro.end ()) {
new_key_bindings.push_back (*kb);
}
}
}
// delete all existing items
for (std::vector<lay::Action>::iterator a = m_macro_actions.begin (); a != m_macro_actions.end (); ++a) {
mp_mw->menu ()->delete_items (*a);
@ -794,13 +764,15 @@ MacroController::do_update_menu_with_macros ()
std::set<std::string> groups;
std::set<std::string> used_names;
add_macro_items_to_menu (m_temp_macros, used_names, groups, tech, 0);
add_macro_items_to_menu (lym::MacroCollection::root (), used_names, groups, tech, &new_key_bindings);
add_macro_items_to_menu (m_temp_macros, used_names, groups, tech);
add_macro_items_to_menu (lym::MacroCollection::root (), used_names, groups, tech);
// update the key bindings if required
std::sort (new_key_bindings.begin (), new_key_bindings.end ());
if (new_key_bindings != key_bindings) {
mp_mw->config_set (cfg_key_bindings, pack_key_binding (new_key_bindings));
// apply the custom keyboard shortcuts
for (std::vector<std::pair<std::string, std::string> >::const_iterator kb = m_key_bindings.begin (); kb != m_key_bindings.end (); ++kb) {
if (mp_mw->menu ()->is_valid (kb->first)) {
lay::Action a = mp_mw->menu ()->action (kb->first);
a.set_shortcut (kb->second);
}
}
}

View File

@ -246,9 +246,10 @@ private:
tl::DeferredMethod<MacroController> dm_do_sync_with_external_sources;
tl::DeferredMethod<MacroController> dm_sync_file_watcher;
tl::DeferredMethod<MacroController> dm_sync_files;
std::vector<std::pair<std::string, std::string> > m_key_bindings;
void sync_implicit_macros (bool ask_before_autorun);
void add_macro_items_to_menu (lym::MacroCollection &collection, std::set<std::string> &used_names, std::set<std::string> &groups, const lay::Technology *tech, std::vector<std::pair<std::string, std::string> > *key_bindings);
void add_macro_items_to_menu (lym::MacroCollection &collection, std::set<std::string> &used_names, std::set<std::string> &groups, const lay::Technology *tech);
void do_update_menu_with_macros ();
void do_sync_with_external_sources ();
void sync_file_watcher ();

View File

@ -369,6 +369,8 @@ KeyBindingsConfigPage::KeyBindingsConfigPage (QWidget *parent)
connect (mp_ui->reset_pb, SIGNAL (clicked ()), this, SLOT (reset_clicked ()));
mp_ui->binding_le->setEnabled (false);
mp_ui->binding_le->set_clear_button_enabled (true);
connect (mp_ui->binding_le, SIGNAL (textChanged (QString)), this, SLOT (text_changed ()));
}
KeyBindingsConfigPage::~KeyBindingsConfigPage ()
@ -377,43 +379,34 @@ KeyBindingsConfigPage::~KeyBindingsConfigPage ()
mp_ui = 0;
}
static void fill_paths (const lay::AbstractMenu &menu, const std::string &root, std::map<std::string, std::string> &bindings)
static void fill_paths (const lay::AbstractMenu &menu, const std::string &root, std::map<std::string, std::string> &bindings, bool with_defaults)
{
std::vector<std::string> items = menu.items (root);
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
if (i->size () > 0) {
if (menu.is_valid (*i) && menu.action (*i).is_visible ()) {
if (menu.is_menu (*i)) {
fill_paths (menu, *i, bindings);
fill_paths (menu, *i, bindings, with_defaults);
} else if (! menu.is_separator (*i)) {
bindings.insert (std::make_pair (*i, menu.action (*i).get_shortcut ()));
bindings.insert (std::make_pair (*i, with_defaults ? menu.action (*i).get_default_shortcut () : menu.action (*i).get_shortcut ()));
}
}
}
}
}
std::vector<std::pair<std::string, std::string> > KeyBindingsConfigPage::m_default_bindings;
void
KeyBindingsConfigPage::set_default ()
{
std::map<std::string, std::string> bm;
fill_paths (*lay::MainWindow::instance ()->menu (), std::string (), bm);
m_default_bindings.clear ();
m_default_bindings.insert (m_default_bindings.begin (), bm.begin (), bm.end ());
}
void
KeyBindingsConfigPage::reset_clicked ()
{
if (QMessageBox::question (this,
QObject::tr ("Confirm Reset"),
QObject::tr ("Are you sure to reset the key bindings?\nThis operation will clear all custom settings."),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) == QMessageBox::Yes) {
apply (m_default_bindings);
apply (std::vector<std::pair<std::string, std::string> > ());
}
}
@ -426,15 +419,23 @@ KeyBindingsConfigPage::apply (const std::vector<std::pair<std::string, std::stri
// get the current bindings
m_current_bindings.clear ();
fill_paths (*lay::MainWindow::instance ()->menu (), std::string (), m_current_bindings);
fill_paths (*lay::MainWindow::instance ()->menu (), std::string (), m_current_bindings, false);
// get the default bindings
std::map<std::string, std::string> default_bindings;
fill_paths (*lay::MainWindow::instance ()->menu (), std::string (), default_bindings, true);
m_enable_event = false;
// overwrite with the given ones
for (std::vector<std::pair<std::string, std::string> >::const_iterator kb = key_bindings.begin (); kb != key_bindings.end (); ++kb) {
std::map<std::string, std::string>::iterator cb = m_current_bindings.find (kb->first);
if (cb != m_current_bindings.end ()) {
cb->second = kb->second;
// clear bindings and initialize with the given ones
std::map<std::string, std::string> b;
b.insert (key_bindings.begin (), key_bindings.end ());
for (std::map<std::string, std::string>::iterator kb = m_current_bindings.begin (); kb != m_current_bindings.end (); ++kb) {
std::map<std::string, std::string>::iterator bb = b.find (kb->first);
if (bb != b.end ()) {
kb->second = bb->second;
} else {
kb->second.clear ();
}
}
@ -454,9 +455,19 @@ KeyBindingsConfigPage::apply (const std::vector<std::pair<std::string, std::stri
for (std::map<std::string, std::string>::const_iterator cb = m_current_bindings.begin (); cb != m_current_bindings.end (); ++cb) {
std::string tl_menu;
std::map<std::string, std::string>::const_iterator db = default_bindings.find (cb->first);
bool is_default = false;
std::string sc = cb->second;
if (sc.empty () && db != default_bindings.end ()) {
sc = db->second;
is_default = true;
}
const std::string &path = cb->first;
std::string rem_path = path;
std::string tl_menu;
std::string rem_path = path;
if (path.find ("@") == 0) {
size_t n = path.find (".");
if (n != std::string::npos) {
@ -470,7 +481,8 @@ KeyBindingsConfigPage::apply (const std::vector<std::pair<std::string, std::stri
lay::Action action = lay::MainWindow::instance ()->menu ()->action (cb->first);
item->setData (0, Qt::DisplayRole, tl::to_qstring (rem_path));
item->setData (1, Qt::DisplayRole, tl::to_qstring (action.get_title ()));
item->setData (2, Qt::DisplayRole, tl::to_qstring (cb->second));
item->setData (2, Qt::DisplayRole, tl::to_qstring (sc));
item->setData (2, Qt::ForegroundRole, palette ().color (is_default ? QPalette::Disabled : QPalette::Normal, QPalette::Text));
item->setData (0, Qt::UserRole, tl::to_qstring (path));
m_item_for_path[path] = item;
if (action.qaction ()) {
@ -484,6 +496,7 @@ KeyBindingsConfigPage::apply (const std::vector<std::pair<std::string, std::stri
}
mp_ui->binding_le->setText (QString ());
mp_ui->binding_le->setPlaceholderText (QString ());
mp_ui->binding_le->setEnabled (false);
m_enable_event = true;
@ -527,6 +540,54 @@ KeyBindingsConfigPage::commit (lay::PluginRoot *root)
root->config_set (cfg_key_bindings, packed_key_bindings);
}
void
KeyBindingsConfigPage::text_changed ()
{
if (m_enable_event) {
update_list_item (mp_ui->bindings_list->currentItem ());
}
}
void
KeyBindingsConfigPage::update_list_item (QTreeWidgetItem *item)
{
if (! item || ! mp_ui->binding_le->isEnabled ()) {
return;
}
std::string path = tl::to_string (item->data (0, Qt::UserRole).toString ());
std::string shortcut = tl::to_string (mp_ui->binding_le->text ().simplified ());
m_current_bindings[path] = shortcut;
bool is_default = false;
std::string eff_shortcut = shortcut;
if (shortcut.empty ()) {
lay::Action a = lay::MainWindow::instance ()->menu ()->action (path);
eff_shortcut = a.get_default_shortcut ();
is_default = true;
}
item->setData (2, Qt::DisplayRole, tl::to_qstring (eff_shortcut));
item->setData (2, Qt::ForegroundRole, palette ().color (is_default ? QPalette::Disabled : QPalette::Normal, QPalette::Text));
// Set the aliases too
const lay::AbstractMenu &menu = *lay::MainWindow::instance ()->menu ();
if (menu.is_valid (path)) {
QAction *qaction = menu.action (path).qaction ();
std::map<QAction *, std::vector<std::string> >::const_iterator a = m_paths_for_action.find (qaction);
if (a != m_paths_for_action.end ()) {
for (std::vector<std::string>::const_iterator p = a->second.begin (); p != a->second.end (); ++p) {
m_current_bindings[*p] = shortcut;
std::map<std::string, QTreeWidgetItem *>::const_iterator i = m_item_for_path.find (*p);
if (i != m_item_for_path.end ()) {
i->second->setData (2, Qt::DisplayRole, tl::to_qstring (eff_shortcut));
i->second->setData (2, Qt::ForegroundRole, palette ().color (is_default ? QPalette::Disabled : QPalette::Normal, QPalette::Text));
}
}
}
}
}
void
KeyBindingsConfigPage::current_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
@ -534,41 +595,29 @@ KeyBindingsConfigPage::current_changed (QTreeWidgetItem *current, QTreeWidgetIte
return;
}
if (previous && mp_ui->binding_le->isEnabled ()) {
update_list_item (previous);
QKeySequence key_sequence (mp_ui->binding_le->text ());
previous->setData (2, Qt::DisplayRole, key_sequence.toString ());
std::string path = tl::to_string (previous->data (0, Qt::UserRole).toString ());
std::string shortcut = tl::to_string (previous->data (2, Qt::DisplayRole).toString ());
m_current_bindings[path] = shortcut;
// Set the aliases too
const lay::AbstractMenu &menu = *lay::MainWindow::instance ()->menu ();
if (menu.is_valid (path)) {
QAction *qaction = menu.action (path).qaction ();
std::map<QAction *, std::vector<std::string> >::const_iterator a = m_paths_for_action.find (qaction);
if (a != m_paths_for_action.end ()) {
for (std::vector<std::string>::const_iterator p = a->second.begin (); p != a->second.end (); ++p) {
m_current_bindings[*p] = shortcut;
std::map<std::string, QTreeWidgetItem *>::const_iterator i = m_item_for_path.find (*p);
if (i != m_item_for_path.end ()) {
i->second->setData (2, Qt::DisplayRole, tl::to_qstring (shortcut));
}
}
}
}
}
m_enable_event = false;
if (current && !current->data (0, Qt::UserRole).isNull ()) {
mp_ui->binding_le->setText (current->data (2, Qt::DisplayRole).toString ());
std::string path = tl::to_string (current->data (0, Qt::UserRole).toString ());
std::string shortcut = m_current_bindings[path];
lay::Action a = lay::MainWindow::instance ()->menu ()->action (path);
std::string def_shortcut = a.get_default_shortcut ();
mp_ui->binding_le->setText (tl::to_qstring (shortcut));
mp_ui->binding_le->setPlaceholderText (tl::to_qstring (def_shortcut));
mp_ui->binding_le->setEnabled (true);
} else {
mp_ui->binding_le->setText (QString ());
mp_ui->binding_le->setPlaceholderText (QString ());
mp_ui->binding_le->setEnabled (false);
}
m_enable_event = true;
}
}

View File

@ -184,10 +184,9 @@ public:
virtual void setup (lay::PluginRoot *root);
virtual void commit (lay::PluginRoot *root);
static void set_default ();
public slots:
private slots:
void current_changed (QTreeWidgetItem *current, QTreeWidgetItem *previous);
void text_changed ();
void reset_clicked ();
private:
@ -196,9 +195,9 @@ private:
std::map<std::string, QTreeWidgetItem *> m_item_for_path;
std::map<QAction *, std::vector<std::string> > m_paths_for_action;
bool m_enable_event;
static std::vector<std::pair<std::string, std::string> > m_default_bindings;
void apply (const std::vector<std::pair<std::string, std::string> > &bindings);
void update_list_item (QTreeWidgetItem *item);
};
}

View File

@ -1032,7 +1032,7 @@ MainWindow::init_menu ()
title = tab + 1;
}
Action action (AbstractMenu::create_action (title));
Action action (title);
action.set_checkable (true);
action.qaction ()->setData (QVariant (mode_id));
action.add_to_exclusive_group (mp_menu, "mouse_mode_exclusive_group");
@ -1078,9 +1078,6 @@ MainWindow::init_menu ()
for (std::vector<std::string>::const_iterator g = view_mode_grp.begin (); g != view_mode_grp.end (); ++g) {
mp_menu->action (*g).set_visible (view_mode);
}
// get and store the initial (default) key bindings for restore in the setup dialog
KeyBindingsConfigPage::set_default ();
}
void
@ -4930,7 +4927,7 @@ MainWindow::action_for_slot (const char *slot)
if (a != m_actions_for_slot.end ()) {
return a->second;
} else {
Action a (new ActionHandle (this));
Action a = Action::create_free_action (this);
gtf::action_connect (a.qaction (), SIGNAL (triggered ()), this, slot);
return m_actions_for_slot.insert (std::make_pair (std::string (slot), a)).first->second;
}

View File

@ -239,7 +239,9 @@ public:
ActionHandle::ActionHandle (QWidget *parent)
: mp_action (new ActionObject (parent)),
m_ref_count (0),
m_owned (true)
m_owned (true),
m_visible (true),
m_hidden (false)
{
if (! sp_actionHandles) {
sp_actionHandles = new std::set<ActionHandle *> ();
@ -253,7 +255,9 @@ ActionHandle::ActionHandle (QWidget *parent)
ActionHandle::ActionHandle (QAction *action, bool owned)
: mp_action (action),
m_ref_count (0),
m_owned (owned)
m_owned (owned),
m_visible (true),
m_hidden (false)
{
if (! sp_actionHandles) {
sp_actionHandles = new std::set<ActionHandle *> ();
@ -310,6 +314,89 @@ ActionHandle::destroyed (QObject * /*obj*/)
m_owned = false;
}
void
ActionHandle::set_visible (bool v)
{
if (m_visible != v) {
m_visible = v;
if (mp_action) {
mp_action->setVisible (is_effective_visible ());
}
}
}
void
ActionHandle::set_hidden (bool h)
{
if (m_hidden != h) {
m_hidden = h;
if (mp_action) {
mp_action->setVisible (is_effective_visible ());
}
}
}
bool
ActionHandle::is_visible () const
{
return m_visible;
}
bool
ActionHandle::is_hidden () const
{
return m_hidden;
}
bool
ActionHandle::is_effective_visible () const
{
return m_visible && !m_hidden;
}
void
ActionHandle::set_default_shortcut (const QKeySequence &sc)
{
if (m_default_shortcut != sc) {
m_default_shortcut = sc;
if (mp_action) {
mp_action->setShortcut (get_effective_shortcut ());
}
}
}
void
ActionHandle::set_shortcut (const QKeySequence &sc)
{
if (m_shortcut != sc) {
m_shortcut = sc;
if (mp_action) {
mp_action->setShortcut (get_effective_shortcut ());
}
}
}
const QKeySequence &
ActionHandle::get_default_shortcut () const
{
return m_default_shortcut;
}
const QKeySequence &
ActionHandle::get_shortcut () const
{
return m_shortcut;
}
QKeySequence
ActionHandle::get_effective_shortcut () const
{
if (m_shortcut.isEmpty ()) {
return m_default_shortcut;
} else {
return m_shortcut;
}
}
// ---------------------------------------------------------------
// Action implementation
@ -338,6 +425,12 @@ Action::Action (ActionHandle *handle)
mp_handle->add_ref ();
}
Action
Action::create_free_action (QWidget *parent)
{
return Action (new ActionHandle (parent));
}
Action::Action (const Action &action)
: QObject ()
{
@ -422,24 +515,56 @@ Action::get_title () const
void
Action::set_shortcut (const QKeySequence &s)
{
if (qaction ()) {
qaction ()->setShortcut (s);
if (mp_handle) {
mp_handle->set_shortcut (s);
}
}
void
void
Action::set_default_shortcut (const QKeySequence &s)
{
if (mp_handle) {
mp_handle->set_default_shortcut (s);
}
}
void
Action::set_shortcut (const std::string &s)
{
if (qaction () && s != get_shortcut ()) {
qaction ()->setShortcut (QKeySequence (tl::to_qstring (s)));
set_shortcut (QKeySequence (tl::to_qstring (s)));
}
void
Action::set_default_shortcut (const std::string &s)
{
set_default_shortcut (QKeySequence (tl::to_qstring (s)));
}
std::string
Action::get_effective_shortcut () const
{
if (mp_handle) {
return tl::to_string (mp_handle->get_effective_shortcut ().toString ());
} else {
return std::string ();
}
}
std::string
std::string
Action::get_shortcut () const
{
if (qaction ()) {
return tl::to_string (qaction ()->shortcut ().toString ());
if (mp_handle) {
return tl::to_string (mp_handle->get_shortcut ().toString ());
} else {
return std::string ();
}
}
std::string
Action::get_default_shortcut () const
{
if (mp_handle) {
return tl::to_string (mp_handle->get_default_shortcut ().toString ());
} else {
return std::string ();
}
@ -478,7 +603,19 @@ Action::is_enabled () const
bool
Action::is_visible () const
{
return qaction () && qaction ()->isVisible ();
return mp_handle && mp_handle->is_visible ();
}
bool
Action::is_hidden () const
{
return mp_handle && mp_handle->is_hidden ();
}
bool
Action::is_effective_visible () const
{
return mp_handle && mp_handle->is_effective_visible ();
}
bool
@ -498,8 +635,16 @@ Action::set_enabled (bool b)
void
Action::set_visible (bool v)
{
if (qaction ()) {
qaction ()->setVisible (v);
if (mp_handle) {
mp_handle->set_visible (v);
}
}
void
Action::set_hidden (bool h)
{
if (mp_handle) {
mp_handle->set_hidden (h);
}
}
@ -705,7 +850,7 @@ AbstractMenu::create_action (const std::string &s)
}
if (! shortcut.empty ()) {
ah->ptr ()->setShortcut (QKeySequence (tl::to_qstring (shortcut)));
ah->set_default_shortcut (QKeySequence (tl::to_qstring (shortcut)));
}
return ah;
@ -1036,7 +1181,7 @@ AbstractMenu::insert_menu (const std::string &p, const std::string &name, const
void
AbstractMenu::insert_menu (const std::string &path, const std::string &name, const std::string &title)
{
insert_menu (path, name, Action (create_action (title)));
insert_menu (path, name, create_action (title));
}
void
@ -1324,7 +1469,7 @@ AbstractMenu::transfer (const MenuLayoutEntry *layout, AbstractMenuItem &item)
a.set_title (title);
if (! shortcut.empty ()) {
a.set_shortcut (QKeySequence (tl::to_qstring (shortcut)));
a.set_default_shortcut (shortcut);
}
if (! tool_tip.empty ()) {

View File

@ -57,7 +57,7 @@ class PluginRoot;
/**
* @brief A helper class that does reference counting for the QAction object
*/
class LAYBASIC_PUBLIC ActionHandle
class ActionHandle
: public QObject
{
Q_OBJECT
@ -70,6 +70,18 @@ public:
void remove_ref ();
QAction *ptr () const;
void set_visible (bool v);
void set_hidden (bool h);
bool is_visible () const;
bool is_hidden () const;
bool is_effective_visible () const;
void set_default_shortcut (const QKeySequence &sc);
void set_shortcut (const QKeySequence &sc);
const QKeySequence &get_default_shortcut () const;
const QKeySequence &get_shortcut () const;
QKeySequence get_effective_shortcut () const;
protected slots:
void destroyed (QObject *obj);
@ -77,6 +89,10 @@ private:
QAction *mp_action;
int m_ref_count;
bool m_owned;
bool m_visible;
bool m_hidden;
QKeySequence m_default_shortcut;
QKeySequence m_shortcut;
// no copying
ActionHandle (const ActionHandle &);
@ -125,20 +141,18 @@ public:
*/
Action (const std::string &title);
/**
* @brief Creates a new free action
* This constructor wil create a new action with it's own QAction object
* under the given parent.
*/
static Action create_free_action (QWidget *parent);
/**
* @brief Assignement
*/
Action &operator= (const Action &action);
/**
* @brief The proxy constructor
*
* This constructor takes a QAction object that it will refer to.
* If the Action is copied, the copy will refer to the same QAction.
* The QAction object is deleted if the last Action referring to QAction is deleted.
*/
Action (ActionHandle *action);
/**
* @brief The destructor
*/
@ -155,20 +169,48 @@ public:
std::string get_title () const;
/**
* @brief Set the keyboard shortcut (as a QKeySequence object)
* @brief Sets the keyboard shortcut (as a QKeySequence object)
* If no shortcut is set, the default shortcut will be taken.
*/
void set_shortcut (const QKeySequence &s);
/**
* @brief Set the keyboard shortcut
* @brief Sets the keyboard shortcut
* If no shortcut is set, the default shortcut will be taken.
*/
void set_shortcut (const std::string &s);
/**
* @brief Get the keyboard shortcut
* @brief Gets the keyboard shortcut
* To get the effective shortcut (combination of default shortcut and shortcut),
* use "get_effective_shortcut".
*/
std::string get_shortcut () const;
/**
* @brief Sets the default keyboard shortcut (as a QKeySequence object)
* This shortcut is used when no specific shortcut is set.
*/
void set_default_shortcut (const QKeySequence &s);
/**
* @brief Sets the default keyboard shortcut
* This shortcut is used when no specific shortcut is set.
*/
void set_default_shortcut (const std::string &s);
/**
* @brief Gets the default keyboard shortcut
* To get the effective shortcut (combination of default shortcut and shortcut),
* use "get_effective_shortcut".
*/
std::string get_default_shortcut () const;
/**
* @brief Gets the effective shortcut
*/
std::string get_effective_shortcut () const;
/**
* @brief "is_checkable" attribute
*/
@ -189,6 +231,20 @@ public:
*/
bool is_visible () const;
/**
* @brief Gets a value indicating whether the action is intentionally hidden
* This flag combines with the visibility. "is_effective_visible" is false
* if hidden is true. This feature allows implementation of the menu configuration
* feature where users can deliberately switch off and on menu items.
*/
bool is_hidden () const;
/**
* @brief Gets the effective visibility
* See "is_hidden" for details.
*/
bool is_effective_visible () const;
/**
* @brief "is_separator" attribute
*/
@ -204,6 +260,12 @@ public:
*/
void set_visible (bool v);
/**
* @brief Sets a value indicating whether the menu item is hidden
* See "is_hidden" for details.
*/
void set_hidden (bool h);
/**
* @brief Make checkable or not
*/
@ -295,7 +357,18 @@ public slots:
void triggered_slot ();
private:
friend class AbstractMenu;
ActionHandle *mp_handle;
/**
* @brief The proxy constructor
*
* This constructor takes a QAction object that it will refer to.
* If the Action is copied, the copy will refer to the same QAction.
* The QAction object is deleted if the last Action referring to QAction is deleted.
*/
Action (ActionHandle *action);
};
/**
@ -738,17 +811,6 @@ public:
*/
QMenu *detached_menu (const std::string &name);
/**
* @brief Create a action from a string
*
* The format of the string is: <text>["("shortcut")"]["<"icon-resource">"]
*
* @param s The title, key and icon resource string in the format given above
* @param parent The widget to which to attach the QAction object
* @return The ActionHandle object created
*/
static ActionHandle *create_action (const std::string &s);
/**
* @brief Creates a new exclusive action group
*
@ -772,7 +834,9 @@ signals:
*/
void changed ();
private:
private:
friend class Action;
std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator> > find_item (const std::string &path);
const AbstractMenuItem *find_item_exact (const std::string &path) const;
AbstractMenuItem *find_item_exact (const std::string &path);
@ -782,6 +846,17 @@ private:
void collect_group (std::vector<std::string> &grp, const std::string &name, const AbstractMenuItem &item) const;
void reset_menu_objects (AbstractMenuItem &item);
/**
* @brief Create a action from a string
*
* The format of the string is: <text>["("shortcut")"]["<"icon-resource">"]
*
* @param s The title, key and icon resource string in the format given above
* @param parent The widget to which to attach the QAction object
* @return The ActionHandle object created
*/
static ActionHandle *create_action (const std::string &s);
AbstractMenuProvider *mp_provider;
AbstractMenuItem m_root;
tl::stable_vector<QMenu> m_helper_menu_items;

View File

@ -139,7 +139,7 @@ PluginDeclaration::init_menu ()
title = tab + 1;
}
m_editable_mode_action = AbstractMenu::create_action (title);
m_editable_mode_action = Action (title);
gtf::action_connect (m_editable_mode_action.qaction (), SIGNAL (triggered ()), this, SLOT (toggle_editable_enabled ()));
m_editable_mode_action.qaction ()->setData (id ());
m_editable_mode_action.set_checkable (true);
@ -163,7 +163,7 @@ PluginDeclaration::init_menu ()
menu.insert_menu (m->insert_pos, m->menu_name, m->title);
} else {
Action action (AbstractMenu::create_action (m->title));
Action action (m->title);
action.qaction ()->setData (QVariant (tl::to_qstring (m->symbol)));
gtf::action_connect (action.qaction (), SIGNAL (triggered ()), this, SLOT (generic_menu ()));
menu.insert_item (m->insert_pos, m->menu_name, action);
@ -189,7 +189,7 @@ PluginDeclaration::init_menu ()
title = std::string (tab + 1);
}
m_mouse_mode_action = AbstractMenu::create_action (title);
m_mouse_mode_action = Action (title);
m_mouse_mode_action.add_to_exclusive_group (&menu, "mouse_mode_exclusive_group");
m_mouse_mode_action.set_checkable (true);