From 26ecc6e1a6dba0dd1fa918bde30ef3dd616899af Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 29 Oct 2017 13:34:57 +0100 Subject: [PATCH] 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. --- src/lay/lay/KeyBindingsConfigPage.ui | 130 ++++++++++------- src/lay/lay/layMacroController.cc | 54 ++----- src/lay/lay/layMacroController.h | 3 +- src/lay/lay/layMainConfigPages.cc | 153 +++++++++++++------- src/lay/lay/layMainConfigPages.h | 7 +- src/lay/lay/layMainWindow.cc | 7 +- src/laybasic/laybasic/layAbstractMenu.cc | 177 +++++++++++++++++++++-- src/laybasic/laybasic/layAbstractMenu.h | 125 ++++++++++++---- src/laybasic/laybasic/layPlugin.cc | 6 +- 9 files changed, 461 insertions(+), 201 deletions(-) diff --git a/src/lay/lay/KeyBindingsConfigPage.ui b/src/lay/lay/KeyBindingsConfigPage.ui index 4cd5e3d6d..c9822895c 100644 --- a/src/lay/lay/KeyBindingsConfigPage.ui +++ b/src/lay/lay/KeyBindingsConfigPage.ui @@ -1,7 +1,8 @@ - + + KeyBindingsConfigPage - - + + 0 0 @@ -9,78 +10,92 @@ 495 - - - 5 - 7 + + 0 1 - + Settings - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - - - 7 - 7 + + + 1 0 - + true - + Path - + Title - + Shortcut - - + + QFrame::NoFrame - + QFrame::Raised - - + + + 6 + + 9 - - 6 + + 9 + + + 9 + + + 9 - + Qt::Vertical - + QSizePolicy::Fixed - + 20 40 @@ -89,24 +104,24 @@ - - + + Keyboard Shortcut - + - + Qt::Vertical - + QSizePolicy::Fixed - + 20 10 @@ -115,30 +130,30 @@ - - + + Examples: - - "Ctrl+A" - - "Shift+F2" - - "M" + - "Ctrl+A" + - "Shift+F2" + - "M" For special keys: - - "PgUp" - - "Left" - - "Enter" - - "Esc" or "Escape" - - "Ins" or "Insert" - - "Backspace" + - "PgUp" + - "Left" + - "Enter" + - "Esc" or "Escape" + - "Ins" or "Insert" + - "Backspace" - + Qt::Vertical - + 20 40 @@ -147,8 +162,8 @@ For special keys: - - + + Reset To Default @@ -158,7 +173,14 @@ For special keys: - + + + + lay::DecoratedLineEdit + QLineEdit +
layWidgets.h
+
+
diff --git a/src/lay/lay/layMacroController.cc b/src/lay/lay/layMacroController.cc index f84d8f4e9..b708fc15c 100644 --- a/src/lay/lay/layMacroController.cc +++ b/src/lay/lay/layMacroController.cc @@ -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 > key_bindings = unpack_key_binding (value); - for (std::vector >::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 &used_names, const std::stri } void -MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std::set &used_names, std::set &groups, const lay::Technology *tech, std::vector > *key_bindings) +MacroController::add_macro_items_to_menu (lym::MacroCollection &collection, std::set &used_names, std::set &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 > key_bindings = unpack_key_binding (mp_mw->config_get (cfg_key_bindings)); - std::sort (key_bindings.begin (), key_bindings.end ()); - - std::vector > new_key_bindings; - for (std::vector >::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::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 groups; std::set 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 >::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); + } } } diff --git a/src/lay/lay/layMacroController.h b/src/lay/lay/layMacroController.h index 49881a5b2..29f8f70f0 100644 --- a/src/lay/lay/layMacroController.h +++ b/src/lay/lay/layMacroController.h @@ -246,9 +246,10 @@ private: tl::DeferredMethod dm_do_sync_with_external_sources; tl::DeferredMethod dm_sync_file_watcher; tl::DeferredMethod dm_sync_files; + std::vector > m_key_bindings; void sync_implicit_macros (bool ask_before_autorun); - void add_macro_items_to_menu (lym::MacroCollection &collection, std::set &used_names, std::set &groups, const lay::Technology *tech, std::vector > *key_bindings); + void add_macro_items_to_menu (lym::MacroCollection &collection, std::set &used_names, std::set &groups, const lay::Technology *tech); void do_update_menu_with_macros (); void do_sync_with_external_sources (); void sync_file_watcher (); diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index d09abe1bf..081e0fe2f 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -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 &bindings) +static void fill_paths (const lay::AbstractMenu &menu, const std::string &root, std::map &bindings, bool with_defaults) { std::vector items = menu.items (root); for (std::vector::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 > KeyBindingsConfigPage::m_default_bindings; - -void -KeyBindingsConfigPage::set_default () -{ - std::map 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 > ()); + } } @@ -426,15 +419,23 @@ KeyBindingsConfigPage::apply (const std::vectormenu (), std::string (), m_current_bindings); + fill_paths (*lay::MainWindow::instance ()->menu (), std::string (), m_current_bindings, false); + + // get the default bindings + std::map 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 >::const_iterator kb = key_bindings.begin (); kb != key_bindings.end (); ++kb) { - std::map::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 b; + b.insert (key_bindings.begin (), key_bindings.end ()); + for (std::map::iterator kb = m_current_bindings.begin (); kb != m_current_bindings.end (); ++kb) { + std::map::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::const_iterator cb = m_current_bindings.begin (); cb != m_current_bindings.end (); ++cb) { - std::string tl_menu; + std::map::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::vectormenu ()->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::vectorbinding_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 >::const_iterator a = m_paths_for_action.find (qaction); + if (a != m_paths_for_action.end ()) { + for (std::vector::const_iterator p = a->second.begin (); p != a->second.end (); ++p) { + m_current_bindings[*p] = shortcut; + std::map::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 >::const_iterator a = m_paths_for_action.find (qaction); - if (a != m_paths_for_action.end ()) { - for (std::vector::const_iterator p = a->second.begin (); p != a->second.end (); ++p) { - m_current_bindings[*p] = shortcut; - std::map::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; } } diff --git a/src/lay/lay/layMainConfigPages.h b/src/lay/lay/layMainConfigPages.h index da1e0037d..21c3719ef 100644 --- a/src/lay/lay/layMainConfigPages.h +++ b/src/lay/lay/layMainConfigPages.h @@ -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 m_item_for_path; std::map > m_paths_for_action; bool m_enable_event; - static std::vector > m_default_bindings; void apply (const std::vector > &bindings); + void update_list_item (QTreeWidgetItem *item); }; } diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 7e5a9f17a..6fccaaa52 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -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::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; } diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc index c11c199b1..c1338f9b1 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -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 (); @@ -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 (); @@ -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 ()) { diff --git a/src/laybasic/laybasic/layAbstractMenu.h b/src/laybasic/laybasic/layAbstractMenu.h index 171b70982..e5ece7024 100644 --- a/src/laybasic/laybasic/layAbstractMenu.h +++ b/src/laybasic/laybasic/layAbstractMenu.h @@ -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: ["("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::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 &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: ["("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 m_helper_menu_items; diff --git a/src/laybasic/laybasic/layPlugin.cc b/src/laybasic/laybasic/layPlugin.cc index 15660e123..173ddb089 100644 --- a/src/laybasic/laybasic/layPlugin.cc +++ b/src/laybasic/laybasic/layPlugin.cc @@ -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);