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