1/* This file is part of the KDE libraries
2 Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org>
3 (C) 1999 Simon Hausmann <hausmann@kde.org>
4 (C) 2000 Nicolas Hadacek <haadcek@kde.org>
5 (C) 2000 Kurt Granroth <granroth@kde.org>
6 (C) 2000 Michael Koch <koch@kde.org>
7 (C) 2001 Holger Freyther <freyther@kde.org>
8 (C) 2002 Ellis Whitehead <ellis@kde.org>
9 (C) 2002 Joseph Wenninger <jowenn@kde.org>
10 (C) 2005-2006 Hamish Rodda <rodda@kde.org>
11
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Library General Public
14 License version 2 as published by the Free Software Foundation.
15
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Library General Public License for more details.
20
21 You should have received a copy of the GNU Library General Public License
22 along with this library; see the file COPYING.LIB. If not, write to
23 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 Boston, MA 02110-1301, USA.
25*/
26
27#include "kaction.h"
28#include "kaction_p.h"
29#include "kglobalaccel_p.h"
30#include "klocale.h"
31#include "kmessagebox.h"
32#include "kauthaction.h"
33#include "kauthactionwatcher.h"
34
35#include <QtGui/QApplication>
36#include <QtGui/QHBoxLayout>
37#include <QtGui/QShortcutEvent>
38#include <QtGui/QToolBar>
39
40#include <kdebug.h>
41
42#include "kguiitem.h"
43#include "kicon.h"
44
45//---------------------------------------------------------------------
46// KActionPrivate
47//---------------------------------------------------------------------
48
49void KActionPrivate::init(KAction *q_ptr)
50{
51 q = q_ptr;
52 globalShortcutEnabled = false;
53 neverSetGlobalShortcut = true;
54
55 QObject::connect(q, SIGNAL(triggered(bool)), q, SLOT(slotTriggered()));
56
57 q->setProperty("isShortcutConfigurable", true);
58}
59
60void KActionPrivate::setActiveGlobalShortcutNoEnable(const KShortcut &cut)
61{
62 globalShortcut = cut;
63 emit q->globalShortcutChanged(cut.primary());
64}
65
66
67void KActionPrivate::slotTriggered()
68{
69#ifdef KDE3_SUPPORT
70 emit q->activated();
71#endif
72 emit q->triggered(QApplication::mouseButtons(), QApplication::keyboardModifiers());
73
74 if (authAction) {
75 KAuth::Action::AuthStatus s = authAction->earlyAuthorize();
76 switch(s) {
77 case KAuth::Action::Denied:
78 q->setEnabled(false);
79 break;
80 case KAuth::Action::Authorized:
81 emit q->authorized(authAction);
82 break;
83 default:
84 break;
85 }
86 }
87}
88
89void KActionPrivate::authStatusChanged(int status)
90{
91 KAuth::Action::AuthStatus s = (KAuth::Action::AuthStatus)status;
92
93 switch(s) {
94 case KAuth::Action::Authorized:
95 q->setEnabled(true);
96 if(!oldIcon.isNull()) {
97 q->setIcon(oldIcon);
98 oldIcon = KIcon();
99 }
100 break;
101 case KAuth::Action::AuthRequired:
102 q->setEnabled(true);
103 oldIcon = KIcon(q->icon());
104 q->setIcon(KIcon("dialog-password"));
105 break;
106 default:
107 q->setEnabled(false);
108 if(!oldIcon.isNull()) {
109 q->setIcon(oldIcon);
110 oldIcon = KIcon();
111 }
112 }
113}
114
115bool KAction::event(QEvent *event)
116{
117 if (event->type() == QEvent::Shortcut) {
118 QShortcutEvent *se = static_cast<QShortcutEvent*>(event);
119 if(se->isAmbiguous()) {
120 KMessageBox::information(
121 NULL, // No widget to be seen around here
122 i18n( "The key sequence '%1' is ambiguous. Use 'Configure Shortcuts'\n"
123 "from the 'Settings' menu to solve the ambiguity.\n"
124 "No action will be triggered.",
125 se->key().toString(QKeySequence::NativeText)),
126 i18n("Ambiguous shortcut detected"));
127 return true;
128 }
129 }
130
131 return QAction::event(event);
132}
133
134
135//---------------------------------------------------------------------
136// KAction
137//---------------------------------------------------------------------
138
139KAction::KAction(QObject *parent)
140 : QWidgetAction(parent), d(new KActionPrivate)
141{
142 d->init(this);
143}
144
145KAction::KAction(const QString &text, QObject *parent)
146 : QWidgetAction(parent), d(new KActionPrivate)
147{
148 d->init(this);
149 setText(text);
150}
151
152KAction::KAction(const KIcon &icon, const QString &text, QObject *parent)
153 : QWidgetAction(parent), d(new KActionPrivate)
154{
155 d->init(this);
156 setIcon(icon);
157 setText(text);
158}
159
160KAction::~KAction()
161{
162 if (d->globalShortcutEnabled) {
163 // - remove the action from KGlobalAccel
164 d->globalShortcutEnabled = false;
165 KGlobalAccel::self()->d->remove(this, KGlobalAccelPrivate::SetInactive);
166 }
167
168 KGestureMap::self()->removeGesture(d->shapeGesture, this);
169 KGestureMap::self()->removeGesture(d->rockerGesture, this);
170 delete d;
171}
172
173bool KAction::isShortcutConfigurable() const
174{
175 return property("isShortcutConfigurable").toBool();
176}
177
178void KAction::setShortcutConfigurable( bool b )
179{
180 setProperty("isShortcutConfigurable", b);
181}
182
183KShortcut KAction::shortcut(ShortcutTypes type) const
184{
185 Q_ASSERT(type);
186
187 if (type == DefaultShortcut) {
188 QKeySequence primary = property("defaultPrimaryShortcut").value<QKeySequence>();
189 QKeySequence secondary = property("defaultAlternateShortcut").value<QKeySequence>();
190 return KShortcut(primary, secondary);
191 }
192
193 QKeySequence primary = shortcuts().value(0);
194 QKeySequence secondary = shortcuts().value(1);
195 return KShortcut(primary, secondary);
196}
197
198void KAction::setShortcut( const KShortcut & shortcut, ShortcutTypes type )
199{
200 Q_ASSERT(type);
201
202 if (type & DefaultShortcut) {
203 setProperty("defaultPrimaryShortcut", shortcut.primary());
204 setProperty("defaultAlternateShortcut", shortcut.alternate());
205 }
206
207 if (type & ActiveShortcut) {
208 QAction::setShortcuts(shortcut);
209 }
210}
211
212void KAction::setShortcut( const QKeySequence & keySeq, ShortcutTypes type )
213{
214 Q_ASSERT(type);
215
216 if (type & DefaultShortcut)
217 setProperty("defaultPrimaryShortcut", keySeq);
218
219 if (type & ActiveShortcut) {
220 QAction::setShortcut(keySeq);
221 }
222}
223
224void KAction::setShortcuts(const QList<QKeySequence>& shortcuts, ShortcutTypes type)
225{
226 setShortcut(KShortcut(shortcuts), type);
227}
228
229const KShortcut & KAction::globalShortcut(ShortcutTypes type) const
230{
231 Q_ASSERT(type);
232
233 if (type == DefaultShortcut)
234 return d->defaultGlobalShortcut;
235
236 return d->globalShortcut;
237}
238
239void KAction::setGlobalShortcut( const KShortcut & shortcut, ShortcutTypes type,
240 GlobalShortcutLoading load )
241{
242 Q_ASSERT(type);
243 bool changed = false;
244
245 // protect against garbage keycode -1 that Qt sometimes produces for exotic keys;
246 // at the moment (~mid 2008) Multimedia PlayPause is one of those keys.
247 int shortcutKeys[8];
248 for (int i = 0; i < 4; i++) {
249 shortcutKeys[i] = shortcut.primary()[i];
250 shortcutKeys[i + 4] = shortcut.alternate()[i];
251 }
252 for (int i = 0; i < 8; i++) {
253 if (shortcutKeys[i] == -1) {
254 kWarning(283) << "Encountered garbage keycode (keycode = -1) in input, not doing anything.";
255 return;
256 }
257 }
258
259 if (!d->globalShortcutEnabled) {
260 changed = true;
261 if (objectName().isEmpty() || objectName().startsWith(QLatin1String("unnamed-"))) {
262 kWarning(283) << "Attempt to set global shortcut for action without objectName()."
263 " Read the setGlobalShortcut() documentation.";
264 return;
265 }
266 d->globalShortcutEnabled = true;
267 KGlobalAccel::self()->d->doRegister(this);
268 }
269
270 if ((type & DefaultShortcut) && d->defaultGlobalShortcut != shortcut) {
271 d->defaultGlobalShortcut = shortcut;
272 changed = true;
273 }
274
275 if ((type & ActiveShortcut) && d->globalShortcut != shortcut) {
276 d->globalShortcut = shortcut;
277 changed = true;
278 }
279
280 //We want to have updateGlobalShortcuts called on a new action in any case so that
281 //it will be registered properly. In the case of the first setShortcut() call getting an
282 //empty shortcut parameter this would not happen...
283 if (changed || d->neverSetGlobalShortcut) {
284 KGlobalAccel::self()->d->updateGlobalShortcut(this, type | load);
285 d->neverSetGlobalShortcut = false;
286 }
287}
288
289#ifndef KDE_NO_DEPRECATED
290bool KAction::globalShortcutAllowed() const
291{
292 return d->globalShortcutEnabled;
293}
294#endif
295
296bool KAction::isGlobalShortcutEnabled() const
297{
298 return d->globalShortcutEnabled;
299}
300
301#ifndef KDE_NO_DEPRECATED
302void KAction::setGlobalShortcutAllowed( bool allowed, GlobalShortcutLoading /* load */ )
303{
304 if (allowed) {
305 //### no-op
306 } else {
307 forgetGlobalShortcut();
308 }
309}
310#endif
311
312void KAction::forgetGlobalShortcut()
313{
314 d->globalShortcut = KShortcut();
315 d->defaultGlobalShortcut = KShortcut();
316 if (d->globalShortcutEnabled) {
317 d->globalShortcutEnabled = false;
318 d->neverSetGlobalShortcut = true; //it's a fresh start :)
319 KGlobalAccel::self()->d->remove(this, KGlobalAccelPrivate::UnRegister);
320 }
321}
322
323KShapeGesture KAction::shapeGesture( ShortcutTypes type ) const
324{
325 Q_ASSERT(type);
326 if ( type & DefaultShortcut )
327 return d->defaultShapeGesture;
328
329 return d->shapeGesture;
330}
331
332KRockerGesture KAction::rockerGesture( ShortcutTypes type ) const
333{
334 Q_ASSERT(type);
335 if ( type & DefaultShortcut )
336 return d->defaultRockerGesture;
337
338 return d->rockerGesture;
339}
340
341void KAction::setShapeGesture( const KShapeGesture& gest, ShortcutTypes type )
342{
343 Q_ASSERT(type);
344
345 if( type & DefaultShortcut )
346 d->defaultShapeGesture = gest;
347
348 if ( type & ActiveShortcut ) {
349 if ( KGestureMap::self()->findAction( gest ) ) {
350 kDebug(283) << "New mouse gesture already in use, won't change gesture.";
351 return;
352 }
353 KGestureMap::self()->removeGesture( d->shapeGesture, this );
354 KGestureMap::self()->addGesture( gest, this );
355 d->shapeGesture = gest;
356 }
357}
358
359void KAction::setRockerGesture( const KRockerGesture& gest, ShortcutTypes type )
360{
361 Q_ASSERT(type);
362
363 if( type & DefaultShortcut )
364 d->defaultRockerGesture = gest;
365
366 if ( type & ActiveShortcut ) {
367 if ( KGestureMap::self()->findAction( gest ) ) {
368 kDebug(283) << "New mouse gesture already in use, won't change gesture.";
369 return;
370 }
371 KGestureMap::self()->removeGesture( d->rockerGesture, this );
372 KGestureMap::self()->addGesture( gest, this );
373 d->rockerGesture = gest;
374 }
375}
376
377void KAction::setHelpText(const QString& text)
378{
379 setStatusTip(text);
380 setToolTip(text);
381 if (whatsThis().isEmpty())
382 setWhatsThis(text);
383}
384
385KAuth::Action *KAction::authAction() const
386{
387 return d->authAction;
388}
389
390void KAction::setAuthAction(const QString &actionName)
391{
392 if (actionName.isEmpty()) {
393 setAuthAction(0);
394 } else {
395 setAuthAction(new KAuth::Action(actionName));
396 // this memory leak is gone in frameworks 5
397 }
398}
399
400void KAction::setAuthAction(KAuth::Action *action)
401{
402 if (d->authAction == action) {
403 return;
404 }
405
406 if (d->authAction) {
407 disconnect(d->authAction->watcher(), SIGNAL(statusChanged(int)),
408 this, SLOT(authStatusChanged(int)));
409 // d->authAction can not be deleted because it could
410 // be any kind of pointer, including a pointer to a stack object.
411 d->authAction = 0;
412 if (!d->oldIcon.isNull()) {
413 setIcon(d->oldIcon);
414 d->oldIcon = KIcon();
415 }
416 }
417
418 if (action != 0) {
419 d->authAction = action;
420
421 // Set the parent widget
422 d->authAction->setParentWidget(parentWidget());
423
424 connect(d->authAction->watcher(), SIGNAL(statusChanged(int)),
425 this, SLOT(authStatusChanged(int)));
426 d->authStatusChanged(d->authAction->status());
427 }
428}
429
430/* vim: et sw=2 ts=2
431 */
432
433#include "kaction.moc"
434