ActionManager API cleanup.

d-pointer instead of inheritance
static methods

Change-Id: I7b2f0c8b05ad3951e1ff26a7d4e08e195d2dd258
Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
Eike Ziller
2012-05-24 13:49:06 +02:00
committed by hjk
parent 7c7ccdc764
commit 3934347fe9
78 changed files with 1198 additions and 1338 deletions

View File

@@ -30,6 +30,7 @@
**
**************************************************************************/
#include "actionmanager.h"
#include "actionmanager_p.h"
#include "mainwindow.h"
#include "actioncontainer_p.h"
@@ -53,6 +54,9 @@ namespace {
enum { warnAboutFindFailures = 0 };
}
using namespace Core;
using namespace Core::Internal;
/*!
\class Core::ActionManager
\mainclass
@@ -61,11 +65,8 @@ namespace {
menu items and keyboard shortcuts.
The ActionManager is the central bookkeeper of actions and their shortcuts and layout.
You get the only implementation of this class from the core interface
ICore::actionManager() method, e.g.
\code
Core::ICore::actionManager()
\endcode
It is a singleton containing mostly static methods. If you need access to the instance,
e.g. for connecting to signals, is its ActionManager::instance() method.
The main reasons for the need of this class is to provide a central place where the user
can specify all his keyboard shortcuts, and to provide a solution for actions that should
@@ -73,7 +74,7 @@ namespace {
\section1 Contexts
All actions that are registered with the same string ID (but different context lists)
All actions that are registered with the same Id (but different context lists)
are considered to be overloads of the same command, represented by an instance
of the Command class.
Exactly only one of the registered actions with the same ID is active at any time.
@@ -99,9 +100,8 @@ namespace {
To register a globally active action "My Action"
put the following in your plugin's IPlugin::initialize method:
\code
Core::ActionManager *am = Core::ICore::actionManager();
QAction *myAction = new QAction(tr("My Action"), this);
Core::Command *cmd = am->registerAction(myAction,
Core::Command *cmd = Core::ActionManager::registerAction(myAction,
"myplugin.myaction",
Core::Context(C_GLOBAL));
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+u")));
@@ -125,7 +125,7 @@ namespace {
Following the example adding "My Action" to the "Tools" menu would be done by
\code
am->actionContainer(Core::M_TOOLS)->addAction(cmd);
Core::ActionManager::actionContainer(Core::M_TOOLS)->addAction(cmd);
\endcode
\section1 Important Guidelines:
@@ -146,9 +146,36 @@ namespace {
\sa Core::IContext
*/
static ActionManager *m_instance = 0;
/*!
\fn ActionContainer *ActionManager::createMenu(const Id &id)
\brief Creates a new menu with the given string \a id.
\internal
*/
ActionManager::ActionManager(QObject *parent)
: QObject(parent),
d(new ActionManagerPrivate())
{
m_instance = this;
}
/*!
\internal
*/
ActionManager::~ActionManager()
{
delete d;
}
/*!
* \return Singleton action manager instance
*/
ActionManager *ActionManager::instance()
{
return m_instance;
}
/*!
\brief Creates a new menu with the given \a id.
Returns a new ActionContainer that you can use to get the QMenu instance
or to add menu items to the menu. The ActionManager owns
@@ -156,19 +183,51 @@ namespace {
Add your menu to some other menu or a menu bar via the
ActionManager::actionContainer and ActionContainer::addMenu methods.
*/
ActionContainer *ActionManager::createMenu(const Id &id)
{
const ActionManagerPrivate::IdContainerMap::const_iterator it = m_instance->d->m_idContainerMap.constFind(id);
if (it != m_instance->d->m_idContainerMap.constEnd())
return it.value();
QMenu *m = new QMenu(ICore::mainWindow());
m->setObjectName(QLatin1String(id.name()));
MenuActionContainer *mc = new MenuActionContainer(id);
mc->setMenu(m);
m_instance->d->m_idContainerMap.insert(id, mc);
connect(mc, SIGNAL(destroyed()), m_instance->d, SLOT(containerDestroyed()));
return mc;
}
/*!
\fn ActionContainer *ActionManager::createMenuBar(const Id &id)
\brief Creates a new menu bar with the given string \a id.
\brief Creates a new menu bar with the given \a id.
Returns a new ActionContainer that you can use to get the QMenuBar instance
or to add menus to the menu bar. The ActionManager owns
the returned ActionContainer.
*/
ActionContainer *ActionManager::createMenuBar(const Id &id)
{
const ActionManagerPrivate::IdContainerMap::const_iterator it = m_instance->d->m_idContainerMap.constFind(id);
if (it != m_instance->d->m_idContainerMap.constEnd())
return it.value();
QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X)
mb->setObjectName(id.toString());
MenuBarActionContainer *mbc = new MenuBarActionContainer(id);
mbc->setMenuBar(mb);
m_instance->d->m_idContainerMap.insert(id, mbc);
connect(mbc, SIGNAL(destroyed()), m_instance->d, SLOT(containerDestroyed()));
return mbc;
}
/*!
\fn Command *ActionManager::registerAction(QAction *action, const Id &id, const Context &context, bool scriptable)
\brief Makes an \a action known to the system under the specified string \a id.
\brief Makes an \a action known to the system under the specified \a id.
Returns a command object that represents the action in the application and is
owned by the ActionManager. You can register several actions with the
@@ -178,10 +237,19 @@ namespace {
A scriptable action can be called from a script without the need for the user
to interact with it.
*/
Command *ActionManager::registerAction(QAction *action, const Id &id, const Context &context, bool scriptable)
{
Action *a = m_instance->d->overridableAction(id);
if (a) {
a->addOverrideAction(action, context, scriptable);
emit m_instance->commandListChanged();
emit m_instance->commandAdded(id.toString());
}
return a;
}
/*!
\fn Command *ActionManager::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context, bool scriptable)
\brief Makes a \a shortcut known to the system under the specified string \a id.
\brief Makes a \a shortcut known to the system under the specified \a id.
Returns a command object that represents the shortcut in the application and is
owned by the ActionManager. You can registered several shortcuts with the
@@ -191,47 +259,199 @@ namespace {
A scriptable shortcut can be called from a script without the need for the user
to interact with it.
*/
Command *ActionManager::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context, bool scriptable)
{
Shortcut *sc = 0;
if (CommandPrivate *c = m_instance->d->m_idCmdMap.value(id, 0)) {
sc = qobject_cast<Shortcut *>(c);
if (!sc) {
qWarning() << "registerShortcut: id" << id.name()
<< "is registered with a different command type.";
return c;
}
} else {
sc = new Shortcut(id);
m_instance->d->m_idCmdMap.insert(id, sc);
}
if (sc->shortcut()) {
qWarning() << "registerShortcut: action already registered, id" << id.name() << ".";
return sc;
}
if (!m_instance->d->hasContext(context))
shortcut->setEnabled(false);
shortcut->setObjectName(id.toString());
shortcut->setParent(ICore::mainWindow());
sc->setShortcut(shortcut);
sc->setScriptable(scriptable);
if (context.isEmpty())
sc->setContext(Context(0));
else
sc->setContext(context);
emit m_instance->commandListChanged();
emit m_instance->commandAdded(id.toString());
if (isPresentationModeEnabled())
connect(sc->shortcut(), SIGNAL(activated()), m_instance->d, SLOT(shortcutTriggered()));
return sc;
}
/*!
\fn Command *ActionManager::command(const Id &id) const
\brief Returns the Command object that is known to the system
under the given string \a id.
under the given \a id.
\sa ActionManager::registerAction()
*/
Command *ActionManager::command(const Id &id)
{
const ActionManagerPrivate::IdCmdMap::const_iterator it = m_instance->d->m_idCmdMap.constFind(id);
if (it == m_instance->d->m_idCmdMap.constEnd()) {
if (warnAboutFindFailures)
qWarning() << "ActionManagerPrivate::command(): failed to find :"
<< id.name();
return 0;
}
return it.value();
}
/*!
\fn ActionContainer *ActionManager::actionContainer(const Id &id) const
\brief Returns the IActionContainter object that is know to the system
under the given string \a id.
under the given \a id.
\sa ActionManager::createMenu()
\sa ActionManager::createMenuBar()
*/
ActionContainer *ActionManager::actionContainer(const Id &id)
{
const ActionManagerPrivate::IdContainerMap::const_iterator it = m_instance->d->m_idContainerMap.constFind(id);
if (it == m_instance->d->m_idContainerMap.constEnd()) {
if (warnAboutFindFailures)
qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :"
<< id.name();
return 0;
}
return it.value();
}
/*!
\fn Command *ActionManager::unregisterAction(QAction *action, const Id &id)
\brief Removes the knowledge about an \a action under the specified string \a id.
* \brief Returns all commands that have been registered.
*/
QList<Command *> ActionManager::commands()
{
// transform list of CommandPrivate into list of Command
QList<Command *> result;
foreach (Command *cmd, m_instance->d->m_idCmdMap.values())
result << cmd;
return result;
}
/*!
\brief Removes the knowledge about an \a action under the specified \a id.
Usually you do not need to unregister actions. The only valid use case for unregistering
actions, is for actions that represent user definable actions, like for the custom Locator
filters. If the user removes such an action, it also has to be unregistered from the action manager,
to make it disappear from shortcut settings etc.
*/
void ActionManager::unregisterAction(QAction *action, const Id &id)
{
Action *a = 0;
CommandPrivate *c = m_instance->d->m_idCmdMap.value(id, 0);
QTC_ASSERT(c, return);
a = qobject_cast<Action *>(c);
if (!a) {
qWarning() << "unregisterAction: id" << id.name()
<< "is registered with a different command type.";
return;
}
a->removeOverrideAction(action);
if (a->isEmpty()) {
// clean up
// ActionContainers listen to the commands' destroyed signals
ICore::mainWindow()->removeAction(a->action());
delete a->action();
m_instance->d->m_idCmdMap.remove(id);
delete a;
}
emit m_instance->commandListChanged();
}
/*!
\fn ActionManager::ActionManager(QObject *parent)
\internal
*/
/*!
\fn ActionManager::~ActionManager()
\internal
*/
\brief Removes the knowledge about a shortcut under the specified \a id.
using namespace Core;
using namespace Core::Internal;
Usually you do not need to unregister shortcuts. The only valid use case for unregistering
shortcuts, is for shortcuts that represent user definable actions. If the user removes such an action,
a corresponding shortcut also has to be unregistered from the action manager,
to make it disappear from shortcut settings etc.
*/
void ActionManager::unregisterShortcut(const Core::Id &id)
{
Shortcut *sc = 0;
CommandPrivate *c = m_instance->d->m_idCmdMap.value(id, 0);
QTC_ASSERT(c, return);
sc = qobject_cast<Shortcut *>(c);
if (!sc) {
qWarning() << "unregisterShortcut: id" << id.name()
<< "is registered with a different command type.";
return;
}
delete sc->shortcut();
m_instance->d->m_idCmdMap.remove(id);
delete sc;
emit m_instance->commandListChanged();
}
ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
void ActionManager::setPresentationModeEnabled(bool enabled)
{
if (enabled == isPresentationModeEnabled())
return;
// Signal/slots to commands:
foreach (Command *c, commands()) {
if (c->action()) {
if (enabled)
connect(c->action(), SIGNAL(triggered()), m_instance->d, SLOT(actionTriggered()));
else
disconnect(c->action(), SIGNAL(triggered()), m_instance->d, SLOT(actionTriggered()));
}
if (c->shortcut()) {
if (enabled)
connect(c->shortcut(), SIGNAL(activated()), m_instance->d, SLOT(shortcutTriggered()));
else
disconnect(c->shortcut(), SIGNAL(activated()), m_instance->d, SLOT(shortcutTriggered()));
}
}
// The label for the shortcuts:
if (!m_instance->d->m_presentationLabel) {
m_instance->d->m_presentationLabel = new QLabel(0, Qt::ToolTip | Qt::WindowStaysOnTopHint);
QFont font = m_instance->d->m_presentationLabel->font();
font.setPixelSize(45);
m_instance->d->m_presentationLabel->setFont(font);
m_instance->d->m_presentationLabel->setAlignment(Qt::AlignCenter);
m_instance->d->m_presentationLabel->setMargin(5);
connect(&m_instance->d->m_presentationLabelTimer, SIGNAL(timeout()), m_instance->d->m_presentationLabel, SLOT(hide()));
} else {
m_instance->d->m_presentationLabelTimer.stop();
delete m_instance->d->m_presentationLabel;
m_instance->d->m_presentationLabel = 0;
}
}
bool ActionManager::isPresentationModeEnabled()
{
return m_instance->d->m_presentationLabel;
}
bool ActionManager::hasContext(int context)
{
return m_instance->d->m_context.contains(context);
}
/*!
\class ActionManagerPrivate
@@ -239,13 +459,10 @@ ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
\internal
*/
ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
: ActionManager(mainWnd),
m_mainWnd(mainWnd),
m_presentationLabel(0)
ActionManagerPrivate::ActionManagerPrivate()
: m_presentationLabel(0)
{
m_presentationLabelTimer.setInterval(1000);
m_instance = this;
}
ActionManagerPrivate::~ActionManagerPrivate()
@@ -257,25 +474,6 @@ ActionManagerPrivate::~ActionManagerPrivate()
qDeleteAll(m_idCmdMap.values());
}
ActionManagerPrivate *ActionManagerPrivate::instance()
{
return m_instance;
}
QList<Command *> ActionManagerPrivate::commands() const
{
// transform list of CommandPrivate into list of Command
QList<Command *> result;
foreach (Command *cmd, m_idCmdMap.values())
result << cmd;
return result;
}
bool ActionManagerPrivate::hasContext(int context) const
{
return m_context.contains(context);
}
QDebug operator<<(QDebug in, const Context &context)
{
in << "CONTEXT: ";
@@ -304,42 +502,6 @@ bool ActionManagerPrivate::hasContext(const Context &context) const
return false;
}
ActionContainer *ActionManagerPrivate::createMenu(const Id &id)
{
const IdContainerMap::const_iterator it = m_idContainerMap.constFind(id);
if (it != m_idContainerMap.constEnd())
return it.value();
QMenu *m = new QMenu(m_mainWnd);
m->setObjectName(QLatin1String(id.name()));
MenuActionContainer *mc = new MenuActionContainer(id);
mc->setMenu(m);
m_idContainerMap.insert(id, mc);
connect(mc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
return mc;
}
ActionContainer *ActionManagerPrivate::createMenuBar(const Id &id)
{
const IdContainerMap::const_iterator it = m_idContainerMap.constFind(id);
if (it != m_idContainerMap.constEnd())
return it.value();
QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X)
mb->setObjectName(id.toString());
MenuBarActionContainer *mbc = new MenuBarActionContainer(id);
mbc->setMenuBar(mb);
m_idContainerMap.insert(id, mbc);
connect(mbc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
return mbc;
}
void ActionManagerPrivate::containerDestroyed()
{
ActionContainerPrivate *container = static_cast<ActionContainerPrivate *>(sender());
@@ -362,13 +524,13 @@ void ActionManagerPrivate::shortcutTriggered()
void ActionManagerPrivate::showShortcutPopup(const QString &shortcut)
{
if (shortcut.isEmpty() || !isPresentationModeEnabled())
if (shortcut.isEmpty() || !ActionManager::isPresentationModeEnabled())
return;
m_presentationLabel->setText(shortcut);
m_presentationLabel->adjustSize();
QPoint p = m_mainWnd->mapToGlobal(m_mainWnd->rect().center() - m_presentationLabel->rect().center());
QPoint p = ICore::mainWindow()->mapToGlobal(ICore::mainWindow()->rect().center() - m_presentationLabel->rect().center());
m_presentationLabel->move(p);
m_presentationLabel->show();
@@ -376,17 +538,6 @@ void ActionManagerPrivate::showShortcutPopup(const QString &shortcut)
m_presentationLabelTimer.start();
}
Command *ActionManagerPrivate::registerAction(QAction *action, const Id &id, const Context &context, bool scriptable)
{
Action *a = overridableAction(id);
if (a) {
a->addOverrideAction(action, context, scriptable);
emit commandListChanged();
emit commandAdded(id.toString());
}
return a;
}
Action *ActionManagerPrivate::overridableAction(const Id &id)
{
Action *a = 0;
@@ -400,105 +551,18 @@ Action *ActionManagerPrivate::overridableAction(const Id &id)
} else {
a = new Action(id);
m_idCmdMap.insert(id, a);
m_mainWnd->addAction(a->action());
ICore::mainWindow()->addAction(a->action());
a->action()->setObjectName(id.toString());
a->action()->setShortcutContext(Qt::ApplicationShortcut);
a->setCurrentContext(m_context);
if (isPresentationModeEnabled())
if (ActionManager::isPresentationModeEnabled())
connect(a->action(), SIGNAL(triggered()), this, SLOT(actionTriggered()));
}
return a;
}
void ActionManagerPrivate::unregisterAction(QAction *action, const Id &id)
{
Action *a = 0;
CommandPrivate *c = m_idCmdMap.value(id, 0);
QTC_ASSERT(c, return);
a = qobject_cast<Action *>(c);
if (!a) {
qWarning() << "unregisterAction: id" << id.name()
<< "is registered with a different command type.";
return;
}
a->removeOverrideAction(action);
if (a->isEmpty()) {
// clean up
// ActionContainers listen to the commands' destroyed signals
m_mainWnd->removeAction(a->action());
delete a->action();
m_idCmdMap.remove(id);
delete a;
}
emit commandListChanged();
}
Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context, bool scriptable)
{
Shortcut *sc = 0;
if (CommandPrivate *c = m_idCmdMap.value(id, 0)) {
sc = qobject_cast<Shortcut *>(c);
if (!sc) {
qWarning() << "registerShortcut: id" << id.name()
<< "is registered with a different command type.";
return c;
}
} else {
sc = new Shortcut(id);
m_idCmdMap.insert(id, sc);
}
if (sc->shortcut()) {
qWarning() << "registerShortcut: action already registered, id" << id.name() << ".";
return sc;
}
if (!hasContext(context))
shortcut->setEnabled(false);
shortcut->setObjectName(id.toString());
shortcut->setParent(m_mainWnd);
sc->setShortcut(shortcut);
sc->setScriptable(scriptable);
if (context.isEmpty())
sc->setContext(Context(0));
else
sc->setContext(context);
emit commandListChanged();
emit commandAdded(id.toString());
if (isPresentationModeEnabled())
connect(sc->shortcut(), SIGNAL(activated()), this, SLOT(shortcutTriggered()));
return sc;
}
Command *ActionManagerPrivate::command(const Id &id) const
{
const IdCmdMap::const_iterator it = m_idCmdMap.constFind(id);
if (it == m_idCmdMap.constEnd()) {
if (warnAboutFindFailures)
qWarning() << "ActionManagerPrivate::command(): failed to find :"
<< id.name();
return 0;
}
return it.value();
}
ActionContainer *ActionManagerPrivate::actionContainer(const Id &id) const
{
const IdContainerMap::const_iterator it = m_idContainerMap.constFind(id);
if (it == m_idContainerMap.constEnd()) {
if (warnAboutFindFailures)
qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :"
<< id.name();
return 0;
}
return it.value();
}
static const char settingsGroup[] = "KeyBindings";
static const char idKey[] = "ID";
static const char sequenceKey[] = "Keysequence";
@@ -512,7 +576,7 @@ void ActionManagerPrivate::initialize()
const QKeySequence key(settings->value(QLatin1String(sequenceKey)).toString());
const Id id = Id(settings->value(QLatin1String(idKey)).toString());
Command *cmd = command(id);
Command *cmd = ActionManager::command(id);
if (cmd)
cmd->setKeySequence(key);
}
@@ -539,63 +603,3 @@ void ActionManagerPrivate::saveSettings(QSettings *settings)
settings->endArray();
}
void ActionManagerPrivate::unregisterShortcut(const Core::Id &id)
{
Shortcut *sc = 0;
CommandPrivate *c = m_idCmdMap.value(id, 0);
QTC_ASSERT(c, return);
sc = qobject_cast<Shortcut *>(c);
if (!sc) {
qWarning() << "unregisterShortcut: id" << id.name()
<< "is registered with a different command type.";
return;
}
delete sc->shortcut();
m_idCmdMap.remove(id);
delete sc;
emit commandListChanged();
}
void ActionManagerPrivate::setPresentationModeEnabled(bool enabled)
{
if (enabled == isPresentationModeEnabled())
return;
// Signal/slots to commands:
foreach (Command *c, commands()) {
if (c->action()) {
if (enabled)
connect(c->action(), SIGNAL(triggered()), this, SLOT(actionTriggered()));
else
disconnect(c->action(), SIGNAL(triggered()), this, SLOT(actionTriggered()));
}
if (c->shortcut()) {
if (enabled)
connect(c->shortcut(), SIGNAL(activated()), this, SLOT(shortcutTriggered()));
else
disconnect(c->shortcut(), SIGNAL(activated()), this, SLOT(shortcutTriggered()));
}
}
// The label for the shortcuts:
if (!m_presentationLabel) {
m_presentationLabel = new QLabel(0, Qt::ToolTip | Qt::WindowStaysOnTopHint);
QFont font = m_presentationLabel->font();
font.setPixelSize(45);
m_presentationLabel->setFont(font);
m_presentationLabel->setAlignment(Qt::AlignCenter);
m_presentationLabel->setMargin(5);
connect(&m_presentationLabelTimer, SIGNAL(timeout()), m_presentationLabel, SLOT(hide()));
} else {
m_presentationLabelTimer.stop();
delete m_presentationLabel;
m_presentationLabel = 0;
}
}
bool ActionManagerPrivate::isPresentationModeEnabled()
{
return m_presentationLabel;
}