320 lines
9.6 KiB
C++
320 lines
9.6 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2005-09 by the Quassel Project *
|
|
* devel@quassel-irc.org *
|
|
* *
|
|
* 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., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************
|
|
* Parts of this implementation are based on KDE's KActionCollection. *
|
|
***************************************************************************/
|
|
|
|
#include "actioncollection.h"
|
|
#include "action.h"
|
|
#include "icon.h"
|
|
#include <QCoreApplication>
|
|
#include <QSettings>
|
|
#include <QMetaMethod>
|
|
|
|
static const char *constProp="Category";
|
|
static ActionCollection *coll=nullptr;
|
|
static QWidget *mainWidget=nullptr;
|
|
void ActionCollection::setMainWidget(QWidget *w)
|
|
{
|
|
mainWidget=w;
|
|
}
|
|
|
|
ActionCollection * ActionCollection::get()
|
|
{
|
|
if (!coll) {
|
|
coll=new ActionCollection(mainWidget);
|
|
coll->setProperty(constProp, QCoreApplication::applicationName());
|
|
if (mainWidget) {
|
|
coll->addAssociatedWidget(mainWidget);
|
|
}
|
|
}
|
|
return coll;
|
|
}
|
|
|
|
Action * ActionCollection::createAction(const QString &name, const QString &text, const char *icon, const QString &whatsThis)
|
|
{
|
|
Action *act = static_cast<Action *>(addAction(name));
|
|
act->setText(text);
|
|
if (nullptr!=icon) {
|
|
act->setIcon(Icon::get(icon));
|
|
}
|
|
if (!whatsThis.isEmpty()) {
|
|
act->setWhatsThis(whatsThis);
|
|
}
|
|
Action::initIcon(act);
|
|
return act;
|
|
}
|
|
|
|
Action * ActionCollection::createAction(const QString &name, const QString &text, const QIcon &icon, const QString &whatsThis)
|
|
{
|
|
Action *act = static_cast<Action *>(addAction(name));
|
|
act->setText(text);
|
|
if (!icon.isNull()) {
|
|
act->setIcon(icon);
|
|
}
|
|
if (!whatsThis.isEmpty()) {
|
|
act->setWhatsThis(whatsThis);
|
|
}
|
|
Action::initIcon(act);
|
|
return act;
|
|
}
|
|
|
|
void ActionCollection::updateToolTips()
|
|
{
|
|
for (QAction *act: actions()) {
|
|
QString prev=act->toolTip();
|
|
Action::updateToolTip(act);
|
|
if (prev!=act->toolTip()) {
|
|
emit tooltipUpdated(act);
|
|
}
|
|
}
|
|
}
|
|
|
|
ActionCollection::ActionCollection(QObject *parent) : QObject(parent) {
|
|
_connectTriggered = _connectHovered = false;
|
|
}
|
|
|
|
ActionCollection::~ActionCollection() {
|
|
}
|
|
|
|
void ActionCollection::clear() {
|
|
_actionByName.clear();
|
|
qDeleteAll(_actions);
|
|
_actions.clear();
|
|
}
|
|
|
|
QAction *ActionCollection::action(const QString &name) const {
|
|
return _actionByName.value(name, nullptr);
|
|
}
|
|
|
|
QList<QAction *> ActionCollection::actions() const {
|
|
return _actions;
|
|
}
|
|
|
|
Action *ActionCollection::addAction(const QString &name, Action *action) {
|
|
QAction *act = addAction(name, static_cast<QAction *>(action));
|
|
Q_UNUSED(act)
|
|
Q_ASSERT(act == action);
|
|
return action;
|
|
}
|
|
|
|
Action *ActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) {
|
|
Action *a = new Action(this);
|
|
if(receiver && member)
|
|
connect(a, SIGNAL(triggered(bool)), receiver, member);
|
|
return addAction(name, a);
|
|
}
|
|
|
|
QAction *ActionCollection::addAction(const QString &name, QAction *action) {
|
|
if(!action)
|
|
return action;
|
|
|
|
const QString origName = action->objectName();
|
|
QString indexName = name;
|
|
|
|
if(indexName.isEmpty())
|
|
indexName = action->objectName();
|
|
else
|
|
action->setObjectName(indexName);
|
|
if(indexName.isEmpty())
|
|
indexName = QString::asprintf("unnamed-%p", (void *)action);
|
|
|
|
// do we already have this action?
|
|
if(_actionByName.value(indexName, nullptr) == action)
|
|
return action;
|
|
// or maybe another action under this name?
|
|
if(QAction *oldAction = _actionByName.value(indexName))
|
|
takeAction(oldAction);
|
|
|
|
// do we already have this action under a different name?
|
|
int oldIndex = _actions.indexOf(action);
|
|
if(oldIndex != -1) {
|
|
_actionByName.remove(origName);
|
|
_actions.removeAt(oldIndex);
|
|
}
|
|
|
|
// add action
|
|
_actionByName.insert(indexName, action);
|
|
_actions.append(action);
|
|
|
|
for (QWidget *widget: _associatedWidgets) {
|
|
widget->addAction(action);
|
|
}
|
|
|
|
connect(action, SIGNAL(destroyed(QObject *)), SLOT(actionDestroyed(QObject *)));
|
|
if(_connectHovered)
|
|
connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
|
|
if(_connectTriggered)
|
|
connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
|
|
|
|
emit inserted(action);
|
|
return action;
|
|
}
|
|
|
|
void ActionCollection::removeAction(QAction *action) {
|
|
delete takeAction(action);
|
|
}
|
|
|
|
QAction *ActionCollection::takeAction(QAction *action) {
|
|
if(!unlistAction(action))
|
|
return nullptr;
|
|
|
|
for (QWidget *widget: _associatedWidgets) {
|
|
widget->removeAction(action);
|
|
}
|
|
|
|
action->disconnect(this);
|
|
return action;
|
|
}
|
|
|
|
QString ActionCollection::configKey() const
|
|
{
|
|
QVariant v=property(constProp);
|
|
if (v.isValid() && !v.toString().isEmpty()) {
|
|
return QLatin1String("Shortcuts-")+v.toString();
|
|
}
|
|
return QLatin1String("Shortcuts");
|
|
}
|
|
|
|
void ActionCollection::readSettings() {
|
|
QSettings s;
|
|
s.beginGroup(configKey());
|
|
QStringList savedShortcuts = s.childKeys();
|
|
|
|
for (const QString &name: _actionByName.keys()) {
|
|
if(!savedShortcuts.contains(name))
|
|
continue;
|
|
Action *action = qobject_cast<Action *>(_actionByName.value(name));
|
|
if(action)
|
|
action->setShortcut(s.value(name, QKeySequence()).value<QKeySequence>(), Action::ActiveShortcut);
|
|
}
|
|
}
|
|
|
|
void ActionCollection::writeSettings() const {
|
|
QSettings s;
|
|
s.beginGroup(configKey());
|
|
for (const QString &name: _actionByName.keys()) {
|
|
Action *action = qobject_cast<Action *>(_actionByName.value(name));
|
|
if(!action)
|
|
continue;
|
|
if(!action->isShortcutConfigurable())
|
|
continue;
|
|
if(action->shortcut(Action::ActiveShortcut) == action->shortcut(Action::DefaultShortcut)) {
|
|
s.remove(name);
|
|
continue;
|
|
}
|
|
s.setValue(name, action->shortcut(Action::ActiveShortcut));
|
|
}
|
|
}
|
|
|
|
void ActionCollection::slotActionTriggered() {
|
|
QAction *action = qobject_cast<QAction *>(sender());
|
|
if(action)
|
|
emit actionTriggered(action);
|
|
}
|
|
|
|
void ActionCollection::slotActionHovered() {
|
|
QAction *action = qobject_cast<QAction *>(sender());
|
|
if(action)
|
|
emit actionHovered(action);
|
|
}
|
|
|
|
void ActionCollection::actionDestroyed(QObject *obj) {
|
|
// remember that this is not an QAction anymore at this point
|
|
QAction *action = static_cast<QAction *>(obj);
|
|
|
|
unlistAction(action);
|
|
}
|
|
|
|
void ActionCollection::connectNotify(const QMetaMethod &signal) {
|
|
if(_connectHovered && _connectTriggered)
|
|
return;
|
|
|
|
if(QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal.methodSignature()) {
|
|
if(!_connectHovered) {
|
|
_connectHovered = true;
|
|
for (QAction* action: actions())
|
|
connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
|
|
}
|
|
} else if(QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal.methodSignature()) {
|
|
if(!_connectTriggered) {
|
|
_connectTriggered = true;
|
|
for (QAction* action: actions())
|
|
connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
|
|
}
|
|
}
|
|
|
|
QObject::connectNotify(signal);
|
|
}
|
|
|
|
void ActionCollection::associateWidget(QWidget *widget) const {
|
|
for (QAction *action: actions()) {
|
|
if(!widget->actions().contains(action))
|
|
widget->addAction(action);
|
|
}
|
|
}
|
|
|
|
void ActionCollection::addAssociatedWidget(QWidget *widget) {
|
|
if(!_associatedWidgets.contains(widget)) {
|
|
widget->addActions(actions());
|
|
_associatedWidgets.append(widget);
|
|
connect(widget, SIGNAL(destroyed(QObject *)), SLOT(associatedWidgetDestroyed(QObject *)));
|
|
}
|
|
}
|
|
|
|
void ActionCollection::removeAssociatedWidget(QWidget *widget) {
|
|
for (QAction *action: actions())
|
|
widget->removeAction(action);
|
|
_associatedWidgets.removeAll(widget);
|
|
disconnect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(associatedWidgetDestroyed(QObject *)));
|
|
}
|
|
|
|
QList<QWidget *> ActionCollection::associatedWidgets() const {
|
|
return _associatedWidgets;
|
|
}
|
|
|
|
void ActionCollection::clearAssociatedWidgets() {
|
|
for (QWidget *widget: _associatedWidgets)
|
|
for (QAction *action: actions())
|
|
widget->removeAction(action);
|
|
|
|
_associatedWidgets.clear();
|
|
}
|
|
|
|
void ActionCollection::associatedWidgetDestroyed(QObject *obj) {
|
|
_associatedWidgets.removeAll(static_cast<QWidget *>(obj));
|
|
}
|
|
|
|
bool ActionCollection::unlistAction(QAction *action) {
|
|
// This might be called with a partly destroyed QAction!
|
|
|
|
int index = _actions.indexOf(action);
|
|
if(index == -1) return false;
|
|
|
|
QString name = action->objectName();
|
|
_actionByName.remove(name);
|
|
_actions.removeAt(index);
|
|
|
|
// TODO: remove from ActionCategory if we ever get that
|
|
|
|
return true;
|
|
}
|
|
|
|
#include "moc_actioncollection.cpp"
|