/* KLayout Layout Viewer Copyright (C) 2006-2016 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "layAbstractMenu.h" #include "layAbstractMenuProvider.h" #include "tlExceptions.h" #include "layPlugin.h" #include "tlAssert.h" #include "gtf.h" #include "gsi.h" #include #include #include #include #include #include #include #include #include namespace lay { // --------------------------------------------------------------- // Helper function to parse a title with potential shortcut and // icon specification static void parse_menu_title (const std::string &s, std::string &title, std::string &shortcut, std::string &icon_res, std::string &tool_tip) { const char *p = s.c_str (); while (*p) { if (*p == '\\' && p[1]) { ++p; title += *p++; } else if (*p == '(' || *p == '<' || *p == '{') { break; } else { title += *p++; } } do { if (*p == '(') { ++p; while (*p && *p != ')') { shortcut += *p++; } if (*p == ')') { ++p; } } else if (*p == '{') { ++p; while (*p && *p != '}') { tool_tip += *p++; } if (*p == '}') { ++p; } } else if (*p == '<') { ++p; while (*p && *p != '>') { icon_res += *p++; } if (*p == '>') { ++p; } } while (*p && isspace (*p)) { ++p; } } while (*p); } // --------------------------------------------------------------- // AbstractMenuItem implementation AbstractMenuItem::AbstractMenuItem () : mp_menu (0), m_has_submenu (false) { // ... nothing yet .. } AbstractMenuItem::AbstractMenuItem (const AbstractMenuItem &) : mp_menu (0), m_has_submenu (false) { // ... nothing yet .. } void AbstractMenuItem::setup_item (const std::string &pn, const std::string &s, const Action &a) { m_basename.clear (); tl::Extractor ex (s.c_str ()); m_name = pn; if (! m_name.empty ()) { m_name += "."; } if (! ex.at_end ()) { ex.read (m_basename, ":"); m_name += m_basename; while (ex.test (":")) { std::string g; ex.read (g, ":"); m_groups.insert (g); } } set_action (a, false); } void AbstractMenuItem::set_action (const Action &a, bool copy_properties) { Action acopy = a; if (copy_properties && m_action.qaction () && acopy.qaction ()) { acopy.qaction ()->setIcon (m_action.qaction ()->icon ()); acopy.qaction ()->setToolTip (m_action.qaction ()->toolTip ()); acopy.qaction ()->setShortcut (m_action.qaction ()->shortcut ()); acopy.qaction ()->setIconText (m_action.qaction ()->iconText ()); } bool enabled = m_action.is_enabled (); bool visible = m_action.is_visible (); m_action = acopy; m_action.set_enabled (enabled); m_action.set_visible (visible); m_action.set_object_name (m_basename); } void AbstractMenuItem::set_action_title (const std::string &s) { m_action.set_title (s); } void AbstractMenuItem::set_has_submenu () { m_has_submenu = true; } void AbstractMenuItem::set_menu (QMenu *menu) { mp_menu = menu; if (mp_menu) { mp_menu->setObjectName (tl::to_qstring (m_basename)); } } // --------------------------------------------------------------- // Action implementation static std::set *sp_actionHandles = 0; /** * @brief A specialization that provides a way to catch ambiguous key shortcuts */ class ActionObject : public QAction { public: ActionObject (QObject *parent) : QAction (parent) { } bool event(QEvent *e) { if (e->type() == QEvent::Shortcut) { QShortcutEvent *se = static_cast(e); if (se->isAmbiguous() && sp_actionHandles) { QString msg = QObject::tr ("Keyboard shortcut is ambiguous: "); msg += QString (se->key ().toString ()); msg += tl::to_qstring ("\n\n"); msg += QObject::tr ("Targets of the that shortcut are:"); msg += tl::to_qstring ("\n"); for (std::set::const_iterator a = sp_actionHandles->begin (); a != sp_actionHandles->end (); ++a) { if (! (*a)->ptr ()->shortcut ().isEmpty () && (*a)->ptr ()->shortcut ().matches (se->key ()) != QKeySequence::NoMatch) { msg += QChar (0x2022) /*bullet*/; msg += tl::to_qstring (" "); msg += (*a)->ptr ()->text (); msg += tl::to_qstring ("\n"); } } QMessageBox::warning (0, QObject::tr ("Warning"), msg); return true; } } return QAction::event(e); } }; ActionHandle::ActionHandle (QWidget *parent) : mp_action (new ActionObject (parent)), m_ref_count (0), m_owned (true) { if (! sp_actionHandles) { sp_actionHandles = new std::set (); } sp_actionHandles->insert (this); // catch the destroyed signal to tell if the QAction object is deleted. connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (destroyed (QObject *))); } ActionHandle::ActionHandle (QAction *action, bool owned) : mp_action (action), m_ref_count (0), m_owned (owned) { if (! sp_actionHandles) { sp_actionHandles = new std::set (); } sp_actionHandles->insert (this); // catch the destroyed signal to tell if the QAction object is deleted. connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (destroyed (QObject *))); } ActionHandle::~ActionHandle () { if (sp_actionHandles) { sp_actionHandles->erase (this); if (sp_actionHandles->empty ()) { delete sp_actionHandles; sp_actionHandles = 0; } } if (mp_action) { if (m_owned) { delete mp_action; m_owned = false; } mp_action = 0; } } void ActionHandle::add_ref () { ++m_ref_count; } void ActionHandle::remove_ref () { if (--m_ref_count == 0) { delete this; } } QAction * ActionHandle::ptr () const { return mp_action; } void ActionHandle::destroyed (QObject * /*obj*/) { mp_action = 0; m_owned = false; } // --------------------------------------------------------------- // Action implementation Action::Action () : mp_handle (0) { if (lay::AbstractMenuProvider::instance ()) { mp_handle = new ActionHandle (lay::AbstractMenuProvider::instance ()->menu_parent_widget ()); gtf::action_connect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); mp_handle->add_ref (); } } Action::Action (const std::string &title) { mp_handle = AbstractMenu::create_action (title); gtf::action_connect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); mp_handle->add_ref (); } Action::Action (ActionHandle *handle) { mp_handle = handle; gtf::action_connect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); mp_handle->add_ref (); } Action::Action (const Action &action) : QObject () { mp_handle = action.mp_handle; if (mp_handle) { gtf::action_connect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); mp_handle->add_ref (); } } Action & Action::operator= (const Action &action) { if (this != &action) { if (mp_handle) { if (mp_handle->ptr ()) { gtf::action_disconnect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); } mp_handle->remove_ref (); mp_handle = 0; } mp_handle = action.mp_handle; if (mp_handle) { gtf::action_connect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); mp_handle->add_ref (); } } return *this; } Action::~Action () { if (mp_handle) { if (mp_handle->ptr ()) { gtf::action_disconnect (mp_handle->ptr (), SIGNAL (triggered ()), this, SLOT (triggered_slot ())); } mp_handle->remove_ref (); mp_handle = 0; } } void Action::trigger () { if (qaction ()) { qaction ()->trigger (); } } void Action::triggered () { // .. no action yet, the reimplementation must provide some .. } void Action::triggered_slot () { BEGIN_PROTECTED triggered (); END_PROTECTED } void Action::set_title (const std::string &t) { if (qaction ()) { qaction ()->setText (tl::to_qstring (t)); } } std::string Action::get_title () const { if (qaction ()) { return tl::to_string (qaction ()->text ()); } else { return std::string (); } } void Action::set_shortcut (const QKeySequence &s) { if (qaction ()) { qaction ()->setShortcut (s); } } void Action::set_shortcut (const std::string &s) { if (qaction () && s != get_shortcut ()) { qaction ()->setShortcut (QKeySequence (tl::to_qstring (s))); } } std::string Action::get_shortcut () const { if (qaction ()) { return tl::to_string (qaction ()->shortcut ().toString ()); } else { return std::string (); } } QAction * Action::qaction () const { return mp_handle ? mp_handle->ptr () : 0; } void Action::add_to_exclusive_group (lay::AbstractMenu *menu, const std::string &group_name) { menu->make_exclusive_group (group_name)->addAction (qaction ()); } bool Action::is_checkable () const { return qaction () && qaction ()->isCheckable (); } bool Action::is_checked () const { return qaction () && qaction ()->isChecked (); } bool Action::is_enabled () const { return qaction () && qaction ()->isEnabled (); } bool Action::is_visible () const { return qaction () && qaction ()->isVisible (); } bool Action::is_separator () const { return qaction () && qaction ()->isSeparator (); } void Action::set_enabled (bool b) { if (qaction ()) { qaction ()->setEnabled (b); } } void Action::set_visible (bool v) { if (qaction ()) { qaction ()->setVisible (v); } } void Action::set_checked (bool c) { if (qaction ()) { qaction ()->setChecked (c); } } void Action::set_checkable (bool c) { if (qaction ()) { qaction ()->setCheckable (c); } } void Action::set_separator (bool s) { if (qaction ()) { qaction ()->setSeparator (s); } } void Action::set_icon (const std::string &filename) { if (qaction ()) { if (filename.empty ()) { qaction ()->setIcon (QIcon ()); } else { qaction ()->setIcon (QIcon (tl::to_qstring (filename))); } } } std::string Action::get_tool_tip () const { if (qaction ()) { return tl::to_string (qaction ()->toolTip ()); } else { return std::string (); } } void Action::set_tool_tip (const std::string &text) { if (qaction ()) { if (text.empty ()) { qaction ()->setToolTip (QString ()); } else { qaction ()->setToolTip (tl::to_qstring (text)); } } } std::string Action::get_icon_text () const { if (qaction ()) { return tl::to_string (qaction ()->iconText ()); } else { return std::string (); } } void Action::set_icon_text (const std::string &icon_text) { if (qaction ()) { if (icon_text.empty ()) { qaction ()->setIconText (QString ()); } else { qaction ()->setIconText (tl::to_qstring (icon_text)); } } } void Action::set_object_name (const std::string &name) { if (qaction ()) { qaction ()->setObjectName (tl::to_qstring (name)); } } // --------------------------------------------------------------- // ConfigureAction implementation ConfigureAction::ConfigureAction (lay::PluginRoot *pr) : Action (), m_pr (pr), m_type (ConfigureAction::setter_type) { // .. nothing yet .. } ConfigureAction::ConfigureAction (lay::PluginRoot *pr, const std::string &cname, const std::string &cvalue) : Action (), m_pr (pr), m_cname (cname), m_cvalue (cvalue), m_type (ConfigureAction::setter_type) { if (cvalue == "?") { m_type = boolean_type; set_checkable (true); } reg (); } ConfigureAction::ConfigureAction (lay::PluginRoot *pr, const std::string &title, const std::string &cname, const std::string &cvalue) : Action (title), m_pr (pr), m_cname (cname), m_cvalue (cvalue), m_type (ConfigureAction::setter_type) { if (cvalue == "?") { // A "?" notation indicates a boolean toogle entry m_type = boolean_type; set_checkable (true); } else if (! cvalue.empty () && cvalue[0] == '?') { // A "?value" notation indicates a choice m_type = choice_type; m_cvalue.erase (m_cvalue.begin (), m_cvalue.begin () + 1); set_checkable (true); } reg (); } ConfigureAction::~ConfigureAction () { unreg (); } void ConfigureAction::triggered () { if (m_type == boolean_type) { m_cvalue = tl::to_string (is_checked ()); } m_pr->config_set (m_cname, m_cvalue); m_pr->config_end (); } void ConfigureAction::reg () { if (lay::AbstractMenuProvider::instance ()) { lay::AbstractMenuProvider::instance ()->register_config_action (m_cname, this); } } void ConfigureAction::unreg () { if (lay::AbstractMenuProvider::instance ()) { lay::AbstractMenuProvider::instance ()->unregister_config_action (m_cname, this); } } void ConfigureAction::configure (const std::string &value) { if (m_type == boolean_type) { bool f = false; tl::from_string (value, f); set_checkable (true); set_checked (f); } else if (m_type == choice_type) { set_checkable (true); set_checked (m_cvalue == value); } } // --------------------------------------------------------------- // AbstractMenu implementation ActionHandle * AbstractMenu::create_action (const std::string &s) { tl_assert (lay::AbstractMenuProvider::instance () != 0); std::string title; std::string shortcut; std::string res; std::string tool_tip; parse_menu_title (s, title, shortcut, res, tool_tip); ActionHandle *ah = new ActionHandle (lay::AbstractMenuProvider::instance ()->menu_parent_widget ()); ah->ptr ()->setText (tl::to_qstring (title)); if (! tool_tip.empty ()) { ah->ptr ()->setToolTip (tl::to_qstring (tool_tip)); } if (! res.empty ()) { ah->ptr ()->setIcon (QIcon (tl::to_qstring (res))); } if (! shortcut.empty ()) { ah->ptr ()->setShortcut (QKeySequence (tl::to_qstring (shortcut))); } return ah; } AbstractMenu::AbstractMenu (AbstractMenuProvider *provider) : mp_provider (provider) { tl_assert (mp_provider != 0); } AbstractMenu::~AbstractMenu () { reset_menu_objects (m_root); } void AbstractMenu::init (const MenuLayoutEntry *layout) { m_root.set_has_submenu (); transfer (layout, m_root); } void AbstractMenu::reset_menu_objects (AbstractMenuItem &item) { for (std::list::iterator c = item.children.begin (); c != item.children.end (); ++c) { reset_menu_objects (*c); } item.set_menu (0); } QActionGroup * AbstractMenu::make_exclusive_group (const std::string &name) { std::map::const_iterator a = m_action_groups.find (name); if (a == m_action_groups.end ()) { QActionGroup *ag = new QActionGroup (this); ag->setExclusive (true); a = m_action_groups.insert (std::make_pair (name, ag)).first; } return a->second; } void AbstractMenu::build_detached (const std::string &name, QMenuBar *mbar) { AbstractMenuItem *item = find_item_exact ("@@" + name); tl_assert (item != 0); mbar->clear (); for (std::list::iterator c = item->children.begin (); c != item->children.end (); ++c) { if (c->has_submenu ()) { if (c->menu () == 0) { c->set_menu (mbar->addMenu (tl::to_qstring (c->action ().get_title ()))); c->set_action (Action (new ActionHandle (c->menu ()->menuAction (), false)), true); } else { mbar->addMenu (c->menu ()); } build (c->menu (), c->children); } else { mbar->addAction (c->action ().qaction ()); } } } void AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar) { m_helper_menu_items.clear (); mbar->clear (); tbar->clear (); for (std::list::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) { if (c->has_submenu ()) { if (c->name () == "@toolbar") { build (tbar, c->children); } else if (c->name ().find ("@@") == 0) { // nothing: let build_detached build the menu } else if (c->name ().find ("@") == 0) { if (c->menu () == 0) { QMenu *menu = new QMenu (tl::to_qstring (c->action ().get_title ()), mp_provider->menu_parent_widget ()); // HINT: it is necessary to add the menu action to a widget below the main window. // Otherwise, the keyboard shortcuts do not work for menu items inside such a // popup menu. It seems not to have a negative effect to add the menu to the // main widget. mp_provider->menu_parent_widget ()->addAction (menu->menuAction ()); c->set_menu (menu); c->set_action (Action (new ActionHandle (c->menu ()->menuAction (), false)), true); } // prepare a detached menu which can be used as context menues build (c->menu (), c->children); } else { if (c->menu () == 0) { c->set_menu (mbar->addMenu (tl::to_qstring (c->action ().get_title ()))); c->set_action (Action (new ActionHandle (c->menu ()->menuAction (), false)), true); } else { mbar->addMenu (c->menu ()); } build (c->menu (), c->children); } } else { mbar->addAction (c->action ().qaction ()); } } } void AbstractMenu::build (QMenu *m, std::list &items) { m->clear (); for (std::list::iterator c = items.begin (); c != items.end (); ++c) { if (c->has_submenu ()) { // HINT: the action acts as a container for the title. Unfortunately, we cannot create a // menu with a given action. The action is provided by addMenu instead. QMenu *menu = m->addMenu (tl::to_qstring (c->action ().get_title ())); c->set_action (Action (new ActionHandle (menu->menuAction (), false)), true); // HINT: build must be done before set_menu because set_menu might delete all child QAction's .. build (menu, c->children); c->set_menu (menu); } else { m->addAction (c->action ().qaction ()); } } } void AbstractMenu::build (QToolBar *t, std::list &items) { for (std::list::iterator c = items.begin (); c != items.end (); ++c) { if (! c->children.empty ()) { // To support tool buttons with menu we have to attach a helper menu // item to the QAction object. // TODO: this hurts if we use this QAction otherwise. In this case, this // QAction would get a menu too. However, hopefully this usage is constrained // to special toolbar buttons only. // In order to be able to manage the QMenu ourselves, we must not give it a parent. QMenu *menu = new QMenu (0); m_helper_menu_items.push_back (menu); // will be owned by the stable vector c->action ().qaction ()->setMenu (menu); t->addAction (c->action ().qaction ()); build (menu, c->children); } else { t->addAction (c->action ().qaction ()); } } } QMenu * AbstractMenu::detached_menu (const std::string &name) { AbstractMenuItem *item = find_item_exact ("@" + name); tl_assert (item != 0); return item->menu (); } QMenu * AbstractMenu::menu (const std::string &path) { AbstractMenuItem *item = find_item_exact (path); if (item) { return item->menu (); } else { return 0; } } bool AbstractMenu::is_valid (const std::string &path) const { const AbstractMenuItem *item = find_item_exact (path); return item != 0; } bool AbstractMenu::is_menu (const std::string &path) const { const AbstractMenuItem *item = find_item_exact (path); return item != 0 && item->has_submenu (); } bool AbstractMenu::is_separator (const std::string &path) const { const AbstractMenuItem *item = find_item_exact (path); return item != 0 && item->action ().is_separator (); } Action AbstractMenu::action (const std::string &path) const { const AbstractMenuItem *item = find_item_exact (path); if (! item) { throw tl::Exception (tl::to_string (QObject::tr ("Not a valid menu item path: ")) + path); } return item->action (); } std::vector AbstractMenu::items (const std::string &path) const { std::vector res; const AbstractMenuItem *item = find_item_exact (path); if (item) { res.reserve (item->children.size ()); for (std::list ::const_iterator c = item->children.begin (); c != item->children.end (); ++c) { res.push_back (c->name ()); } } return res; } void AbstractMenu::insert_item (const std::string &path, const std::string &name, const Action &action) { std::pair::iterator > ins = find_item (path); if (ins.first) { // insert the new item ins.first->children.insert (ins.second, AbstractMenuItem ()); --ins.second; ins.second->setup_item (ins.first->name (), name, action); // find an item with the same name and remove it for (std::list::iterator existing = ins.first->children.begin (); existing != ins.first->children.end (); ) { std::list::iterator existing_next = existing; ++existing_next; if (existing->name () == ins.second->name () && &*existing != &*ins.second) { ins.first->children.erase (existing); } existing = existing_next; } } emit changed (); } void AbstractMenu::insert_separator (const std::string &path, const std::string &name) { std::pair::iterator > ins = find_item (path); if (ins.first) { ins.first->children.insert (ins.second, AbstractMenuItem ()); --ins.second; Action action (new ActionHandle (mp_provider->menu_parent_widget ())); action.set_separator (true); ins.second->setup_item (ins.first->name (), name, action); } emit changed (); } void AbstractMenu::insert_menu (const std::string &path, const std::string &name, const Action &action) { std::pair::iterator > ins = find_item (path); if (ins.first) { ins.first->children.insert (ins.second, AbstractMenuItem ()); --ins.second; ins.second->setup_item (ins.first->name (), name, action); ins.second->set_has_submenu (); } emit changed (); } void AbstractMenu::insert_menu (const std::string &path, const std::string &name, const std::string &title) { insert_menu (path, name, Action (create_action (title))); } void AbstractMenu::delete_item (const std::string &path) { std::pair::iterator > ins = find_item (path); if (ins.first) { if (ins.second == ins.first->children.end ()) { throw tl::Exception (tl::to_string (QObject::tr ("delete_item cannot delete past-the-end item"))); } reset_menu_objects (*ins.second); ins.first->children.erase (ins.second); } emit changed (); } static void do_delete_items (std::list &list, const Action &action) { for (std::list::iterator l = list.begin (); l != list.end (); ) { std::list::iterator ll = l; ++ll; if (l->action () == action) { list.erase (l); } else { do_delete_items (l->children, action); } l = ll; } } void AbstractMenu::delete_items (const Action &action) { do_delete_items (m_root.children, action); emit changed (); } const AbstractMenuItem * AbstractMenu::find_item_exact (const std::string &path) const { // Hint: this const_cast saves us having to implement const and non-const versions .. return (const_cast (this))->find_item_exact (path); } AbstractMenuItem * AbstractMenu::find_item_exact (const std::string &path) { tl::Extractor extr (path.c_str ()); AbstractMenuItem *item = &m_root; while (! extr.at_end ()) { if (extr.test ("#")) { unsigned int n = 0; extr.try_read (n); std::list::iterator c = item->children.begin (); ++n; while (--n > 0 && c != item->children.end ()) { ++c; } if (n > 0) { return 0; } item = &*c; } else { std::string n; extr.read (n, "."); std::string name (item->name ()); if (! name.empty ()) { name += "."; } name += n; AbstractMenuItem *p = item; item = 0; for (std::list::iterator c = p->children.begin (); c != p->children.end () && item == 0; ++c) { if (c->name () == name) { item = &*c; } } if (! item) { return 0; } } extr.test ("."); } return item; } std::pair::iterator> AbstractMenu::find_item (const std::string &path) { tl::Extractor extr (path.c_str ()); AbstractMenuItem *parent = &m_root; std::list::iterator iter = m_root.children.end (); while (true) { if (extr.test ("#")) { unsigned int n = 0; extr.try_read (n); iter = parent->children.begin (); ++n; while (--n > 0 && iter != parent->children.end ()) { ++iter; } if (n > 0) { return std::make_pair ((AbstractMenuItem *) 0, m_root.children.end ()); } } else if (extr.test ("begin")) { return std::make_pair (parent, parent->children.begin ()); } else if (extr.test ("end")) { return std::make_pair (parent, parent->children.end ()); } else { std::string n; extr.read (n, ".+"); std::string name (parent->name ()); if (! name.empty ()) { name += "."; } name += n; bool after = extr.test ("+"); AbstractMenuItem *p = parent; parent = 0; for (std::list::iterator c = p->children.begin (); c != p->children.end (); ++c) { if (c->name () == name) { if (after) { ++c; if (c == p->children.end ()) { return std::make_pair (parent, parent->children.end ()); } } parent = p; iter = c; break; } } if (! parent) { return std::make_pair ((AbstractMenuItem *) 0, m_root.children.end ()); } } if (extr.at_end ()) { return std::make_pair (parent, iter); } extr.test ("."); parent = &*iter; } } void AbstractMenu::transfer (const MenuLayoutEntry *layout, AbstractMenuItem &item) { while (layout->name) { item.children.push_back (AbstractMenuItem ()); AbstractMenuItem &new_item = item.children.back (); lay::Action a; if (layout->slot) { // reuse any actions already registered for this slot a = mp_provider->action_for_slot (layout->slot); } else if (! layout->kv_pair.first.empty ()) { a = *mp_provider->create_config_action (layout->kv_pair.first, layout->kv_pair.second); } else { a = lay::Action (new ActionHandle (mp_provider->menu_parent_widget ())); } if (! layout->title.empty ()) { std::string title; std::string shortcut; std::string res; std::string tool_tip; parse_menu_title (layout->title, title, shortcut, res, tool_tip); a.set_separator (false); a.set_title (title); if (! shortcut.empty ()) { a.set_shortcut (QKeySequence (tl::to_qstring (shortcut))); } if (! tool_tip.empty ()) { a.set_tool_tip (tool_tip); } if (! res.empty ()) { a.set_icon (res); } } else { a.set_separator (true); } new_item.setup_item (item.name (), layout->name, a); if (layout->submenu) { new_item.set_has_submenu (); transfer (layout->submenu, item.children.back ()); } ++layout; } } std::vector AbstractMenu::group (const std::string &name) const { std::vector grp; collect_group (grp, name, m_root); return grp; } void AbstractMenu::collect_group (std::vector &grp, const std::string &name, const AbstractMenuItem &item) const { for (std::list::const_iterator c = item.children.begin (); c != item.children.end (); ++c) { if (c->groups ().find (name) != c->groups ().end ()) { grp.push_back (c->name ()); } collect_group (grp, name, *c); } } }