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 | |
49 | void 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 | |
60 | void KActionPrivate::setActiveGlobalShortcutNoEnable(const KShortcut &cut) |
61 | { |
62 | globalShortcut = cut; |
63 | emit q->globalShortcutChanged(cut.primary()); |
64 | } |
65 | |
66 | |
67 | void 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 | |
89 | void 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 | |
115 | bool 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 | |
139 | KAction::KAction(QObject *parent) |
140 | : QWidgetAction(parent), d(new KActionPrivate) |
141 | { |
142 | d->init(this); |
143 | } |
144 | |
145 | KAction::KAction(const QString &text, QObject *parent) |
146 | : QWidgetAction(parent), d(new KActionPrivate) |
147 | { |
148 | d->init(this); |
149 | setText(text); |
150 | } |
151 | |
152 | KAction::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 | |
160 | KAction::~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 | |
173 | bool KAction::isShortcutConfigurable() const |
174 | { |
175 | return property("isShortcutConfigurable" ).toBool(); |
176 | } |
177 | |
178 | void KAction::setShortcutConfigurable( bool b ) |
179 | { |
180 | setProperty("isShortcutConfigurable" , b); |
181 | } |
182 | |
183 | KShortcut 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 | |
198 | void 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 | |
212 | void 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 | |
224 | void KAction::setShortcuts(const QList<QKeySequence>& shortcuts, ShortcutTypes type) |
225 | { |
226 | setShortcut(KShortcut(shortcuts), type); |
227 | } |
228 | |
229 | const 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 | |
239 | void 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 |
290 | bool KAction::globalShortcutAllowed() const |
291 | { |
292 | return d->globalShortcutEnabled; |
293 | } |
294 | #endif |
295 | |
296 | bool KAction::isGlobalShortcutEnabled() const |
297 | { |
298 | return d->globalShortcutEnabled; |
299 | } |
300 | |
301 | #ifndef KDE_NO_DEPRECATED |
302 | void KAction::setGlobalShortcutAllowed( bool allowed, GlobalShortcutLoading /* load */ ) |
303 | { |
304 | if (allowed) { |
305 | //### no-op |
306 | } else { |
307 | forgetGlobalShortcut(); |
308 | } |
309 | } |
310 | #endif |
311 | |
312 | void 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 | |
323 | KShapeGesture 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 | |
332 | KRockerGesture 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 | |
341 | void 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 | |
359 | void 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 | |
377 | void KAction::setHelpText(const QString& text) |
378 | { |
379 | setStatusTip(text); |
380 | setToolTip(text); |
381 | if (whatsThis().isEmpty()) |
382 | setWhatsThis(text); |
383 | } |
384 | |
385 | KAuth::Action *KAction::authAction() const |
386 | { |
387 | return d->authAction; |
388 | } |
389 | |
390 | void 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 | |
400 | void 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 | |