1 /*
2 * iconaction.cpp - the QAction subclass that uses Icons and supports animation
3 * Copyright (C) 2003-2005 Michail Pishchagin
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "iconaction.h"
22 #include "icontoolbutton.h"
23
24 #include "iconwidget.h"
25
26 #ifndef WIDGET_PLUGIN
27 #include "iconset.h"
28 #else
29 class PsiIcon;
30 class Iconset;
31 #endif
32
33 #include "psioptions.h"
34
35 #include <QLayout>
36 #include <QMenu>
37 #include <QTimer>
38 #include <QChildEvent>
39
40 //----------------------------------------------------------------------------
41 // IconAction
42 //----------------------------------------------------------------------------
43
44 class IconAction::Private : public QObject
45 {
46 Q_OBJECT
47 public:
48 QList<IconToolButton *> buttons;
49 PsiIcon *icon;
50 #ifdef WIDGET_PLUGIN
51 QString iconName;
52 #endif
53 IconAction *action;
54
Private(IconAction * act,QObject * parent)55 Private(IconAction *act, QObject *parent) {
56 icon = 0;
57 action = act;
58 Q_ASSERT(action);
59
60 if (parent && parent->isWidgetType())
61 ((QWidget *)parent)->addAction(action);
62
63 #ifdef Q_OS_MAC
64 action->setIconVisibleInMenu(false);
65 #endif
66 }
67
~Private()68 ~Private()
69 {
70 #ifndef WIDGET_PLUGIN
71 if (icon) {
72 delete icon;
73 icon = 0;
74 }
75 #endif
76 }
77
init(const QString & objectName,const QString & statusTip,QKeySequence shortcut,bool checkable)78 void init(const QString &objectName, const QString &statusTip, QKeySequence shortcut, bool checkable) {
79 action->setObjectName(objectName);
80 action->setStatusTip(statusTip);
81 action->setShortcut(shortcut);
82 action->setCheckable(checkable);
83 }
84 };
85
IconAction(QObject * parent,const QString & name)86 IconAction::IconAction(QObject *parent, const QString &name)
87 : QAction(parent)
88 {
89 d = new Private(this, parent);
90 setObjectName(name);
91 }
92
IconAction(const QString & statusTip,const QString & icon,const QString & text,QKeySequence accel,QObject * parent,const QString & name,bool checkable)93 IconAction::IconAction(const QString &statusTip, const QString &icon, const QString &text, QKeySequence accel, QObject *parent, const QString &name, bool checkable)
94 : QAction(text, parent)
95 {
96 d = new Private(this, parent);
97 d->init(name, statusTip, accel, checkable);
98
99 setPsiIcon(icon);
100 }
101
IconAction(const QString & statusTip,const QString & icon,const QString & text,QList<QKeySequence> accel,QObject * parent,const QString & name,bool checkable)102 IconAction::IconAction(const QString &statusTip, const QString &icon, const QString &text, QList<QKeySequence> accel, QObject *parent, const QString &name, bool checkable)
103 : QAction(text, parent)
104 {
105 d = new Private(this, parent);
106 d->init(name, statusTip, QKeySequence(), checkable);
107 setShortcuts(accel);
108
109 setPsiIcon(icon);
110 }
111
IconAction(const QString & statusTip,const QString & text,QKeySequence accel,QObject * parent,const QString & name,bool checkable)112 IconAction::IconAction(const QString &statusTip, const QString &text, QKeySequence accel, QObject *parent, const QString &name, bool checkable)
113 : QAction(text, parent)
114 {
115 d = new Private(this, parent);
116 d->init(name, statusTip, accel, checkable);
117 }
118
IconAction(const QString & statusTip,const QString & text,QList<QKeySequence> accel,QObject * parent,const QString & name,bool checkable)119 IconAction::IconAction(const QString &statusTip, const QString &text, QList<QKeySequence> accel, QObject *parent, const QString &name, bool checkable)
120 : QAction(text, parent)
121 {
122 d = new Private(this, parent);
123 d->init(name, statusTip, QKeySequence(), checkable);
124 setShortcuts(accel);
125 }
126
IconAction(const QString & text,QObject * parent,const QString & icon)127 IconAction::IconAction(const QString &text, QObject *parent, const QString &icon)
128 : QAction(text, parent)
129 {
130 d = new Private(this, parent);
131 d->init(QString(), QString(), QKeySequence(), false);
132
133 setPsiIcon(icon);
134 }
135
~IconAction()136 IconAction::~IconAction()
137 {
138 // delete the buttons list before our own destruction
139 IconToolButton *button;
140 foreach ( button, d->buttons )
141 delete button;
142 d->buttons.clear();
143
144 delete d;
145 }
146
psiIcon() const147 const PsiIcon *IconAction::psiIcon() const
148 {
149 return d->icon;
150 }
151
setPsiIcon(const PsiIcon * i)152 void IconAction::setPsiIcon(const PsiIcon *i)
153 {
154 #ifdef WIDGET_PLUGIN
155 Q_UNUSED(i);
156 #else
157 if ( d->icon ) {
158 disconnect(d->icon, 0, this, 0 );
159 d->icon->stop();
160 delete d->icon;
161 d->icon = 0;
162 }
163
164 QIcon is;
165 if ( i ) {
166 d->icon = new PsiIcon(*i);
167 connect(d->icon, SIGNAL(iconModified()), SLOT(iconUpdated()));
168 //We newer use animated iconactions
169 //d->icon->activated(true);
170
171 is = d->icon->icon();
172 }
173
174 QAction::setIcon( is );
175
176 IconToolButton *btn;
177 foreach ( btn, d->buttons )
178 btn->setPsiIcon ( d->icon );
179 #endif
180 }
181
setPsiIcon(const QString & name)182 void IconAction::setPsiIcon(const QString &name)
183 {
184 #ifdef WIDGET_PLUGIN
185 d->iconName = name;
186 #else
187 if (name.isEmpty()) {
188 setPsiIcon( 0 );
189 return;
190 }
191 setPsiIcon( IconsetFactory::iconPtr(name) );
192 #endif
193 }
194
psiIconName() const195 QString IconAction::psiIconName() const
196 {
197 #ifndef WIDGET_PLUGIN
198 if ( d->icon )
199 return d->icon->name();
200 #else
201 return d->iconName;
202 #endif
203 return QString();
204 }
205
addTo(QWidget * w)206 bool IconAction::addTo(QWidget *w)
207 {
208 w->addAction(this);
209 return true;
210
211 QStringList supportedContainers;
212 supportedContainers << "QWidget";
213 if (w->inherits("QToolBar") ||
214 supportedContainers.contains(w->metaObject()->className()))
215 {
216 QString bname = objectName() + "_action_button";
217 IconToolButton *btn = new IconToolButton(w);
218 btn->setObjectName(bname);
219 d->buttons.append(btn);
220
221 btn->setDefaultAction(this);
222
223 btn->setText(text());
224 btn->setPsiIcon(d->icon, false);
225
226 btn->setDefaultAction(this);
227
228 // need to explicitly update popupMode,
229 // because setDefaultAction resets it
230 btn->setPopupMode(QToolButton::InstantPopup);
231
232 btn->setToolTip(toolTipFromMenuText());
233
234 btn->setAutoRaise(true);
235 btn->setFocusPolicy(Qt::NoFocus);
236
237 if (supportedContainers.contains(w->metaObject()->className()))
238 if (w->layout())
239 w->layout()->addWidget(btn);
240
241 connect(btn, SIGNAL(toggled(bool)), this, SLOT(setChecked(bool)));
242 connect(btn, SIGNAL(destroyed()), SLOT(objectDestroyed()));
243
244 addingToolButton(btn);
245 }
246 else
247 w->addAction(this);
248
249 return true;
250 }
251
objectDestroyed()252 void IconAction::objectDestroyed()
253 {
254 const QObject *obj = sender();
255 d->buttons.removeAll((IconToolButton *)obj);
256 }
257
setChecked(bool b)258 void IconAction::setChecked(bool b)
259 {
260 QAction::setChecked(b);
261 IconToolButton *btn;
262 foreach ( btn, d->buttons )
263 btn->setChecked(b);
264 }
265
toolButtonToggled(bool b)266 void IconAction::toolButtonToggled(bool b)
267 {
268 setChecked(b);
269 }
270
setEnabled(bool e)271 void IconAction::setEnabled(bool e)
272 {
273 QAction::setEnabled(e);
274 IconToolButton *btn;
275 foreach ( btn, d->buttons )
276 btn->setEnabled (e);
277 }
278
setText(const QString & t)279 void IconAction::setText(const QString &t)
280 {
281 QAction::setText(t);
282 IconToolButton *btn;
283 foreach ( btn, d->buttons )
284 btn->setText(t);
285 }
286
buttonList()287 QList<IconToolButton *> IconAction::buttonList()
288 {
289 return d->buttons;
290 }
291
iconUpdated()292 void IconAction::iconUpdated()
293 {
294 #ifndef WIDGET_PLUGIN
295 QAction::setIcon(d->icon ? d->icon->icon() : QIcon());
296 #endif
297 }
298
toolTipFromMenuText() const299 QString IconAction::toolTipFromMenuText() const
300 {
301 QString tt, str = text();
302 for (int i = 0; i < (int)str.length(); i++)
303 if ( str[i] == '&' && str[i+1] != '&' )
304 continue;
305 else
306 tt += str[i];
307
308 return tt;
309 }
310
setMenu(QMenu * p)311 void IconAction::setMenu( QMenu *p )
312 {
313 doSetMenu(p);
314 }
315
doSetMenu(QMenu * p)316 void IconAction::doSetMenu(QMenu* p)
317 {
318 QAction::setMenu(p);
319
320 IconToolButton* btn;
321 foreach(btn, d->buttons) {
322 btn->setMenu(0);
323
324 if (menu())
325 btn->setMenu(menu());
326 }
327 }
328
setIcon(const QIcon & ic)329 void IconAction::setIcon( const QIcon &ic )
330 {
331 QAction::setIcon( ic );
332
333 IconToolButton *btn;
334 foreach ( btn, d->buttons )
335 btn->setIcon( ic );
336 }
337
setVisible(bool b)338 void IconAction::setVisible( bool b )
339 {
340 QAction::setVisible( b );
341
342 IconToolButton *btn;
343 foreach ( btn, d->buttons ) {
344 if ( b )
345 btn->show();
346 else
347 btn->hide();
348 }
349 }
350
copy() const351 IconAction *IconAction::copy() const
352 {
353 IconAction *act = new IconAction(text(), psiIconName(), statusTip(), shortcut(), 0, objectName(), isCheckable());
354
355 *act = *this;
356
357 return act;
358 }
359
operator =(const IconAction & from)360 IconAction &IconAction::operator=( const IconAction &from )
361 {
362 setText( from.text() );
363 setPsiIcon( from.psiIconName() );
364 setStatusTip( from.statusTip() );
365 setShortcut( from.shortcut() );
366 setObjectName( from.objectName() );
367 setCheckable( from.isCheckable() );
368 setWhatsThis( whatsThis() );
369
370 // TODO: add more
371
372 return *this;
373 }
374
setParent(QObject * newParent)375 void IconAction::setParent(QObject *newParent)
376 {
377 QWidget *oldParent = qobject_cast<QWidget*>(parent());
378 if (oldParent) {
379 oldParent->removeAction(this);
380 }
381
382 QAction::setParent(newParent);
383 if (newParent && newParent->isWidgetType()) {
384 ((QWidget *)newParent)->addAction(this);
385 }
386 }
387
388 //----------------------------------------------------------------------------
389 // IconActionGroup
390 //----------------------------------------------------------------------------
391
392 class IconActionGroup::Private : public QObject
393 {
394 Q_OBJECT
395 public:
Private(IconActionGroup * _group)396 Private(IconActionGroup *_group) {
397 group = _group;
398 dirty = false;
399 }
400
401 IconActionGroup *group;
402
403 QMenu *popup;
404
405 bool exclusive;
406 bool usesDropDown;
407
408 bool dirty;
409
410 public slots:
411 void updatePopup();
412 };
413
updatePopup()414 void IconActionGroup::Private::updatePopup()
415 {
416 if (!dirty)
417 return;
418
419 if (!usesDropDown)
420 qWarning("IconActionGroup does not support !usesDropDown yet");
421
422 popup->clear();
423
424 QList<QAction *> list = group->findChildren<QAction *>();
425 foreach(QAction *action, list) {
426 if (!group->psiIcon() && action->inherits("IconAction"))
427 group->setIcon(((IconAction *)action)->icon());
428
429 popup->addAction(action);
430 }
431
432 group->setMenu(popup);
433 dirty = false;
434 }
435
IconActionGroup(QObject * parent,const char * name,bool exclusive)436 IconActionGroup::IconActionGroup(QObject *parent, const char *name, bool exclusive)
437 : IconAction( parent, name )
438 {
439 d = new Private(this);
440 d->popup = new QMenu();
441 d->dirty = true;
442 setUsesDropDown(true);
443 d->updatePopup();
444
445 d->exclusive = exclusive;
446
447 const QString css = PsiOptions::instance()->getOption("options.ui.contactlist.css").toString();
448 if (!css.isEmpty()) {
449 d->popup->setStyleSheet(css);
450 }
451 }
452
~IconActionGroup()453 IconActionGroup::~IconActionGroup()
454 {
455 delete d->popup;
456 delete d;
457 }
458
childEvent(QChildEvent * e)459 void IconActionGroup::childEvent(QChildEvent *e)
460 {
461 IconAction::childEvent(e);
462
463 d->dirty = true;
464 QTimer::singleShot( 0, d, SLOT( updatePopup() ) );
465 }
466
add(QAction *)467 void IconActionGroup::add( QAction * )
468 {
469 qWarning("IconActionGroup::add(): not implemented");
470 }
471
addSeparator()472 void IconActionGroup::addSeparator()
473 {
474 QAction *separatorAction = new QAction(this);
475 separatorAction->setObjectName("separator_action");
476 separatorAction->setSeparator(true);
477 }
478
addTo(QWidget * w)479 bool IconActionGroup::addTo( QWidget *w )
480 {
481 if ( w->inherits("Q3PopupMenu") || w->inherits("QMenu") ) {
482 QMenu *popup = (QMenu *)w;
483
484 QList<QAction *> list = findChildren<QAction *>();
485 QAction *action;
486 foreach ( action, list )
487 popup->addAction(action);
488
489 return true;
490 }
491
492 w->addAction(this);
493 return true;
494 }
495
copy() const496 IconAction *IconActionGroup::copy() const
497 {
498 qWarning("IconActionGroup::copy() doesn't work!");
499 return (IconAction *)this;
500 }
501
setExclusive(bool e)502 void IconActionGroup::setExclusive( bool e )
503 {
504 d->exclusive = e;
505 }
506
isExclusive() const507 bool IconActionGroup::isExclusive() const
508 {
509 return d->exclusive;
510 }
511
setUsesDropDown(bool u)512 void IconActionGroup::setUsesDropDown( bool u )
513 {
514 d->usesDropDown = u;
515 }
516
usesDropDown() const517 bool IconActionGroup::usesDropDown() const
518 {
519 return d->usesDropDown;
520 }
521
addingToolButton(IconToolButton * btn)522 void IconActionGroup::addingToolButton(IconToolButton *btn)
523 {
524 btn->setPopupMode( QToolButton::MenuButtonPopup );
525 }
526
popup()527 QMenu* IconActionGroup::popup()
528 {
529 return d->popup;
530 }
531
532 #include "iconaction.moc"
533