Make ActionCollection load and save custom shortcuts
[quassel.git] / src / uisupport / actioncollection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************
20  * Parts of this implementation are based on KDE's KActionCollection.      *
21  ***************************************************************************/
22
23 #ifndef HAVE_KDE
24
25 #include <QAction>
26 #include <QDebug>
27
28 #include "actioncollection.h"
29
30 #include "action.h"
31 #include "uisettings.h"
32
33 ActionCollection::ActionCollection(QObject *parent) : QObject(parent) {
34   _connectTriggered = _connectHovered = false;
35 }
36
37 ActionCollection::~ActionCollection() {
38
39 }
40
41 void ActionCollection::clear() {
42   _actionByName.clear();
43   qDeleteAll(_actions);
44   _actions.clear();
45 }
46
47 QAction *ActionCollection::action(const QString &name) const {
48   return _actionByName.value(name, 0);
49 }
50
51 QList<QAction *> ActionCollection::actions() const {
52   return _actions;
53 }
54
55 Action *ActionCollection::addAction(const QString &name, Action *action) {
56   QAction *act = addAction(name, static_cast<QAction *>(action));
57   Q_UNUSED(act);
58   Q_ASSERT(act == action);
59   return action;
60 }
61
62 Action *ActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) {
63   Action *a = new Action(this);
64   if(receiver && member)
65     connect(a, SIGNAL(triggered(bool)), receiver, member);
66   return addAction(name, a);
67 }
68
69 QAction *ActionCollection::addAction(const QString &name, QAction *action) {
70   if(!action)
71     return action;
72
73   const QString origName = action->objectName();
74   QString indexName = name;
75
76   if(indexName.isEmpty())
77     indexName = action->objectName();
78   else
79     action->setObjectName(indexName);
80   if(indexName.isEmpty())
81     indexName = indexName.sprintf("unnamed-%p", (void *)action);
82
83   // do we already have this action?
84   if(_actionByName.value(indexName, 0) == action)
85     return action;
86   // or maybe another action under this name?
87   if(QAction *oldAction = _actionByName.value(indexName))
88     takeAction(oldAction);
89
90   // do we already have this action under a different name?
91   int oldIndex = _actions.indexOf(action);
92   if(oldIndex != -1) {
93     _actionByName.remove(origName);
94     _actions.removeAt(oldIndex);
95   }
96
97   // add action
98   _actionByName.insert(indexName, action);
99   _actions.append(action);
100
101   foreach(QWidget *widget, _associatedWidgets) {
102     widget->addAction(action);
103   }
104
105   connect(action, SIGNAL(destroyed(QObject *)), SLOT(actionDestroyed(QObject *)));
106   if(_connectHovered)
107     connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
108   if(_connectTriggered)
109     connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
110
111   emit inserted(action);
112   return action;
113 }
114
115 void ActionCollection::removeAction(QAction *action) {
116   delete takeAction(action);
117 }
118
119 QAction *ActionCollection::takeAction(QAction *action) {
120   if(!unlistAction(action))
121     return 0;
122
123   foreach(QWidget *widget, _associatedWidgets) {
124     widget->removeAction(action);
125   }
126
127   action->disconnect(this);
128   return action;
129 }
130
131 void ActionCollection::readSettings() {
132   ShortcutSettings s;
133   QStringList savedShortcuts = s.savedShortcuts();
134
135   foreach(const QString &name, _actionByName.keys()) {
136     if(!savedShortcuts.contains(name))
137       continue;
138     Action *action = qobject_cast<Action *>(_actionByName.value(name));
139     if(action)
140       action->setShortcut(s.loadShortcut(name), Action::ActiveShortcut);
141   }
142 }
143
144 void ActionCollection::writeSettings() const {
145   ShortcutSettings s;
146   foreach(const QString &name, _actionByName.keys()) {
147     Action *action = qobject_cast<Action *>(_actionByName.value(name));
148     if(!action)
149       continue;
150     if(!action->isShortcutConfigurable())
151       continue;
152     if(action->shortcut(Action::ActiveShortcut) == action->shortcut(Action::DefaultShortcut))
153       continue;
154     s.saveShortcut(name, action->shortcut(Action::ActiveShortcut));
155   }
156 }
157
158 void ActionCollection::slotActionTriggered() {
159   QAction *action = qobject_cast<QAction *>(sender());
160   if(action)
161     emit actionTriggered(action);
162 }
163
164 void ActionCollection::slotActionHovered() {
165   QAction *action = qobject_cast<QAction *>(sender());
166   if(action)
167     emit actionHovered(action);
168 }
169
170 void ActionCollection::actionDestroyed(QObject *obj) {
171   // remember that this is not an QAction anymore at this point
172   QAction *action = static_cast<QAction *>(obj);
173
174   unlistAction(action);
175 }
176
177 void ActionCollection::connectNotify(const char *signal) {
178   if(_connectHovered && _connectTriggered)
179     return;
180
181   if(QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal) {
182     if(!_connectHovered) {
183       _connectHovered = true;
184       foreach (QAction* action, actions())
185         connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
186     }
187   } else if(QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal) {
188     if(!_connectTriggered) {
189       _connectTriggered = true;
190       foreach (QAction* action, actions())
191         connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
192     }
193   }
194
195   QObject::connectNotify(signal);
196 }
197
198 void ActionCollection::associateWidget(QWidget *widget) const {
199   foreach(QAction *action, actions()) {
200     if(!widget->actions().contains(action))
201       widget->addAction(action);
202   }
203 }
204
205 void ActionCollection::addAssociatedWidget(QWidget *widget) {
206   if(!_associatedWidgets.contains(widget)) {
207     widget->addActions(actions());
208     _associatedWidgets.append(widget);
209     connect(widget, SIGNAL(destroyed(QObject *)), SLOT(associatedWidgetDestroyed(QObject *)));
210   }
211 }
212
213 void ActionCollection::removeAssociatedWidget(QWidget *widget) {
214   foreach(QAction *action, actions())
215     widget->removeAction(action);
216   _associatedWidgets.removeAll(widget);
217   disconnect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(associatedWidgetDestroyed(QObject *)));
218 }
219
220 QList<QWidget *> ActionCollection::associatedWidgets() const {
221   return _associatedWidgets;
222 }
223
224 void ActionCollection::clearAssociatedWidgets() {
225   foreach(QWidget *widget, _associatedWidgets)
226     foreach(QAction *action, actions())
227       widget->removeAction(action);
228
229   _associatedWidgets.clear();
230 }
231
232 void ActionCollection::associatedWidgetDestroyed(QObject *obj) {
233   _associatedWidgets.removeAll(static_cast<QWidget *>(obj));
234 }
235
236 bool ActionCollection::unlistAction(QAction *action) {
237   // This might be called with a partly destroyed QAction!
238
239   int index = _actions.indexOf(action);
240   if(index == -1) return false;
241
242   QString name = action->objectName();
243   _actionByName.remove(name);
244   _actions.removeAt(index);
245
246   // TODO: remove from ActionCategory if we ever get that
247
248   return true;
249 }
250
251 #endif /* HAVE_KDE */