2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
KLayout Layout Viewer
|
2023-01-01 22:27:09 +01:00
|
|
|
Copyright (C) 2006-2023 Matthias Koefferlein
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
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"
|
2020-02-07 00:41:37 +01:00
|
|
|
#include "layDispatcher.h"
|
2017-02-12 13:21:08 +01:00
|
|
|
#include "layPlugin.h"
|
2022-06-11 00:41:06 +02:00
|
|
|
#include "layUtils.h"
|
2020-02-07 00:41:37 +01:00
|
|
|
#include "tlExceptions.h"
|
2017-02-12 13:21:08 +01:00
|
|
|
#include "tlAssert.h"
|
|
|
|
|
#include "gtf.h"
|
|
|
|
|
#include "gsi.h"
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
# include <QAction>
|
|
|
|
|
# include <QActionGroup>
|
|
|
|
|
# include <QMenu>
|
|
|
|
|
# include <QMenuBar>
|
|
|
|
|
# include <QShortcutEvent>
|
|
|
|
|
# include <QToolBar>
|
|
|
|
|
# include <QToolButton>
|
|
|
|
|
# include <QApplication>
|
|
|
|
|
# include <QMessageBox>
|
|
|
|
|
# include <QHBoxLayout>
|
|
|
|
|
# include <QFrame>
|
|
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
namespace lay
|
|
|
|
|
{
|
|
|
|
|
|
2018-01-02 22:16:54 +01:00
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
|
2023-07-30 14:46:58 +02:00
|
|
|
// On MacOS, the main menu bar and its decendent children
|
2021-06-03 07:05:32 +02:00
|
|
|
// can't be modified using "removeAction", followed by "addAction"
|
2018-01-02 22:16:54 +01:00
|
|
|
// to achieve a move operation.If we try to do so, segmentation faults happen
|
|
|
|
|
// in the timer event that presumably tries to merge the menu bar
|
|
|
|
|
// with the application menu.
|
|
|
|
|
// The fallback is to only allow add/delete, not move operations on the
|
|
|
|
|
// menu. In effect, the order of the menu items may not be the one desired
|
|
|
|
|
// if menus are dynamically created. However, this will only happen when
|
|
|
|
|
// new packages or macros are installed.
|
|
|
|
|
static const bool s_can_move_menu = false;
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
static const bool s_can_move_menu = true;
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-08-17 19:54:03 +02:00
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
// Serialization of key bindings and hidden menu state
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string> >
|
|
|
|
|
unpack_key_binding (const std::string &packed)
|
|
|
|
|
{
|
|
|
|
|
tl::Extractor ex (packed.c_str ());
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string> > key_bindings;
|
|
|
|
|
|
|
|
|
|
while (! ex.at_end ()) {
|
|
|
|
|
ex.test(";");
|
|
|
|
|
key_bindings.push_back (std::make_pair (std::string (), std::string ()));
|
|
|
|
|
ex.read_word_or_quoted (key_bindings.back ().first);
|
|
|
|
|
ex.test(":");
|
|
|
|
|
ex.read_word_or_quoted (key_bindings.back ().second);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return key_bindings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string
|
|
|
|
|
pack_key_binding (const std::vector<std::pair<std::string, std::string> > &unpacked)
|
|
|
|
|
{
|
|
|
|
|
std::string packed;
|
|
|
|
|
|
|
|
|
|
for (std::vector<std::pair<std::string, std::string> >::const_iterator p = unpacked.begin (); p != unpacked.end (); ++p) {
|
|
|
|
|
if (! packed.empty ()) {
|
|
|
|
|
packed += ";";
|
|
|
|
|
}
|
|
|
|
|
packed += tl::to_word_or_quoted_string (p->first);
|
|
|
|
|
packed += ":";
|
|
|
|
|
packed += tl::to_word_or_quoted_string (p->second);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return packed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, bool> >
|
|
|
|
|
unpack_menu_items_hidden (const std::string &packed)
|
|
|
|
|
{
|
|
|
|
|
tl::Extractor ex (packed.c_str ());
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, bool> > hidden;
|
|
|
|
|
|
|
|
|
|
while (! ex.at_end ()) {
|
|
|
|
|
ex.test(";");
|
|
|
|
|
hidden.push_back (std::make_pair (std::string (), false));
|
|
|
|
|
ex.read_word_or_quoted (hidden.back ().first);
|
|
|
|
|
ex.test(":");
|
|
|
|
|
ex.read (hidden.back ().second);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string
|
|
|
|
|
pack_menu_items_hidden (const std::vector<std::pair<std::string, bool> > &unpacked)
|
|
|
|
|
{
|
|
|
|
|
std::string packed;
|
|
|
|
|
|
|
|
|
|
for (std::vector<std::pair<std::string, bool> >::const_iterator p = unpacked.begin (); p != unpacked.end (); ++p) {
|
|
|
|
|
if (! packed.empty ()) {
|
|
|
|
|
packed += ";";
|
|
|
|
|
}
|
|
|
|
|
packed += tl::to_word_or_quoted_string (p->first);
|
|
|
|
|
packed += ":";
|
|
|
|
|
packed += tl::to_string (p->second);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return packed;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
// ---------------------------------------------------------------
|
2018-02-09 01:23:59 +01:00
|
|
|
// Helper function to parse a title with potential shortcut and
|
2017-02-12 13:21:08 +01:00
|
|
|
// 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
|
|
|
|
|
|
2020-02-10 00:07:41 +01:00
|
|
|
AbstractMenuItem::AbstractMenuItem (Dispatcher *dispatcher)
|
2020-02-13 00:16:37 +01:00
|
|
|
: mp_action (new Action ()), mp_dispatcher (dispatcher), m_has_submenu (false), m_remove_on_empty (false)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
// ... nothing yet ..
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-10 00:07:41 +01:00
|
|
|
AbstractMenuItem::AbstractMenuItem (const AbstractMenuItem &item)
|
2020-02-13 00:16:37 +01:00
|
|
|
: mp_action (new Action ()), mp_dispatcher (item.dispatcher ()), m_has_submenu (false), m_remove_on_empty (false)
|
2018-02-09 01:23:59 +01:00
|
|
|
{
|
2017-02-12 13:21:08 +01:00
|
|
|
// ... nothing yet ..
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
AbstractMenuItem::setup_item (const std::string &pn, const std::string &s, Action *a)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
AbstractMenuItem::set_action (Action *a, bool copy_properties)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
tl_assert (a != 0);
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-19 01:06:39 +01:00
|
|
|
a->keep ();
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
if (copy_properties && mp_action->qaction () && a->qaction ()) {
|
|
|
|
|
a->qaction ()->setIcon (mp_action->qaction ()->icon ());
|
|
|
|
|
a->qaction ()->setToolTip (mp_action->qaction ()->toolTip ());
|
|
|
|
|
a->qaction ()->setShortcut (mp_action->qaction ()->shortcut ());
|
|
|
|
|
a->qaction ()->setIconText (mp_action->qaction ()->iconText ());
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
bool enabled = mp_action.get () ? mp_action->is_enabled () : true;
|
|
|
|
|
bool visible = mp_action.get () ? mp_action->is_visible () : true;
|
|
|
|
|
mp_action->set_dispatcher (0);
|
|
|
|
|
mp_action.reset (a);
|
|
|
|
|
mp_action->set_enabled (enabled);
|
|
|
|
|
mp_action->set_visible (visible);
|
|
|
|
|
mp_action->set_dispatcher (mp_dispatcher);
|
|
|
|
|
mp_action->set_object_name (m_basename);
|
2018-01-02 22:16:54 +01:00
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
if (mp_action->menu ()) {
|
|
|
|
|
mp_action->menu ()->setObjectName (tl::to_qstring (m_basename));
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenuItem::set_action_title (const std::string &s)
|
|
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
mp_action->set_title (s);
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenuItem::set_has_submenu ()
|
|
|
|
|
{
|
|
|
|
|
m_has_submenu = true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
void
|
|
|
|
|
AbstractMenuItem::set_remove_on_empty ()
|
|
|
|
|
{
|
|
|
|
|
m_remove_on_empty = true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
// Action implementation
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
static std::set<Action *> *sp_actionHandles = 0;
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
namespace {
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief A specialization that provides a way to catch ambiguous key shortcuts
|
|
|
|
|
*/
|
|
|
|
|
class ActionObject
|
2018-02-09 01:23:59 +01:00
|
|
|
: public QAction
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2018-02-09 01:23:59 +01:00
|
|
|
ActionObject (QObject *parent)
|
|
|
|
|
: QAction (parent)
|
|
|
|
|
{
|
|
|
|
|
static size_t s_id = 0;
|
|
|
|
|
m_id = ++s_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t id () const
|
|
|
|
|
{
|
|
|
|
|
return m_id;
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
bool event(QEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->type() == QEvent::Shortcut) {
|
|
|
|
|
|
|
|
|
|
QShortcutEvent *se = static_cast<QShortcutEvent *>(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");
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
for (std::set<Action *>::const_iterator a = sp_actionHandles->begin (); a != sp_actionHandles->end (); ++a) {
|
|
|
|
|
if (! (*a)->qaction ()->shortcut ().isEmpty () && (*a)->qaction ()->shortcut ().matches (se->key ()) != QKeySequence::NoMatch) {
|
2017-02-12 13:21:08 +01:00
|
|
|
msg += QChar (0x2022) /*bullet*/;
|
|
|
|
|
msg += tl::to_qstring (" ");
|
2020-02-13 00:16:37 +01:00
|
|
|
msg += (*a)->qaction ()->text ();
|
2017-02-12 13:21:08 +01:00
|
|
|
msg += tl::to_qstring ("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMessageBox::warning (0, QObject::tr ("Warning"), msg);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QAction::event(e);
|
|
|
|
|
}
|
2018-02-09 01:23:59 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
size_t m_id;
|
2017-02-12 13:21:08 +01:00
|
|
|
};
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
static size_t
|
|
|
|
|
id_from_action (QAction *action)
|
|
|
|
|
{
|
|
|
|
|
ActionObject *ao = dynamic_cast<ActionObject *> (action);
|
|
|
|
|
return ao ? ao->id () : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
Action::Action () :
|
|
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
mp_menu (0),
|
2022-06-11 00:41:06 +02:00
|
|
|
mp_action (lay::has_gui () ? new ActionObject (0) : 0),
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_checked (false),
|
|
|
|
|
m_checkable (false),
|
|
|
|
|
m_enabled (true),
|
|
|
|
|
m_separator (false),
|
2020-02-10 00:07:41 +01:00
|
|
|
mp_dispatcher (0),
|
2017-10-29 13:34:57 +01:00
|
|
|
m_owned (true),
|
|
|
|
|
m_visible (true),
|
2019-08-17 19:17:40 +02:00
|
|
|
m_hidden (false),
|
|
|
|
|
m_no_key_sequence (false)
|
2018-02-09 01:23:59 +01:00
|
|
|
{
|
2017-02-12 13:21:08 +01:00
|
|
|
if (! sp_actionHandles) {
|
2020-02-13 00:16:37 +01:00
|
|
|
sp_actionHandles = new std::set<Action *> ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
sp_actionHandles->insert (this);
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
// catch the destroyed signal to tell if the QAction object is deleted.
|
2022-06-11 00:41:06 +02:00
|
|
|
if (mp_action) {
|
2022-08-01 18:49:42 +02:00
|
|
|
connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *)));
|
2022-06-11 00:41:06 +02:00
|
|
|
connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ()));
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::Action (QAction *action, bool owned)
|
2018-01-02 22:16:54 +01:00
|
|
|
: mp_menu (0),
|
|
|
|
|
mp_action (action),
|
2022-06-11 00:41:06 +02:00
|
|
|
m_checked (false),
|
|
|
|
|
m_checkable (false),
|
|
|
|
|
m_enabled (true),
|
|
|
|
|
m_separator (false),
|
2020-02-10 00:07:41 +01:00
|
|
|
mp_dispatcher (0),
|
2017-10-29 13:34:57 +01:00
|
|
|
m_owned (owned),
|
|
|
|
|
m_visible (true),
|
2019-08-17 19:17:40 +02:00
|
|
|
m_hidden (false),
|
|
|
|
|
m_no_key_sequence (false)
|
2018-02-09 01:23:59 +01:00
|
|
|
{
|
2017-02-12 13:21:08 +01:00
|
|
|
if (! sp_actionHandles) {
|
2020-02-13 00:16:37 +01:00
|
|
|
sp_actionHandles = new std::set<Action *> ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
sp_actionHandles->insert (this);
|
|
|
|
|
|
|
|
|
|
// catch the destroyed signal to tell if the QAction object is deleted.
|
2022-08-01 18:49:42 +02:00
|
|
|
connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *)));
|
2020-02-10 00:07:41 +01:00
|
|
|
connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ()));
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::Action (QMenu *menu, bool owned)
|
2018-01-02 22:16:54 +01:00
|
|
|
: mp_menu (menu),
|
|
|
|
|
mp_action (menu->menuAction ()),
|
2022-06-11 00:41:06 +02:00
|
|
|
m_checked (false),
|
|
|
|
|
m_checkable (false),
|
|
|
|
|
m_enabled (true),
|
|
|
|
|
m_separator (false),
|
2020-02-10 00:07:41 +01:00
|
|
|
mp_dispatcher (0),
|
2018-01-02 22:16:54 +01:00
|
|
|
m_owned (owned),
|
|
|
|
|
m_visible (true),
|
2019-08-17 19:17:40 +02:00
|
|
|
m_hidden (false),
|
|
|
|
|
m_no_key_sequence (false)
|
2018-01-02 22:16:54 +01:00
|
|
|
{
|
|
|
|
|
if (! sp_actionHandles) {
|
2020-02-13 00:16:37 +01:00
|
|
|
sp_actionHandles = new std::set<Action *> ();
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
|
|
|
|
sp_actionHandles->insert (this);
|
|
|
|
|
|
|
|
|
|
// catch the destroyed signal to tell if the QAction object is deleted.
|
2022-08-01 18:49:42 +02:00
|
|
|
connect (mp_menu, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *)));
|
|
|
|
|
connect (mp_menu, SIGNAL (aboutToShow ()), this, SLOT (menu_about_to_show ()));
|
2020-02-10 00:07:41 +01:00
|
|
|
connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ()));
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2018-01-02 22:16:54 +01:00
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
Action::Action (const std::string &title) :
|
|
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
mp_menu (0),
|
2022-06-11 00:41:06 +02:00
|
|
|
mp_action (lay::has_gui () ? new ActionObject (0) : 0),
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_checked (false),
|
|
|
|
|
m_checkable (false),
|
|
|
|
|
m_enabled (true),
|
|
|
|
|
m_separator (false),
|
2020-02-13 00:16:37 +01:00
|
|
|
mp_dispatcher (0),
|
|
|
|
|
m_owned (true),
|
|
|
|
|
m_visible (true),
|
|
|
|
|
m_hidden (false),
|
|
|
|
|
m_no_key_sequence (false)
|
|
|
|
|
{
|
|
|
|
|
if (! sp_actionHandles) {
|
|
|
|
|
sp_actionHandles = new std::set<Action *> ();
|
|
|
|
|
}
|
|
|
|
|
sp_actionHandles->insert (this);
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
configure_from_title (title);
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
// catch the destroyed signal to tell if the QAction object is deleted.
|
2022-06-11 00:41:06 +02:00
|
|
|
if (mp_action) {
|
2022-08-01 18:49:42 +02:00
|
|
|
connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *)));
|
2022-06-11 00:41:06 +02:00
|
|
|
connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ()));
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2020-02-13 00:16:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Action::~Action ()
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
if (sp_actionHandles) {
|
|
|
|
|
sp_actionHandles->erase (this);
|
|
|
|
|
if (sp_actionHandles->empty ()) {
|
|
|
|
|
delete sp_actionHandles;
|
|
|
|
|
sp_actionHandles = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2018-01-02 22:16:54 +01:00
|
|
|
if (mp_menu) {
|
|
|
|
|
if (m_owned) {
|
|
|
|
|
delete mp_menu;
|
|
|
|
|
m_owned = false;
|
|
|
|
|
}
|
|
|
|
|
mp_menu = 0;
|
|
|
|
|
mp_action = 0;
|
|
|
|
|
} else if (mp_action) {
|
2017-02-12 13:21:08 +01:00
|
|
|
if (m_owned) {
|
|
|
|
|
delete mp_action;
|
|
|
|
|
m_owned = false;
|
|
|
|
|
}
|
|
|
|
|
mp_action = 0;
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-10 00:07:41 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::set_dispatcher (Dispatcher *dispatcher)
|
2020-02-10 00:07:41 +01:00
|
|
|
{
|
|
|
|
|
if (mp_dispatcher != dispatcher) {
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-10 00:07:41 +01:00
|
|
|
if (mp_action && m_owned) {
|
|
|
|
|
mp_action->setParent (dispatcher ? dispatcher->menu_parent_widget () : 0);
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2020-02-10 00:07:41 +01:00
|
|
|
mp_dispatcher = dispatcher;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
void
|
|
|
|
|
Action::configure_from_title (const std::string &s)
|
|
|
|
|
{
|
|
|
|
|
std::string title;
|
|
|
|
|
std::string shortcut;
|
|
|
|
|
std::string res;
|
|
|
|
|
std::string tool_tip;
|
|
|
|
|
|
|
|
|
|
parse_menu_title (s, title, shortcut, res, tool_tip);
|
|
|
|
|
|
2022-06-11 00:41:06 +02:00
|
|
|
set_title (title);
|
2022-06-07 23:27:34 +02:00
|
|
|
|
2022-06-11 00:41:06 +02:00
|
|
|
if (! shortcut.empty ()) {
|
|
|
|
|
set_default_shortcut (shortcut);
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
if (! tool_tip.empty ()) {
|
2022-06-11 00:41:06 +02:00
|
|
|
set_tool_tip (tool_tip);
|
2022-06-07 23:27:34 +02:00
|
|
|
}
|
|
|
|
|
if (! res.empty ()) {
|
2022-06-11 00:41:06 +02:00
|
|
|
set_icon (res);
|
2022-06-07 23:27:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-01 18:49:42 +02:00
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
void
|
|
|
|
|
Action::menu_about_to_show ()
|
|
|
|
|
{
|
2022-08-15 23:43:45 +02:00
|
|
|
// keeps a reference to self in case the action handler code removes actions
|
|
|
|
|
tl::shared_ptr<Action> self_holder (this);
|
|
|
|
|
|
2022-08-01 18:49:42 +02:00
|
|
|
BEGIN_PROTECTED
|
|
|
|
|
|
2022-08-15 23:43:45 +02:00
|
|
|
on_menu_opening_event ();
|
|
|
|
|
menu_opening ();
|
|
|
|
|
|
2022-08-01 18:49:42 +02:00
|
|
|
if (! mp_dispatcher || ! mp_dispatcher->menu ()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AbstractMenuItem *item = mp_dispatcher->menu ()->find_item_for_action (this);
|
|
|
|
|
if (! item ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto i = item->children.begin (); i != item->children.end (); ++i) {
|
|
|
|
|
if (i->action ()) {
|
|
|
|
|
i->action ()->sync_qaction ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
END_PROTECTED
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-10 00:07:41 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::qaction_triggered ()
|
2020-02-10 00:07:41 +01:00
|
|
|
{
|
2022-08-15 23:43:45 +02:00
|
|
|
// keeps a reference to self in case the action handler code removes actions
|
|
|
|
|
tl::shared_ptr<Action> self_holder (this);
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
BEGIN_PROTECTED
|
2022-08-15 23:43:45 +02:00
|
|
|
on_triggered_event ();
|
2020-02-13 00:16:37 +01:00
|
|
|
triggered ();
|
|
|
|
|
END_PROTECTED
|
2020-02-10 00:07:41 +01:00
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2020-02-10 00:07:41 +01:00
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::trigger ()
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2022-08-15 23:43:45 +02:00
|
|
|
// keeps a reference to self in case the action handler code removes actions
|
|
|
|
|
tl::shared_ptr<Action> self_holder (this);
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->trigger ();
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#else
|
2022-08-15 23:43:45 +02:00
|
|
|
on_triggered_event ();
|
2022-06-07 23:27:34 +02:00
|
|
|
triggered ();
|
|
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::triggered ()
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
// .. no action yet, the reimplementation must provide some ..
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 23:43:45 +02:00
|
|
|
void
|
|
|
|
|
Action::menu_opening ()
|
|
|
|
|
{
|
|
|
|
|
// .. no action yet, the reimplementation must provide some ..
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
QAction *
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::qaction () const
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
return mp_action;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-02 22:16:54 +01:00
|
|
|
QMenu *
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::menu () const
|
2018-01-02 22:16:54 +01:00
|
|
|
{
|
|
|
|
|
return mp_menu;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-15 23:43:45 +02:00
|
|
|
void
|
|
|
|
|
Action::set_menu (QMenu *menu, bool owned)
|
|
|
|
|
{
|
|
|
|
|
if (mp_menu == menu || ! lay::has_gui () || ! mp_action) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mp_menu && ! menu) {
|
|
|
|
|
|
|
|
|
|
QAction *new_action = new ActionObject (0);
|
2022-08-16 23:00:31 +02:00
|
|
|
configure_action (new_action);
|
2022-08-15 23:43:45 +02:00
|
|
|
|
|
|
|
|
if (m_owned) {
|
|
|
|
|
delete mp_menu;
|
|
|
|
|
}
|
|
|
|
|
mp_menu = 0;
|
|
|
|
|
|
|
|
|
|
mp_action = new_action;
|
|
|
|
|
m_owned = true;
|
|
|
|
|
|
|
|
|
|
} else if (mp_menu && menu) {
|
|
|
|
|
|
2022-08-16 23:00:31 +02:00
|
|
|
configure_action (menu->menuAction ());
|
2022-08-15 23:43:45 +02:00
|
|
|
|
|
|
|
|
if (m_owned) {
|
|
|
|
|
delete mp_menu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mp_menu = menu;
|
|
|
|
|
m_owned = owned;
|
|
|
|
|
mp_action = menu->menuAction ();
|
|
|
|
|
|
|
|
|
|
} else if (! mp_menu && menu) {
|
|
|
|
|
|
2022-08-16 23:00:31 +02:00
|
|
|
configure_action (menu->menuAction ());
|
2022-08-15 23:43:45 +02:00
|
|
|
|
|
|
|
|
if (m_owned) {
|
|
|
|
|
delete mp_action;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mp_menu = menu;
|
|
|
|
|
m_owned = owned;
|
|
|
|
|
mp_action = menu->menuAction ();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mp_menu) {
|
|
|
|
|
connect (mp_menu, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *)));
|
|
|
|
|
connect (mp_menu, SIGNAL (aboutToShow ()), this, SLOT (menu_about_to_show ()));
|
|
|
|
|
} else {
|
|
|
|
|
connect (mp_action, SIGNAL (destroyed (QObject *)), this, SLOT (was_destroyed (QObject *)));
|
|
|
|
|
}
|
|
|
|
|
connect (mp_action, SIGNAL (triggered ()), this, SLOT (qaction_triggered ()));
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2022-08-01 18:49:42 +02:00
|
|
|
Action::was_destroyed (QObject *obj)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2020-02-08 22:15:09 +01:00
|
|
|
if (obj == mp_action) {
|
|
|
|
|
mp_action = 0;
|
|
|
|
|
}
|
|
|
|
|
if (obj == mp_menu) {
|
|
|
|
|
mp_menu = 0;
|
|
|
|
|
mp_action = 0;
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
m_owned = false;
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2022-08-01 18:49:42 +02:00
|
|
|
void
|
|
|
|
|
Action::sync_qaction ()
|
|
|
|
|
{
|
|
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
if (mp_action) {
|
|
|
|
|
mp_action->setVisible (is_effective_visible ());
|
|
|
|
|
mp_action->setShortcut (get_key_sequence ());
|
|
|
|
|
mp_action->setEnabled (is_effective_enabled ());
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 23:00:31 +02:00
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
void
|
|
|
|
|
Action::configure_action (QAction *target) const
|
|
|
|
|
{
|
|
|
|
|
target->setVisible (is_effective_visible ());
|
|
|
|
|
target->setShortcut (get_key_sequence ());
|
|
|
|
|
target->setEnabled (is_effective_enabled ());
|
|
|
|
|
target->setToolTip (tl::to_qstring (get_tool_tip ()));
|
|
|
|
|
target->setCheckable (is_checkable ());
|
|
|
|
|
target->setChecked (is_checked ());
|
|
|
|
|
target->setIconText (tl::to_qstring (get_icon_text ()));
|
|
|
|
|
target->setSeparator (is_separator ());
|
|
|
|
|
target->setText (tl::to_qstring (get_title ()));
|
|
|
|
|
|
|
|
|
|
if (qaction ()) {
|
|
|
|
|
target->setIcon (qaction ()->icon ());
|
|
|
|
|
target->setObjectName (qaction ()->objectName ());
|
|
|
|
|
} else if (m_icon.empty ()) {
|
|
|
|
|
target->setIcon (QIcon ());
|
|
|
|
|
} else {
|
|
|
|
|
target->setIcon (QIcon (tl::to_qstring (m_icon)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-10-29 13:34:57 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::set_visible (bool v)
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
|
|
|
|
if (m_visible != v) {
|
|
|
|
|
m_visible = v;
|
2022-08-01 18:49:42 +02:00
|
|
|
sync_qaction ();
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::set_hidden (bool h)
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
|
|
|
|
if (m_hidden != h) {
|
|
|
|
|
m_hidden = h;
|
2022-08-01 18:49:42 +02:00
|
|
|
sync_qaction ();
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::is_visible () const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
|
|
|
|
return m_visible;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::is_hidden () const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
|
|
|
|
return m_hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::is_effective_visible () const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
2022-08-01 18:49:42 +02:00
|
|
|
return m_visible && !m_hidden && wants_visible ();
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::set_default_shortcut (const std::string &sc)
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
|
|
|
|
if (m_default_shortcut != sc) {
|
|
|
|
|
m_default_shortcut = sc;
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2019-08-17 19:17:40 +02:00
|
|
|
m_default_key_sequence = QKeySequence (tl::to_qstring (sc));
|
2017-10-29 13:34:57 +01:00
|
|
|
if (mp_action) {
|
2019-08-17 19:17:40 +02:00
|
|
|
mp_action->setShortcut (get_key_sequence ());
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::set_shortcut (const std::string &sc)
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
|
|
|
|
if (m_shortcut != sc) {
|
|
|
|
|
m_shortcut = sc;
|
2019-08-17 19:17:40 +02:00
|
|
|
m_no_key_sequence = (sc == Action::no_shortcut ());
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2019-08-17 19:17:40 +02:00
|
|
|
m_key_sequence = m_no_key_sequence ? QKeySequence () : QKeySequence (tl::to_qstring (m_shortcut));
|
2017-10-29 13:34:57 +01:00
|
|
|
if (mp_action) {
|
2019-08-17 19:17:40 +02:00
|
|
|
mp_action->setShortcut (get_key_sequence ());
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 19:17:40 +02:00
|
|
|
std::string
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::get_default_shortcut () const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_default_shortcut;
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-17 19:17:40 +02:00
|
|
|
std::string
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::get_shortcut () const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_no_key_sequence ? Action::no_shortcut () : m_shortcut;
|
2019-08-17 19:17:40 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2019-08-17 19:17:40 +02:00
|
|
|
QKeySequence
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::get_key_sequence () const
|
2019-08-17 19:17:40 +02:00
|
|
|
{
|
|
|
|
|
if (m_hidden) {
|
|
|
|
|
// A hidden menu item does not have a key sequence too.
|
|
|
|
|
return QKeySequence ();
|
|
|
|
|
} else if (m_no_key_sequence) {
|
|
|
|
|
return QKeySequence ();
|
|
|
|
|
} else if (m_key_sequence.isEmpty ()) {
|
|
|
|
|
return m_default_key_sequence;
|
|
|
|
|
} else {
|
|
|
|
|
return m_key_sequence;
|
|
|
|
|
}
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QKeySequence
|
2020-02-13 00:16:37 +01:00
|
|
|
Action::get_key_sequence_for (const std::string &sc) const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
2017-10-29 17:39:49 +01:00
|
|
|
if (m_hidden) {
|
|
|
|
|
// A hidden menu item does not have a key sequence too.
|
|
|
|
|
return QKeySequence ();
|
2019-08-17 19:17:40 +02:00
|
|
|
} else if (sc.empty ()) {
|
|
|
|
|
return m_default_key_sequence;
|
|
|
|
|
} else if (sc == Action::no_shortcut ()) {
|
|
|
|
|
return QKeySequence ();
|
2017-10-29 13:34:57 +01:00
|
|
|
} else {
|
2019-08-17 19:17:40 +02:00
|
|
|
return QKeySequence::fromString (tl::to_qstring (sc));
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2019-08-17 19:17:40 +02:00
|
|
|
const std::string &
|
|
|
|
|
Action::no_shortcut ()
|
|
|
|
|
{
|
|
|
|
|
static const std::string no_shortcut ("none");
|
|
|
|
|
return no_shortcut;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::set_title (const std::string &t)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->setText (tl::to_qstring (t));
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_title = t;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
std::string
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::get_title () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_title;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-17 19:17:40 +02:00
|
|
|
std::string
|
|
|
|
|
Action::get_effective_shortcut () const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
if (m_hidden || m_no_key_sequence) {
|
|
|
|
|
// A hidden menu item does not have a key sequence too.
|
|
|
|
|
return std::string ();
|
|
|
|
|
} else if (m_shortcut.empty ()) {
|
|
|
|
|
return m_default_shortcut;
|
|
|
|
|
} else {
|
|
|
|
|
return m_shortcut;
|
|
|
|
|
}
|
2017-10-29 13:34:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string
|
2019-08-17 19:17:40 +02:00
|
|
|
Action::get_effective_shortcut_for (const std::string &sc) const
|
2017-10-29 13:34:57 +01:00
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
if (m_hidden) {
|
|
|
|
|
// A hidden menu item does not have a key sequence too.
|
|
|
|
|
return std::string ();
|
|
|
|
|
} else if (sc.empty ()) {
|
|
|
|
|
return m_default_shortcut;
|
|
|
|
|
} else if (sc == Action::no_shortcut ()) {
|
|
|
|
|
return std::string ();
|
|
|
|
|
} else {
|
|
|
|
|
return sc;
|
|
|
|
|
}
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
void
|
|
|
|
|
Action::add_to_exclusive_group (lay::AbstractMenu *menu, const std::string &group_name)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
// NOTE: this feature does not work without Qt
|
|
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
menu->make_exclusive_group (group_name)->addAction (qaction ());
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Action::is_checkable () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_checkable;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Action::is_checked () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2022-06-12 14:00:06 +02:00
|
|
|
return qaction () ? qaction ()->isChecked () : m_checked;
|
2022-06-07 23:27:34 +02:00
|
|
|
#else
|
|
|
|
|
return m_checked;
|
|
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Action::is_enabled () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_enabled;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Action::is_separator () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_separator;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Action::set_enabled (bool b)
|
|
|
|
|
{
|
2022-08-01 18:49:42 +02:00
|
|
|
if (m_enabled != b) {
|
|
|
|
|
m_enabled = b;
|
|
|
|
|
sync_qaction ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2022-08-01 18:49:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Action::is_effective_enabled () const
|
|
|
|
|
{
|
|
|
|
|
return m_enabled && wants_enabled ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Action::set_checked (bool c)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->setChecked (c);
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_checked = c;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Action::set_checkable (bool c)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->setCheckable (c);
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_checkable = c;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Action::set_separator (bool s)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->setSeparator (s);
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_separator = s;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::set_icon (const std::string &filename)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
if (filename.empty ()) {
|
|
|
|
|
qaction ()->setIcon (QIcon ());
|
|
|
|
|
} else {
|
|
|
|
|
qaction ()->setIcon (QIcon (tl::to_qstring (filename)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_icon = filename;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 23:43:45 +02:00
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
void
|
|
|
|
|
Action::set_qicon (const QIcon &icon)
|
|
|
|
|
{
|
|
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->setIcon (icon);
|
|
|
|
|
}
|
|
|
|
|
m_icon.clear ();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
std::string
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::get_tool_tip () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_tooltip;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::set_tool_tip (const std::string &text)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
if (text.empty ()) {
|
|
|
|
|
qaction ()->setToolTip (QString ());
|
|
|
|
|
} else {
|
|
|
|
|
qaction ()->setToolTip (tl::to_qstring (text));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_tooltip = text;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
std::string
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::get_icon_text () const
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
return m_icontext;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::set_icon_text (const std::string &icon_text)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
if (icon_text.empty ()) {
|
|
|
|
|
qaction ()->setIconText (QString ());
|
|
|
|
|
} else {
|
|
|
|
|
qaction ()->setIconText (tl::to_qstring (icon_text));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2022-06-11 00:41:06 +02:00
|
|
|
m_icontext = icon_text;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
Action::set_object_name (const std::string &name)
|
|
|
|
|
{
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
if (qaction ()) {
|
|
|
|
|
qaction ()->setObjectName (tl::to_qstring (name));
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
// ConfigureAction implementation
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
ConfigureAction::ConfigureAction ()
|
|
|
|
|
: Action (), m_type (ConfigureAction::setter_type)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
// .. nothing yet ..
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
ConfigureAction::ConfigureAction (const std::string &cname, const std::string &cvalue)
|
|
|
|
|
: Action (), m_cname (cname), m_cvalue (cvalue), m_type (ConfigureAction::setter_type)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
if (cvalue == "?") {
|
|
|
|
|
m_type = boolean_type;
|
|
|
|
|
set_checkable (true);
|
|
|
|
|
}
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
ConfigureAction::ConfigureAction (const std::string &title, const std::string &cname, const std::string &cvalue)
|
|
|
|
|
: Action (title), m_cname (cname), m_cvalue (cvalue), m_type (ConfigureAction::setter_type)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
if (cvalue == "?") {
|
2021-06-03 07:05:32 +02:00
|
|
|
// A "?" notation indicates a boolean toggle entry
|
2017-02-12 13:21:08 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
ConfigureAction::~ConfigureAction ()
|
|
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
// .. nothing yet ..
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
ConfigureAction::triggered ()
|
|
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
if (dispatcher ()) {
|
|
|
|
|
if (m_type == boolean_type) {
|
|
|
|
|
m_cvalue = tl::to_string (is_checked ());
|
|
|
|
|
}
|
|
|
|
|
dispatcher ()->config_set (m_cname, m_cvalue);
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
// AbstractMenu implementation
|
|
|
|
|
|
2020-02-07 00:41:37 +01:00
|
|
|
AbstractMenu::AbstractMenu (Dispatcher *dispatcher)
|
2020-02-13 00:16:37 +01:00
|
|
|
: mp_dispatcher (dispatcher), m_root (dispatcher), m_config_actions_valid (false)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2017-10-22 23:06:42 +02:00
|
|
|
// .. nothing yet ..
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AbstractMenu::~AbstractMenu ()
|
|
|
|
|
{
|
2018-01-02 22:16:54 +01:00
|
|
|
// .. nothing yet ..
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2017-02-12 13:21:08 +01:00
|
|
|
QActionGroup *
|
|
|
|
|
AbstractMenu::make_exclusive_group (const std::string &name)
|
|
|
|
|
{
|
|
|
|
|
std::map<std::string, QActionGroup *>::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;
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
return a->second;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-12-30 23:28:09 +01:00
|
|
|
AbstractMenu::build_detached (const std::string &name, QFrame *mbar)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2017-12-30 23:28:09 +01:00
|
|
|
// Clean up the menu bar before rebuilding
|
|
|
|
|
if (mbar->layout ()) {
|
|
|
|
|
delete mbar->layout ();
|
|
|
|
|
}
|
|
|
|
|
QObjectList children = mbar->children ();
|
|
|
|
|
for (QObjectList::const_iterator c = children.begin (); c != children.end (); ++c) {
|
|
|
|
|
if (dynamic_cast<QToolButton *> (*c)) {
|
|
|
|
|
delete *c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *menu_layout = new QHBoxLayout (mbar);
|
2021-11-25 00:57:11 +01:00
|
|
|
menu_layout->setContentsMargins (0, 0, 0, 0);
|
2017-12-30 23:28:09 +01:00
|
|
|
mbar->setLayout (menu_layout);
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenuItem *item = find_item_exact ("@@" + name);
|
|
|
|
|
tl_assert (item != 0);
|
|
|
|
|
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator c = item->children.begin (); c != item->children.end (); ++c) {
|
|
|
|
|
|
|
|
|
|
if (c->has_submenu ()) {
|
|
|
|
|
|
2017-12-30 23:28:09 +01:00
|
|
|
QToolButton *menu_button = new QToolButton (mbar);
|
|
|
|
|
menu_layout->addWidget (menu_button);
|
|
|
|
|
menu_button->setAutoRaise (true);
|
|
|
|
|
menu_button->setPopupMode (QToolButton::MenuButtonPopup);
|
2020-02-13 00:16:37 +01:00
|
|
|
menu_button->setText (tl::to_qstring (c->action ()->get_title ()));
|
2017-12-30 23:28:09 +01:00
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
if (c->menu () == 0) {
|
2022-08-15 23:43:45 +02:00
|
|
|
c->set_menu (new QMenu (mp_dispatcher->menu_parent_widget ()), true);
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 23:43:45 +02:00
|
|
|
menu_button->setMenu (c->menu ());
|
2017-02-12 13:21:08 +01:00
|
|
|
build (c->menu (), c->children);
|
|
|
|
|
|
|
|
|
|
} else {
|
2017-12-30 23:28:09 +01:00
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
QAction *action = c->action ()->qaction ();
|
2017-12-30 23:28:09 +01:00
|
|
|
|
|
|
|
|
QToolButton *menu_button = new QToolButton (mbar);
|
|
|
|
|
menu_layout->addWidget (menu_button);
|
|
|
|
|
menu_button->setAutoRaise (true);
|
|
|
|
|
menu_button->setDefaultAction (action);
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2017-12-30 23:28:09 +01:00
|
|
|
|
|
|
|
|
menu_layout->addStretch (1);
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-13 02:04:53 +01:00
|
|
|
static QAction *insert_action_after (QWidget *widget, QAction *after, QAction *action)
|
|
|
|
|
{
|
|
|
|
|
QList<QAction *> actions = widget->actions ();
|
|
|
|
|
|
|
|
|
|
QAction *before = 0;
|
|
|
|
|
if (after == 0) {
|
|
|
|
|
if (! actions.isEmpty ()) {
|
|
|
|
|
before = actions.front ();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int index = actions.indexOf (after);
|
|
|
|
|
if (index >= 0 && index + 1 < actions.size ()) {
|
|
|
|
|
before = actions [index + 1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
widget->insertAction (before, action);
|
|
|
|
|
|
|
|
|
|
return action;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar)
|
|
|
|
|
{
|
2020-02-04 23:54:29 +01:00
|
|
|
if (tbar) {
|
|
|
|
|
tbar->clear ();
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
std::set<std::pair<size_t, QAction *> > present_actions;
|
2020-02-04 23:54:29 +01:00
|
|
|
if (mbar) {
|
|
|
|
|
QList<QAction *> a = mbar->actions ();
|
|
|
|
|
for (QList<QAction *>::const_iterator i = a.begin (); i != a.end (); ++i) {
|
|
|
|
|
present_actions.insert (std::make_pair (id_from_action (*i), *i));
|
|
|
|
|
}
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2018-01-02 22:16:54 +01:00
|
|
|
|
2020-11-13 02:04:53 +01:00
|
|
|
QAction *prev_action = 0;
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
for (std::list<AbstractMenuItem>::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) {
|
|
|
|
|
|
|
|
|
|
if (c->has_submenu ()) {
|
|
|
|
|
|
|
|
|
|
if (c->name () == "@toolbar") {
|
|
|
|
|
|
2020-02-04 23:54:29 +01:00
|
|
|
if (tbar) {
|
|
|
|
|
build (tbar, c->children);
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
} else if (c->name ().find ("@@") == 0) {
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
// nothing: let build_detached build the menu
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
} else if (c->name ().find ("@") == 0) {
|
|
|
|
|
|
|
|
|
|
if (c->menu () == 0) {
|
2020-02-13 00:16:37 +01:00
|
|
|
QMenu *menu = new QMenu (tl::to_qstring (c->action ()->get_title ()), mp_dispatcher->menu_parent_widget ());
|
2022-08-15 23:43:45 +02:00
|
|
|
c->action ()->set_menu (menu, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
if (mp_dispatcher->menu_parent_widget ()) {
|
|
|
|
|
mp_dispatcher->menu_parent_widget ()->addAction (c->menu ()->menuAction ());
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-03 07:05:32 +02:00
|
|
|
// prepare a detached menu which can be used as context menus
|
2017-02-12 13:21:08 +01:00
|
|
|
build (c->menu (), c->children);
|
|
|
|
|
|
2020-02-04 23:54:29 +01:00
|
|
|
} else if (mbar) {
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
if (c->menu () == 0) {
|
2020-02-08 22:15:09 +01:00
|
|
|
QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ());
|
2020-02-13 00:16:37 +01:00
|
|
|
menu->setTitle (tl::to_qstring (c->action ()->get_title ()));
|
|
|
|
|
c->set_action (new Action (menu), true);
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = insert_action_after (mbar, prev_action, menu->menuAction ());
|
2017-02-12 13:21:08 +01:00
|
|
|
} else {
|
2018-01-02 22:16:54 +01:00
|
|
|
// Move the action to the end if present in the menu already
|
2018-02-09 01:23:59 +01:00
|
|
|
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ()));
|
2018-01-02 22:16:54 +01:00
|
|
|
if (a != present_actions.end ()) {
|
|
|
|
|
if (s_can_move_menu) {
|
2018-02-09 01:23:59 +01:00
|
|
|
mbar->removeAction (a->second);
|
2020-11-13 02:04:53 +01:00
|
|
|
insert_action_after (mbar, prev_action, a->second);
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = a->second;
|
2018-01-02 22:16:54 +01:00
|
|
|
present_actions.erase (*a);
|
|
|
|
|
} else {
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = insert_action_after (mbar, prev_action, c->menu ()->menuAction ());
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
build (c->menu (), c->children);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-04 23:54:29 +01:00
|
|
|
} else if (mbar) {
|
2020-11-13 02:04:53 +01:00
|
|
|
|
2018-01-02 22:16:54 +01:00
|
|
|
// Move the action to the end if present in the menu already
|
2020-02-13 00:16:37 +01:00
|
|
|
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ()->qaction ()), c->action ()->qaction ()));
|
2018-01-02 22:16:54 +01:00
|
|
|
if (a != present_actions.end ()) {
|
|
|
|
|
if (s_can_move_menu) {
|
2018-02-09 01:23:59 +01:00
|
|
|
mbar->removeAction (a->second);
|
2020-11-13 02:04:53 +01:00
|
|
|
insert_action_after (mbar, prev_action, a->second);
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = a->second;
|
2018-01-02 22:16:54 +01:00
|
|
|
present_actions.erase (*a);
|
|
|
|
|
} else {
|
2020-11-14 20:51:39 +01:00
|
|
|
prev_action = insert_action_after (mbar, prev_action, c->action ()->qaction ());
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2020-11-13 02:04:53 +01:00
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2018-01-02 22:16:54 +01:00
|
|
|
|
|
|
|
|
// Remove all actions that have vanished
|
2020-02-04 23:54:29 +01:00
|
|
|
if (mbar) {
|
|
|
|
|
for (std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
|
|
|
|
|
mbar->removeAction (a->second);
|
|
|
|
|
}
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::build (QMenu *m, std::list<AbstractMenuItem> &items)
|
|
|
|
|
{
|
2018-02-09 01:23:59 +01:00
|
|
|
std::set<std::pair<size_t, QAction *> > present_actions;
|
2018-01-02 22:16:54 +01:00
|
|
|
QList<QAction *> a = m->actions ();
|
2018-02-09 01:23:59 +01:00
|
|
|
for (QList<QAction *>::const_iterator i = a.begin (); i != a.end (); ++i) {
|
|
|
|
|
present_actions.insert (std::make_pair (id_from_action (*i), *i));
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-11-13 02:04:53 +01:00
|
|
|
QAction *prev_action = 0;
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
for (std::list<AbstractMenuItem>::iterator c = items.begin (); c != items.end (); ++c) {
|
2018-01-02 22:16:54 +01:00
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
if (c->has_submenu ()) {
|
2018-01-02 22:16:54 +01:00
|
|
|
|
|
|
|
|
if (! c->menu ()) {
|
2022-08-15 23:43:45 +02:00
|
|
|
|
2020-02-08 22:15:09 +01:00
|
|
|
QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ());
|
2020-02-13 00:16:37 +01:00
|
|
|
menu->setTitle (tl::to_qstring (c->action ()->get_title ()));
|
2022-08-15 23:43:45 +02:00
|
|
|
c->set_menu (menu, true);
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = insert_action_after (m, prev_action, menu->menuAction ());
|
2022-08-15 23:43:45 +02:00
|
|
|
|
2018-01-02 22:16:54 +01:00
|
|
|
} else {
|
2022-08-15 23:43:45 +02:00
|
|
|
|
2018-01-02 22:16:54 +01:00
|
|
|
// Move the action to the end if present in the menu already
|
2018-02-09 01:23:59 +01:00
|
|
|
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ()));
|
2018-01-02 22:16:54 +01:00
|
|
|
if (a != present_actions.end ()) {
|
|
|
|
|
if (s_can_move_menu) {
|
2018-02-09 01:23:59 +01:00
|
|
|
m->removeAction (a->second);
|
2020-11-13 02:04:53 +01:00
|
|
|
insert_action_after (m, prev_action, a->second);
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = a->second;
|
2018-01-02 22:16:54 +01:00
|
|
|
present_actions.erase (*a);
|
|
|
|
|
} else {
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = insert_action_after (m, prev_action, c->menu ()->menuAction ());
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2022-08-15 23:43:45 +02:00
|
|
|
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
build (c->menu (), c->children);
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
} else {
|
2018-01-02 22:16:54 +01:00
|
|
|
|
|
|
|
|
// Move the action to the end if present in the menu already
|
2020-02-13 00:16:37 +01:00
|
|
|
std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ()->qaction ()), c->action ()->qaction ()));
|
2018-01-02 22:16:54 +01:00
|
|
|
if (a != present_actions.end ()) {
|
|
|
|
|
if (s_can_move_menu) {
|
2018-02-09 01:23:59 +01:00
|
|
|
m->removeAction (a->second);
|
2020-11-13 02:04:53 +01:00
|
|
|
insert_action_after (m, prev_action, a->second);
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2020-11-13 02:04:53 +01:00
|
|
|
prev_action = a->second;
|
2018-01-02 22:16:54 +01:00
|
|
|
present_actions.erase (*a);
|
|
|
|
|
} else {
|
2020-11-14 20:51:39 +01:00
|
|
|
prev_action = insert_action_after (m, prev_action, c->action ()->qaction ());
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-01-02 22:16:54 +01:00
|
|
|
|
|
|
|
|
// Remove all actions that have vanished
|
2018-02-09 01:23:59 +01:00
|
|
|
for (std::set<std::pair<size_t, QAction *> >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) {
|
|
|
|
|
m->removeAction (a->second);
|
2018-01-02 22:16:54 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::build (QToolBar *t, std::list<AbstractMenuItem> &items)
|
|
|
|
|
{
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator c = items.begin (); c != items.end (); ++c) {
|
|
|
|
|
|
|
|
|
|
if (! c->children.empty ()) {
|
2018-02-09 01:23:59 +01:00
|
|
|
// To support tool buttons with menu we have to attach a helper menu
|
|
|
|
|
// item to the QAction object.
|
2022-08-15 23:43:45 +02:00
|
|
|
if (! c->menu ()) {
|
|
|
|
|
c->set_menu (new QMenu (0), true);
|
|
|
|
|
}
|
|
|
|
|
build (c->menu (), c->children);
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 23:43:45 +02:00
|
|
|
t->addAction (c->action ()->qaction ());
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMenu *
|
|
|
|
|
AbstractMenu::detached_menu (const std::string &name)
|
|
|
|
|
{
|
|
|
|
|
AbstractMenuItem *item = find_item_exact ("@" + name);
|
|
|
|
|
tl_assert (item != 0);
|
|
|
|
|
return item->menu ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMenu *
|
2018-02-09 01:23:59 +01:00
|
|
|
AbstractMenu::menu (const std::string &path)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
AbstractMenuItem *item = find_item_exact (path);
|
|
|
|
|
if (item) {
|
|
|
|
|
return item->menu ();
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
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);
|
2020-02-13 00:16:37 +01:00
|
|
|
return item != 0 && item->action ()->is_separator ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
const Action *AbstractMenu::action(const std::string &path) const
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
return (const_cast<AbstractMenu *> (this))->action (path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Action *AbstractMenu::action(const std::string &path)
|
|
|
|
|
{
|
|
|
|
|
AbstractMenuItem *item = find_item_exact (path);
|
2020-02-19 01:06:39 +01:00
|
|
|
return item ? item->action () : 0;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
std::vector<std::string>
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::items (const std::string &path) const
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::string> res;
|
|
|
|
|
|
|
|
|
|
const AbstractMenuItem *item = find_item_exact (path);
|
|
|
|
|
if (item) {
|
|
|
|
|
res.reserve (item->children.size ());
|
|
|
|
|
for (std::list <AbstractMenuItem>::const_iterator c = item->children.begin (); c != item->children.end (); ++c) {
|
|
|
|
|
res.push_back (c->name ());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
AbstractMenu::insert_item (const std::string &p, const std::string &name, Action *action)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2020-02-07 00:41:37 +01:00
|
|
|
tl::Extractor extr (p.c_str ());
|
|
|
|
|
while (! extr.at_end ()) {
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2020-02-07 00:41:37 +01:00
|
|
|
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
|
|
|
|
|
path_type path = find_item (extr);
|
|
|
|
|
if (! path.empty ()) {
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-07 00:41:37 +01:00
|
|
|
AbstractMenuItem *parent = path.back ().first;
|
|
|
|
|
std::list<AbstractMenuItem>::iterator iter = path.back ().second;
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-07 00:41:37 +01:00
|
|
|
// insert the new item
|
2020-02-10 00:07:41 +01:00
|
|
|
parent->children.insert (iter, AbstractMenuItem (mp_dispatcher));
|
2020-02-07 00:41:37 +01:00
|
|
|
--iter;
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-07 00:41:37 +01:00
|
|
|
iter->setup_item (parent->name (), name, action);
|
|
|
|
|
|
|
|
|
|
// find any items with the same name and remove them
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator existing = parent->children.begin (); existing != parent->children.end (); ) {
|
|
|
|
|
std::list<AbstractMenuItem>::iterator existing_next = existing;
|
|
|
|
|
++existing_next;
|
|
|
|
|
if (existing->name () == iter->name () && existing != iter) {
|
|
|
|
|
parent->children.erase (existing);
|
|
|
|
|
}
|
|
|
|
|
existing = existing_next;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2020-02-07 00:41:37 +01:00
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
emit_changed ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-10-22 23:06:42 +02:00
|
|
|
AbstractMenu::insert_separator (const std::string &p, const std::string &name)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2017-10-22 23:06:42 +02:00
|
|
|
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
|
2020-02-07 00:41:37 +01:00
|
|
|
tl::Extractor extr (p.c_str ());
|
|
|
|
|
path_type path = find_item (extr);
|
2017-10-22 23:06:42 +02:00
|
|
|
if (! path.empty ()) {
|
|
|
|
|
|
|
|
|
|
AbstractMenuItem *parent = path.back ().first;
|
|
|
|
|
std::list<AbstractMenuItem>::iterator iter = path.back ().second;
|
|
|
|
|
|
2020-02-10 00:07:41 +01:00
|
|
|
parent->children.insert (iter, AbstractMenuItem (mp_dispatcher));
|
2017-10-22 23:06:42 +02:00
|
|
|
--iter;
|
2020-02-13 00:16:37 +01:00
|
|
|
Action *action = new Action ();
|
|
|
|
|
action->set_separator (true);
|
2017-10-22 23:06:42 +02:00
|
|
|
iter->setup_item (parent->name (), name, action);
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
emit_changed ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
AbstractMenu::insert_menu (const std::string &p, const std::string &name, Action *action)
|
2017-10-22 23:06:42 +02:00
|
|
|
{
|
2022-09-02 00:47:21 +02:00
|
|
|
#if defined(HAVE_QT)
|
|
|
|
|
if (! action->menu () && mp_dispatcher && mp_dispatcher->menu_parent_widget ()) {
|
2022-08-15 23:43:45 +02:00
|
|
|
action->set_menu (new QMenu (), true);
|
|
|
|
|
}
|
2022-09-02 00:47:21 +02:00
|
|
|
#endif
|
2022-08-15 23:43:45 +02:00
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
|
2020-02-07 00:41:37 +01:00
|
|
|
tl::Extractor extr (p.c_str ());
|
|
|
|
|
path_type path = find_item (extr);
|
2017-10-22 23:06:42 +02:00
|
|
|
if (! path.empty ()) {
|
|
|
|
|
|
|
|
|
|
AbstractMenuItem *parent = path.back ().first;
|
|
|
|
|
std::list<AbstractMenuItem>::iterator iter = path.back ().second;
|
|
|
|
|
|
2020-02-10 00:07:41 +01:00
|
|
|
parent->children.insert (iter, AbstractMenuItem (mp_dispatcher));
|
2017-10-22 23:06:42 +02:00
|
|
|
--iter;
|
|
|
|
|
iter->setup_item (parent->name (), name, action);
|
|
|
|
|
iter->set_has_submenu ();
|
|
|
|
|
|
|
|
|
|
// find any items with the same name and remove them
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator existing = parent->children.begin (); existing != parent->children.end (); ) {
|
|
|
|
|
std::list<AbstractMenuItem>::iterator existing_next = existing;
|
|
|
|
|
++existing_next;
|
|
|
|
|
if (existing->name () == iter->name () && existing != iter) {
|
|
|
|
|
parent->children.erase (existing);
|
|
|
|
|
}
|
|
|
|
|
existing = existing_next;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
emit_changed ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::insert_menu (const std::string &path, const std::string &name, const std::string &title)
|
|
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
insert_menu (path, name, new Action (title));
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-10 18:40:24 +01:00
|
|
|
void
|
|
|
|
|
AbstractMenu::clear_menu (const std::string &p)
|
|
|
|
|
{
|
|
|
|
|
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
|
2020-02-07 00:41:37 +01:00
|
|
|
tl::Extractor extr (p.c_str ());
|
|
|
|
|
path_type path = find_item (extr);
|
2018-02-10 18:40:24 +01:00
|
|
|
if (! path.empty () && ! path.back ().second->children.empty ()) {
|
|
|
|
|
path.back ().second->children.clear ();
|
2020-02-13 00:16:37 +01:00
|
|
|
emit_changed ();
|
2018-02-10 18:40:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-10-22 23:06:42 +02:00
|
|
|
AbstractMenu::delete_item (const std::string &p)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2017-10-22 23:06:42 +02:00
|
|
|
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator > > path_type;
|
2020-02-07 00:41:37 +01:00
|
|
|
tl::Extractor extr (p.c_str ());
|
|
|
|
|
path_type path = find_item (extr);
|
2017-10-22 23:06:42 +02:00
|
|
|
if (! path.empty ()) {
|
|
|
|
|
|
|
|
|
|
for (path_type::const_reverse_iterator p = path.rbegin (); p != path.rend (); ++p) {
|
|
|
|
|
|
|
|
|
|
if (p->second == p->first->children.end ()) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (p != path.rbegin () && (! p->second->remove_on_empty () || ! p->second->children.empty ())) {
|
2021-06-03 07:05:32 +02:00
|
|
|
// stop on non-empty parent menus
|
2017-10-22 23:06:42 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p->first->children.erase (p->second);
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
emit_changed ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
static void do_delete_items (AbstractMenuItem &parent, Action *action)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2017-10-22 23:06:42 +02:00
|
|
|
for (std::list<AbstractMenuItem>::iterator l = parent.children.begin (); l != parent.children.end (); ) {
|
2017-02-12 13:21:08 +01:00
|
|
|
std::list<AbstractMenuItem>::iterator ll = l;
|
|
|
|
|
++ll;
|
|
|
|
|
if (l->action () == action) {
|
2017-10-22 23:06:42 +02:00
|
|
|
parent.children.erase (l);
|
2017-02-12 13:21:08 +01:00
|
|
|
} else {
|
2017-10-22 23:06:42 +02:00
|
|
|
do_delete_items (*l, action);
|
|
|
|
|
if (l->remove_on_empty () && l->children.empty ()) {
|
|
|
|
|
parent.children.erase (l);
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
l = ll;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-02-13 00:16:37 +01:00
|
|
|
AbstractMenu::delete_items (Action *action)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2020-02-13 00:16:37 +01:00
|
|
|
if (action) {
|
|
|
|
|
do_delete_items (m_root, action);
|
|
|
|
|
emit_changed ();
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-01 18:49:42 +02:00
|
|
|
const AbstractMenuItem *
|
|
|
|
|
AbstractMenu::find_item_for_action (const Action *action, const AbstractMenuItem *from) const
|
|
|
|
|
{
|
|
|
|
|
return (const_cast<AbstractMenu *> (this))->find_item_for_action (action, const_cast<AbstractMenuItem *> (from));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AbstractMenuItem *
|
|
|
|
|
AbstractMenu::find_item_for_action (const Action *action, AbstractMenuItem *from)
|
|
|
|
|
{
|
|
|
|
|
if (! from) {
|
|
|
|
|
from = const_cast<AbstractMenuItem *> (&root ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (from->action () == action) {
|
|
|
|
|
return from;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto i = from->children.begin (); i != from->children.end (); ++i) {
|
|
|
|
|
AbstractMenuItem *item = find_item_for_action (action, i.operator-> ());
|
|
|
|
|
if (item) {
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
const AbstractMenuItem *
|
|
|
|
|
AbstractMenu::find_item_exact (const std::string &path) const
|
|
|
|
|
{
|
|
|
|
|
return (const_cast<AbstractMenu *> (this))->find_item_exact (path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AbstractMenuItem *
|
2018-02-09 01:23:59 +01:00
|
|
|
AbstractMenu::find_item_exact (const std::string &path)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
|
|
|
|
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<AbstractMenuItem>::iterator c = item->children.begin ();
|
|
|
|
|
++n;
|
|
|
|
|
while (--n > 0 && c != item->children.end ()) {
|
|
|
|
|
++c;
|
|
|
|
|
}
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
return 0;
|
2018-02-09 01:23:59 +01:00
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
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<AbstractMenuItem>::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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator> >
|
2020-02-07 00:41:37 +01:00
|
|
|
AbstractMenu::find_item (tl::Extractor &extr)
|
2017-02-12 13:21:08 +01:00
|
|
|
{
|
2017-10-22 23:06:42 +02:00
|
|
|
typedef std::vector<std::pair<AbstractMenuItem *, std::list<AbstractMenuItem>::iterator> > path_type;
|
|
|
|
|
path_type path;
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenuItem *parent = &m_root;
|
|
|
|
|
std::list<AbstractMenuItem>::iterator iter = m_root.children.end ();
|
|
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
while (parent && ! extr.at_end ()) {
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2020-02-07 00:41:37 +01:00
|
|
|
if (extr.test (";")) {
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
} else if (extr.test ("#")) {
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
unsigned int n = 0;
|
|
|
|
|
extr.try_read (n);
|
|
|
|
|
iter = parent->children.begin ();
|
|
|
|
|
++n;
|
|
|
|
|
while (--n > 0 && iter != parent->children.end ()) {
|
|
|
|
|
++iter;
|
|
|
|
|
}
|
|
|
|
|
if (n > 0) {
|
2017-10-22 23:06:42 +02:00
|
|
|
return path_type ();
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
} else {
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
std::string n;
|
2020-02-07 19:05:45 +01:00
|
|
|
extr.read (n, ".;+>(");
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2020-02-07 01:41:03 +01:00
|
|
|
if (n.empty ()) {
|
|
|
|
|
|
|
|
|
|
// skip (avoids infinite loops on wrong paths)
|
2020-05-02 13:45:20 +02:00
|
|
|
while (! extr.at_end () && *extr != ';') {
|
|
|
|
|
++extr;
|
|
|
|
|
}
|
2020-02-07 01:41:03 +01:00
|
|
|
|
|
|
|
|
} else if (n == "begin") {
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
iter = parent->children.begin ();
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
} else if (n == "end") {
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
iter = parent->children.end ();
|
2017-04-15 17:51:04 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
} else {
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
std::string nn;
|
|
|
|
|
if (extr.test (">")) {
|
2020-02-07 19:05:45 +01:00
|
|
|
extr.read (nn, ".;+>(");
|
2017-10-24 23:49:16 +02:00
|
|
|
}
|
2017-04-15 17:51:04 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
std::string name (parent->name ());
|
|
|
|
|
if (! name.empty ()) {
|
|
|
|
|
name += ".";
|
|
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
std::string nname;
|
|
|
|
|
nname = name + nn;
|
|
|
|
|
name += n;
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
bool after = extr.test ("+");
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
std::string ndesc;
|
|
|
|
|
if (! nn.empty () && extr.test ("(")) {
|
2020-02-07 19:05:45 +01:00
|
|
|
extr.read_word_or_quoted (ndesc, " _.;$");
|
2017-10-24 23:49:16 +02:00
|
|
|
extr.test (")");
|
|
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
AbstractMenuItem *p = parent;
|
|
|
|
|
parent = 0;
|
2017-02-12 13:21:08 +01:00
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
// Look for the next path item
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator c = p->children.begin (); c != p->children.end (); ++c) {
|
|
|
|
|
if (c->name () == name) {
|
|
|
|
|
if (after && nn.empty ()) {
|
|
|
|
|
++c;
|
|
|
|
|
}
|
|
|
|
|
parent = p;
|
|
|
|
|
iter = c;
|
|
|
|
|
break;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
2017-10-22 23:06:42 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
// If that's not found, check whether we are supposed to create one:
|
|
|
|
|
// identify the insert position and create a new entry there.
|
|
|
|
|
if (! parent && ! nn.empty ()) {
|
|
|
|
|
|
|
|
|
|
if (nn == "begin") {
|
|
|
|
|
parent = p;
|
|
|
|
|
iter = parent->children.begin ();
|
|
|
|
|
} else if (nn == "end") {
|
|
|
|
|
parent = p;
|
|
|
|
|
iter = parent->children.end ();
|
|
|
|
|
} else {
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator c = p->children.begin (); c != p->children.end (); ++c) {
|
|
|
|
|
if (c->name () == nname) {
|
|
|
|
|
if (after) {
|
|
|
|
|
++c;
|
|
|
|
|
}
|
|
|
|
|
parent = p;
|
|
|
|
|
iter = c;
|
|
|
|
|
break;
|
2017-10-22 23:06:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
if (parent) {
|
2020-02-10 00:07:41 +01:00
|
|
|
parent->children.insert (iter, AbstractMenuItem (mp_dispatcher));
|
2017-10-24 23:49:16 +02:00
|
|
|
--iter;
|
2020-02-13 00:16:37 +01:00
|
|
|
iter->setup_item (parent->name (), n, new Action ());
|
2017-10-24 23:49:16 +02:00
|
|
|
iter->set_has_submenu ();
|
|
|
|
|
iter->set_remove_on_empty ();
|
|
|
|
|
iter->set_action_title (ndesc.empty () ? n : ndesc);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-24 23:49:16 +02:00
|
|
|
if (! parent) {
|
|
|
|
|
return path_type ();
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
path.push_back (std::make_pair (parent, iter));
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
extr.test (".");
|
|
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
if (iter == parent->children.end ()) {
|
|
|
|
|
parent = 0;
|
|
|
|
|
} else {
|
|
|
|
|
parent = iter.operator-> ();
|
|
|
|
|
}
|
2017-02-12 13:21:08 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-22 23:06:42 +02:00
|
|
|
return path;
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
std::vector<std::string>
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::group (const std::string &name) const
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::string> grp;
|
|
|
|
|
collect_group (grp, name, m_root);
|
|
|
|
|
return grp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
std::vector<lay::Action *>
|
|
|
|
|
AbstractMenu::group_actions (const std::string &name)
|
2020-02-07 23:52:32 +01:00
|
|
|
{
|
|
|
|
|
std::vector<std::string> grp = group (name);
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
std::vector<lay::Action *> actions;
|
2020-02-07 23:52:32 +01:00
|
|
|
actions.reserve (grp.size ());
|
|
|
|
|
for (std::vector<std::string>::const_iterator g = grp.begin (); g != grp.end (); ++g) {
|
|
|
|
|
actions.push_back (action (*g));
|
|
|
|
|
}
|
|
|
|
|
return actions;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 01:23:59 +01:00
|
|
|
void
|
2017-02-12 13:21:08 +01:00
|
|
|
AbstractMenu::collect_group (std::vector<std::string> &grp, const std::string &name, const AbstractMenuItem &item) const
|
|
|
|
|
{
|
|
|
|
|
for (std::list<AbstractMenuItem>::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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-13 00:16:37 +01:00
|
|
|
void
|
|
|
|
|
AbstractMenu::emit_changed ()
|
|
|
|
|
{
|
|
|
|
|
m_config_actions_valid = false;
|
2022-06-07 23:27:34 +02:00
|
|
|
#if defined(HAVE_QT)
|
2020-02-13 00:16:37 +01:00
|
|
|
emit changed ();
|
2022-06-07 23:27:34 +02:00
|
|
|
#endif
|
2020-02-13 00:16:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<ConfigureAction *> AbstractMenu::configure_actions (const std::string &name)
|
|
|
|
|
{
|
|
|
|
|
if (! m_config_actions_valid) {
|
|
|
|
|
|
|
|
|
|
std::vector<lay::ConfigureAction *> ca;
|
|
|
|
|
collect_configure_actions (ca, m_root);
|
|
|
|
|
|
|
|
|
|
m_config_action_by_name.clear ();
|
|
|
|
|
for (std::vector<lay::ConfigureAction *>::const_iterator c = ca.begin (); c != ca.end (); ++c) {
|
|
|
|
|
m_config_action_by_name [(*c)->get_cname ()].push_back (*c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_config_actions_valid = true;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::map<std::string, std::vector<lay::ConfigureAction *> >::const_iterator c = m_config_action_by_name.find (name);
|
|
|
|
|
return c == m_config_action_by_name.end () ? std::vector<lay::ConfigureAction *> () : c->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AbstractMenu::collect_configure_actions (std::vector<lay::ConfigureAction *> &ca, AbstractMenuItem &item)
|
|
|
|
|
{
|
|
|
|
|
for (std::list<AbstractMenuItem>::iterator c = item.children.begin (); c != item.children.end (); ++c) {
|
|
|
|
|
ConfigureAction *cca = dynamic_cast<ConfigureAction *> (c->action ());
|
|
|
|
|
if (cca) {
|
|
|
|
|
ca.push_back (cca);
|
|
|
|
|
}
|
|
|
|
|
collect_configure_actions (ca, *c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-26 23:10:26 +01:00
|
|
|
void
|
|
|
|
|
AbstractMenu::get_shortcuts (const std::string &root, std::map<std::string, std::string> &bindings, bool with_defaults)
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::string> items = this->items (root);
|
|
|
|
|
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
|
|
|
|
|
if (i->size () > 0) {
|
|
|
|
|
if (is_valid (*i) && action (*i)->is_visible ()) {
|
|
|
|
|
if (is_menu (*i)) {
|
|
|
|
|
// a menu must be listed (so it can be hidden), but does not have a shortcut
|
|
|
|
|
// but we don't include special menus
|
|
|
|
|
if (i->at (0) != '@') {
|
|
|
|
|
bindings.insert (std::make_pair (*i, std::string ()));
|
|
|
|
|
}
|
|
|
|
|
get_shortcuts (*i, bindings, with_defaults);
|
|
|
|
|
} else if (! is_separator (*i)) {
|
|
|
|
|
bindings.insert (std::make_pair (*i, with_defaults ? action (*i)->get_default_shortcut () : action (*i)->get_effective_shortcut ()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-12 13:21:08 +01:00
|
|
|
}
|