source: psi/trunk/src/mainwin_p.cpp@ 16

Last change on this file since 16 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 18.2 KB
Line 
1/*
2 * mainwin_p.cpp - classes used privately by the main window.
3 * Copyright (C) 2001-2003 Justin Karneges, Michail Pishchagin
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (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 library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include "common.h"
22#include "mainwin_p.h"
23
24#include <qapplication.h>
25#include <qstyle.h>
26#include <qtoolbar.h>
27#include <qtimer.h>
28#include <qsignalmapper.h>
29#include <qobjectlist.h>
30#include <qpixmapcache.h>
31#include "psiaccount.h"
32#include "iconwidget.h"
33#include "alerticon.h"
34
35//----------------------------------------------------------------------------
36// PopupActionButton
37//----------------------------------------------------------------------------
38
39class PopupActionButton : public QPushButton
40{
41 Q_OBJECT
42public:
43 PopupActionButton(QWidget *parent = 0, const char *name = 0);
44 ~PopupActionButton();
45
46 void setIcon(Icon *, bool showText);
47
48private slots:
49 void pixmapUpdated(const QPixmap &);
50
51private:
52 void drawButtonLabel(QPainter *p);
53 bool hasToolTip;
54 Icon *icon;
55 bool showText;
56};
57
58PopupActionButton::PopupActionButton(QWidget *parent, const char *name)
59: QPushButton(parent, name)
60{
61 setWFlags(getWFlags() | WRepaintNoErase);
62
63 hasToolTip = false;
64 icon = 0;
65}
66
67PopupActionButton::~PopupActionButton()
68{
69 if (icon)
70 icon->stop();
71}
72
73void PopupActionButton::setIcon(Icon *i, bool st)
74{
75 if ( icon ) {
76 icon->stop();
77 disconnect (icon, 0, this, 0);
78 icon = 0;
79 }
80
81 icon = i;
82 showText = st;
83
84 if ( icon ) {
85 pixmapUpdated(icon->pixmap());
86
87 connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(pixmapUpdated(const QPixmap &)));
88 icon->activated();
89 }
90}
91
92void PopupActionButton::pixmapUpdated(const QPixmap &pix)
93{
94 if ( showText )
95 setIconSet(pix);
96 else
97 setPixmap(pix);
98}
99
100void PopupActionButton::drawButtonLabel(QPainter *p)
101{
102 // code by Justin Karneges :-)
103 // crazy code ahead! watch out for potholes and deer.
104
105 // this gets us the width of the "text area" on the button.
106 // adapted from qt/src/styles/qcommonstyle.cpp and qt/src/widgets/qpushbutton.cpp
107 QRect r = style().subRect(QStyle::SR_PushButtonContents, this);
108 if(isMenuButton())
109 r.setWidth(r.width() - style().pixelMetric(QStyle::PM_MenuButtonIndicator, this));
110 if(iconSet() && !iconSet()->isNull())
111 r.setWidth(r.width() - (iconSet()->pixmap(QIconSet::Small, QIconSet::Normal, QIconSet::Off).width() + 4));
112
113 // font metrics
114 QFontMetrics fm(font());
115
116 // w1 = width of button text, w2 = width of text area
117 int w1 = fm.width(text());
118 int w2 = r.width();
119
120 // backup original text
121 QString oldtext = text();
122
123 // button text larger than what will fit?
124 if(w1 > w2) {
125 if( !hasToolTip ) {
126 QToolTip::add(this, text());
127 hasToolTip = TRUE;
128 }
129
130 // make a string that fits
131 bool found = FALSE;
132 QString newtext;
133 int n;
134 for(n = oldtext.length(); n > 0; --n) {
135 if(fm.width(oldtext, n) < w2) {
136 found = TRUE;
137 break;
138 }
139 }
140 if(found)
141 newtext = oldtext.mid(0, n);
142 else
143 newtext = "";
144
145 // set the new text that fits. updates must be off, or we recurse.
146 setUpdatesEnabled(FALSE);
147 QButton::setText(newtext);
148 setUpdatesEnabled(TRUE);
149 }
150 else {
151 if( hasToolTip ) {
152 QToolTip::remove(this);
153 hasToolTip = FALSE;
154 }
155 }
156
157 // draw!
158 QPushButton::drawButtonLabel(p);
159
160 // restore original button text now that we are done drawing.
161 setUpdatesEnabled(FALSE);
162 QButton::setText(oldtext);
163 setUpdatesEnabled(TRUE);
164}
165
166//----------------------------------------------------------------------------
167// PopupAction -- the IconButton with popup or QPopupMenu
168//----------------------------------------------------------------------------
169
170class PopupAction::Private : public QObject
171{
172public:
173 QSizePolicy size;
174 QPtrList<PopupActionButton> buttons;
175 Icon *icon;
176 bool showText;
177
178 Private (QObject *parent)
179 : QObject (parent)
180 {
181 icon = 0;
182 showText = true;
183 }
184
185 ~Private()
186 {
187 buttons.clear();
188 if (icon)
189 delete icon;
190 }
191};
192
193PopupAction::PopupAction (const QString &label, QPopupMenu *_menu, QObject *parent, const char *name)
194: IconAction (label, label, 0, parent, name)
195{
196 d = new Private (this);
197 setPopup( _menu );
198}
199
200void PopupAction::setSizePolicy (const QSizePolicy &p)
201{
202 d->size = p;
203}
204
205void PopupAction::setAlert (const Icon *icon)
206{
207 setIcon(icon, d->showText, true);
208}
209
210void PopupAction::setIcon (const Icon *icon, bool showText, bool alert)
211{
212 Icon *oldIcon = 0;
213 if ( d->icon ) {
214 oldIcon = d->icon;
215 d->icon = 0;
216 }
217
218 d->showText = showText;
219
220 if ( icon ) {
221 if ( !alert )
222 d->icon = new Icon(*icon);
223 else
224 d->icon = new AlertIcon(icon);
225
226 IconAction::setIconSet(*icon);
227 }
228 else {
229 d->icon = 0;
230 IconAction::setIconSet(QIconSet());
231 }
232
233 for ( QPtrListIterator<PopupActionButton> it(d->buttons); it.current(); ++it ) {
234 PopupActionButton *btn = it.current();
235 btn->setIcon (d->icon, showText);
236 }
237
238 if ( oldIcon ) {
239 delete oldIcon;
240 }
241}
242
243void PopupAction::setText (const QString &text)
244{
245 IconAction::setText (text);
246 for ( QPtrListIterator<PopupActionButton> it(d->buttons); it.current(); ++it ) {
247 PopupActionButton *btn = it.current();
248 btn->setText (text);
249 }
250}
251
252bool PopupAction::addTo (QWidget *w)
253{
254 if ( w->inherits("QPopupMenu") ) {
255 QPopupMenu *m = (QPopupMenu*)w;
256#ifndef Q_WS_MAC
257 m->insertItem (iconSet(), menuText(), popup());
258#else
259 m->insertItem (QIconSet(), menuText(), popup());
260#endif
261 }
262 else if ( w->inherits("QToolBar") ) {
263 QCString bname = name() + QCString("_action_button");
264 PopupActionButton *btn = new PopupActionButton ( (QToolBar*)w, bname );
265 d->buttons.append ( btn );
266 btn->setPopup ( popup() );
267 btn->setText ( text() );
268 btn->setIcon ( d->icon, d->showText );
269 btn->setSizePolicy ( d->size );
270 btn->setEnabled ( isEnabled() );
271
272 connect( btn, SIGNAL( destroyed() ), SLOT( objectDestroyed() ) );
273 }
274 else
275 return false;
276
277 return true;
278}
279
280void PopupAction::objectDestroyed ()
281{
282 const QObject *obj = sender();
283 d->buttons.removeRef( (PopupActionButton *) obj );
284}
285
286void PopupAction::setEnabled (bool e)
287{
288 IconAction::setEnabled (e);
289 for ( QPtrListIterator<PopupActionButton> it(d->buttons); it.current(); ++it ) {
290 PopupActionButton *btn = it.current();
291 btn->setEnabled (e);
292 }
293}
294
295IconAction *PopupAction::copy() const
296{
297 PopupAction *act = new PopupAction(text(), popup(), 0, name());
298
299 *act = *this;
300
301 return act;
302}
303
304PopupAction &PopupAction::operator=( const PopupAction &from )
305{
306 *( (IconAction *)this ) = from;
307
308 d->size = from.d->size;
309 setIcon( from.d->icon );
310 d->showText = from.d->showText;
311
312 return *this;
313}
314
315//----------------------------------------------------------------------------
316// MLabel -- a clickable label
317//----------------------------------------------------------------------------
318
319MLabel::MLabel(QWidget *parent, const char *name)
320:QLabel(parent, name)
321{
322 setMinimumWidth(48);
323 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
324 setFrameStyle(QFrame::Panel | QFrame::Sunken);
325}
326
327void MLabel::mouseReleaseEvent(QMouseEvent *e)
328{
329 emit clicked(e->button());
330 e->ignore();
331}
332
333void MLabel::mouseDoubleClickEvent(QMouseEvent *e)
334{
335 if(e->button() == LeftButton)
336 emit doubleClicked();
337
338 e->ignore();
339}
340
341//----------------------------------------------------------------------------
342// MTray
343//----------------------------------------------------------------------------
344
345class MTray::Private
346{
347public:
348 Private() {
349 icon = 0;
350 ti = 0;
351 }
352
353 ~Private() {
354 if ( icon )
355 delete icon;
356
357 delete ti;
358 }
359
360 TrayIcon *ti;
361 Icon *icon;
362
363 QPixmap makeIcon();
364 QRgb pixelBlend(QRgb p1, QRgb p2);
365};
366
367MTray::MTray(const QString &tip, QPopupMenu *popup, QObject *parent)
368:QObject(parent)
369{
370 d = new Private;
371
372 d->ti = new TrayIcon(d->makeIcon(), tip, popup);
373 d->ti->setWMDock(option.isWMDock);
374 connect(d->ti, SIGNAL(clicked(const QPoint &, int)), SIGNAL(clicked(const QPoint &, int)));
375 connect(d->ti, SIGNAL(doubleClicked(const QPoint &)), SIGNAL(doubleClicked(const QPoint &)));
376 connect(d->ti, SIGNAL(closed()), SIGNAL(closed()));
377 connect(qApp, SIGNAL(newTrayOwner()), d->ti, SLOT(newTrayOwner()));
378 connect(qApp, SIGNAL(trayOwnerDied()), d->ti, SLOT(hide()));
379 d->ti->show();
380}
381
382MTray::~MTray()
383{
384 delete d;
385}
386
387void MTray::setToolTip(const QString &str)
388{
389 d->ti->setToolTip(str);
390}
391
392void MTray::setIcon(const Icon *icon, bool alert)
393{
394 if ( d->icon ) {
395 disconnect(d->icon, 0, this, 0 );
396 d->icon->stop();
397
398 delete d->icon;
399 d->icon = 0;
400 }
401
402 if ( icon ) {
403 if ( !alert )
404 d->icon = new Icon(*icon);
405 else
406 d->icon = new AlertIcon(icon);
407
408 connect(d->icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(animate()));
409 d->icon->activated();
410 }
411 else
412 d->icon = new Icon();
413
414 animate();
415}
416
417void MTray::setAlert(const Icon *icon)
418{
419 setIcon(icon, true);
420}
421
422bool MTray::isAnimating() const
423{
424 return d->icon->isAnimated();
425}
426
427bool MTray::isWMDock()
428{
429 return d->ti->isWMDock();
430}
431
432void MTray::show()
433{
434 d->ti->show();
435}
436
437void MTray::hide()
438{
439 d->ti->hide();
440}
441
442
443// a function to blend 2 pixels taking their alpha channels
444// into consideration
445// p1 is in the 1st layer, p2 is in the 2nd layer (over p1)
446QRgb MTray::Private::pixelBlend(QRgb p1, QRgb p2)
447{
448 int a2 = qAlpha(p2);
449 if (a2 == 255) return p2; // don't calculate anything if p2 is completely opaque
450 int a1 = qAlpha(p1);
451 double prop1 = double(a1*(255-a2))/double(255*255);
452 double prop2 = double(a2)/255.0;
453 int r = int( qRed(p1)*prop1 + qRed(p2)*prop2 );
454 int g = int( qGreen(p1)*prop1 + qGreen(p2)*prop2 );
455 int b = int( qBlue(p1)*prop1 + qBlue(p2)*prop2 );
456 return qRgba(r, g, b, (a1>a2) ? a1:a2);
457}
458
459
460QPixmap MTray::Private::makeIcon()
461{
462 if ( !icon )
463 return QPixmap();
464
465#ifdef Q_WS_X11
466 // on X11, the KDE dock is 22x22. let's make our icon "seem" bigger.
467 QImage real(22,22,32);
468 QImage in = icon->image();
469 in.detach();
470 real.setAlphaBuffer(true);
471
472 // make sure it is no bigger than 16x16
473 if(in.width() > 16 || in.height() > 16)
474 in = in.smoothScale(16,16);
475
476 int xo = (real.width() - in.width()) / 2;
477 int yo = (real.height() - in.height()) / 2;
478
479 int n, n2;
480
481 // clear the output and make it transparent
482 // deprecates real.fill(0)
483 for(n2 = 0; n2 < real.height(); ++n2)
484 for(n = 0; n < real.width(); ++n)
485 real.setPixel(n, n2, qRgba(0,0,0,0));
486
487 // draw a dropshadow
488 for(n2 = 0; n2 < in.height(); ++n2) {
489 for(n = 0; n < in.width(); ++n) {
490 if(int a = qAlpha(in.pixel(n,n2))) {
491 int x = n + xo + 2;
492 int y = n2 + yo + 2;
493 real.setPixel(x, y, qRgba(0x80,0x80,0x80,a));
494 }
495 }
496 }
497
498 // draw the image
499 for(n2 = 0; n2 < in.height(); ++n2) {
500 for(n = 0; n < in.width(); ++n) {
501 if(qAlpha(in.pixel(n,n2))) {
502 QRgb pold = real.pixel(n + xo , n2 + yo);
503 QRgb pnew = in.pixel(n , n2);
504 real.setPixel(n + xo, n2 + yo, pixelBlend(pold, pnew));
505 }
506 }
507 }
508
509 QPixmap pixmap;
510 pixmap.convertFromImage(real);
511 return pixmap;
512#else
513 return icon->pixmap();
514#endif
515}
516
517void MTray::animate()
518{
519#ifdef Q_WS_X11
520 if ( !d->icon )
521 return;
522
523 QString cachedName = "PsiTray/" + option.defaultRosterIconset + "/" + d->icon->name() + "/" + QString::number( d->icon->framenumber() );
524
525 QPixmap p;
526 if ( !QPixmapCache::find(cachedName, p) ) {
527 p = d->makeIcon();
528 QPixmapCache::insert( cachedName, p );
529 }
530
531 d->ti->setIcon(p);
532#else
533 d->ti->setIcon( d->makeIcon() );
534#endif
535}
536
537//----------------------------------------------------------------------------
538// MAction
539//----------------------------------------------------------------------------
540
541class MAction::Private : public QObject
542{
543public:
544 int id;
545 PsiCon *psi;
546 QSignalMapper *sm;
547
548 Private (int _id, PsiCon *_psi, QObject *parent)
549 : QObject (parent)
550 {
551 id = _id;
552 psi = _psi;
553 sm = new QSignalMapper(this, "MAction::Private::SignalMapper");
554 }
555
556 QPopupMenu *subMenu(QWidget *p)
557 {
558 QPopupMenu *pm = new QPopupMenu (p);
559 uint i = 0;
560 for ( PsiAccountListIt it(psi->accountList(TRUE)); it.current(); ++it, i++ )
561 {
562 PsiAccount *acc = it.current();
563 pm->insertItem( acc->name(), parent(), SLOT(itemActivated(int)), 0, id*1000 + i );
564 pm->setItemParameter ( id*1000 + i, i );
565 }
566 return pm;
567 }
568};
569
570MAction::MAction(Icon i, const QString &s, int id, PsiCon *psi, QObject *parent)
571: IconAction(s, s, 0, parent)
572{
573 init (i, id, psi);
574}
575
576MAction::MAction(const QString &s, int id, PsiCon *psi, QObject *parent)
577: IconAction(s, s, 0, parent)
578{
579 init (Icon(), id, psi);
580}
581
582void MAction::init(Icon i, int id, PsiCon *psi)
583{
584 d = new Private(id, psi, this);
585 setIcon (&i);
586 connect(psi, SIGNAL(accountCountChanged()), SLOT(numAccountsChanged()));
587 setEnabled ( !d->psi->accountList(TRUE).isEmpty() );
588 connect (d->sm, SIGNAL(mapped(int)), SLOT(itemActivated(int)));
589}
590
591bool MAction::addTo(QWidget *w)
592{
593 if ( w->inherits("QPopupMenu") )
594 {
595 QPopupMenu *menu = (QPopupMenu*)w;
596 QIconSet iconset;
597#ifndef Q_WS_MAC
598 iconset = iconSet();
599#endif
600 if ( d->psi->accountList(TRUE).count() < 2 ) {
601 menu->insertItem ( iconset, menuText(), this, SLOT(itemActivated(int)), 0, d->id*1000 + 0 );
602 menu->setItemEnabled (d->id*1000 + 0, isEnabled());
603 menu->setItemParameter ( d->id*1000 + 0, 0 );
604 }
605 else
606 menu->insertItem(iconset, menuText(), d->subMenu(w));
607 }
608 else
609 return IconAction::addTo(w);
610
611 return true;
612}
613
614void MAction::addingToolButton(IconToolButton *btn)
615{
616 connect (btn, SIGNAL(clicked()), d->sm, SLOT(map()));
617 d->sm->setMapping (btn, 0);
618
619 if ( d->psi->accountList(TRUE).count() >= 2 )
620 btn->setPopup ( d->subMenu(btn) );
621 else
622 btn->setPopup (0);
623}
624
625void MAction::itemActivated(int n)
626{
627 PsiAccountList list = (PsiAccountList)d->psi->accountList(TRUE);
628
629 if (n >= (int)list.count()) // just in case
630 return;
631
632 emit activated((PsiAccount *)list.at(n), d->id);
633}
634
635void MAction::numAccountsChanged()
636{
637 setEnabled ( !d->psi->accountList(TRUE).isEmpty() );
638
639 QPtrList<IconToolButton> btns = buttonList();
640 for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
641 QToolButton *btn = it.current();
642
643 if ( btn->popup() )
644 delete btn->popup();
645 btn->setPopup (0);
646
647 if ( d->psi->accountList(TRUE).count() >= 2 )
648 btn->setPopup ( d->subMenu(btn) );
649 }
650}
651
652IconAction *MAction::copy() const
653{
654 MAction *act = new MAction(text(), d->id, d->psi, 0);
655
656 *act = *this;
657
658 return act;
659}
660
661MAction &MAction::operator=( const MAction &from )
662{
663 *( (IconAction *)this ) = from;
664
665 return *this;
666}
667
668//----------------------------------------------------------------------------
669// SpacerAction
670//----------------------------------------------------------------------------
671
672SpacerAction::SpacerAction(QObject *parent, const char *name)
673: IconAction(parent, name)
674{
675 setText(tr("<Spacer>"));
676 setMenuText(tr("<Spacer>"));
677 setWhatsThis(tr("Spacer provides spacing to separate actions"));
678}
679
680SpacerAction::~SpacerAction()
681{
682}
683
684bool SpacerAction::addTo(QWidget *w)
685{
686 if ( w->inherits("QToolBar") ) {
687 new StretchWidget(w);
688 return true;
689 }
690
691 return false;
692}
693
694IconAction *SpacerAction::copy() const
695{
696 return new SpacerAction( 0 );
697}
698
699//----------------------------------------------------------------------------
700// SeparatorAction
701//----------------------------------------------------------------------------
702
703class SeparatorAction::Private
704{
705public:
706 Private() { }
707
708 QAction *separator;
709};
710
711SeparatorAction::SeparatorAction( QObject *parent, const char *name )
712 : IconAction( tr("<Separator>"), tr("<Separator>"), 0, parent, name )
713{
714 d = new Private();
715 d->separator = new QAction(this, "qt_separator_action");
716
717 setWhatsThis (tr("Separator"));
718}
719
720SeparatorAction::~SeparatorAction()
721{
722 delete d;
723}
724
725bool SeparatorAction::addTo (QWidget *w)
726{
727 return d->separator->addTo( w );
728}
729
730IconAction *SeparatorAction::copy() const
731{
732 return new SeparatorAction( 0 );
733}
734
735//----------------------------------------------------------------------------
736// EventNotifierAction
737//----------------------------------------------------------------------------
738
739class EventNotifierAction::Private
740{
741public:
742 Private() { }
743
744 QPtrList<MLabel> labels;
745 bool hide;
746};
747
748EventNotifierAction::EventNotifierAction(QObject *parent, const char *name)
749: IconAction(parent, name)
750{
751 d = new Private;
752 setMenuText(tr("<Event notifier>"));
753 d->hide = true;
754}
755
756EventNotifierAction::~EventNotifierAction()
757{
758 delete d;
759}
760
761bool EventNotifierAction::addTo(QWidget *w)
762{
763 if ( w->inherits("QToolBar") ) {
764 MLabel *label = new MLabel(w, "EventNotifierAction::MLabel");
765 label->setText(text());
766 d->labels.append(label);
767 connect(label, SIGNAL(destroyed()), SLOT(objectDestroyed()));
768 connect(label, SIGNAL(doubleClicked()), SIGNAL(activated()));
769 connect(label, SIGNAL(clicked(int)), SIGNAL(clicked(int)));
770
771 if ( d->hide )
772 hide();
773
774 return true;
775 }
776
777 return false;
778}
779
780void EventNotifierAction::setText(const QString &t)
781{
782 IconAction::setText("<nobr>" + t + "</nobr>");
783
784 QPtrListIterator<MLabel> it ( d->labels );
785 for ( ; it.current(); ++it) {
786 MLabel *label = it.current();
787 label->setText(text());
788 }
789}
790
791void EventNotifierAction::objectDestroyed()
792{
793 MLabel *label = (MLabel *)sender();
794 d->labels.removeRef(label);
795}
796
797void EventNotifierAction::hide()
798{
799 d->hide = true;
800
801 QPtrListIterator<MLabel> it ( d->labels );
802 for ( ; it.current(); ++it) {
803 MLabel *label = it.current();
804 label->hide();
805 QToolBar *toolBar = (QToolBar *)label->parent();
806
807 QObjectList *l = toolBar->queryList( "QWidget" );
808 int found = 0;
809
810 QObjectListIt it( *l );
811 QObject *obj;
812 for ( ; (obj = it.current()); ++it) {
813 if ( QCString(obj->name()).left(3) != "qt_" ) // misc internal Qt objects
814 found++;
815 }
816 delete l;
817
818 if ( found == 1 ) // only MLabel is on ToolBar
819 toolBar->hide();
820 }
821}
822
823void EventNotifierAction::show()
824{
825 d->hide = false;
826
827 QPtrListIterator<MLabel> it ( d->labels );
828 for ( ; it.current(); ++it) {
829 MLabel *label = it.current();
830 QToolBar *toolBar = (QToolBar *)label->parent();
831 toolBar->show();
832 label->show();
833 }
834}
835
836IconAction *EventNotifierAction::copy() const
837{
838 EventNotifierAction *act = new EventNotifierAction( 0 );
839
840 *act = *this;
841
842 return act;
843}
844
845EventNotifierAction &EventNotifierAction::operator=( const EventNotifierAction &from )
846{
847 *( (IconAction *)this ) = from;
848
849 d->hide = from.d->hide;
850
851 return *this;
852}
853
854#include "mainwin_p.moc"
Note: See TracBrowser for help on using the repository browser.