1/*
2 * Copyright 2008 by Montel Laurent <montel@kde.org>
3 * Copyright 2008 by Marco Martin <notmart@gmail.com>
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 St, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 */
20
21#include "popupapplet.h"
22#include "private/popupapplet_p.h"
23#include "private/dialog_p.h"
24
25#include <QApplication>
26#include <QGraphicsProxyWidget>
27#include <QGraphicsLinearLayout>
28#include <QTimer>
29#include <QVBoxLayout>
30#include <QDeclarativeItem>
31
32#ifdef Q_WS_X11
33#include <QX11Info>
34#endif
35
36#include <kicon.h>
37#include <kiconloader.h>
38#include <kwindowsystem.h>
39#include <kglobalsettings.h>
40#include <netwm.h>
41
42#include "plasma/private/applet_p.h"
43#include "plasma/private/extenderitemmimedata_p.h"
44#include "plasma/corona.h"
45#include "plasma/containment.h"
46#include "plasma/private/containment_p.h"
47#include "plasma/dialog.h"
48#include "plasma/extenders/extender.h"
49#include "plasma/extenders/extenderitem.h"
50#include "plasma/package.h"
51#include "plasma/theme.h"
52#include "plasma/scripting/appletscript.h"
53#include "plasma/tooltipmanager.h"
54#include "plasma/widgets/iconwidget.h"
55
56namespace Plasma
57{
58
59PopupApplet::PopupApplet(QObject *parent, const QVariantList &args)
60 : Plasma::Applet(parent, args),
61 d(new PopupAppletPrivate(this))
62{
63}
64
65PopupApplet::PopupApplet(const QString &packagePath, uint appletId, const QVariantList &args)
66 : Plasma::Applet(packagePath, appletId, args),
67 d(new PopupAppletPrivate(this))
68{
69}
70
71PopupApplet::~PopupApplet()
72{
73 delete widget();
74 delete d;
75}
76
77void PopupApplet::setPopupIcon(const QIcon &icon)
78{
79 if (icon.isNull()) {
80 if (d->icon) {
81 delete d->icon;
82 d->icon = 0;
83 setLayout(0);
84 setAspectRatioMode(d->savedAspectRatio);
85 d->popupConstraintsEvent(FormFactorConstraint);
86 }
87
88 return;
89 }
90
91 d->createIconWidget();
92 d->icon->setIcon(icon);
93}
94
95void PopupApplet::setPopupIcon(const QString &iconName)
96{
97 // Attempt 1: is it in the plasmoid package?
98 if (package()) {
99 const QString file = package()->filePath("images", iconName);
100 if (!file.isEmpty()) {
101 setPopupIcon(KIcon(file));
102 return;
103 }
104 }
105
106 // Attempt 2: is it a svg in the icons directory?
107 QString name = QString("icons/") + iconName.split("-").first();
108 if (!Plasma::Theme::defaultTheme()->imagePath(name).isEmpty()) {
109 d->createIconWidget();
110 d->icon->setSvg(name, iconName);
111 if (d->icon->svg().isEmpty()) {
112 setPopupIcon(KIcon(iconName));
113 }
114
115 return;
116 }
117
118 // Final Attempt: use KIcon
119 setPopupIcon(KIcon(iconName));
120}
121
122QIcon PopupApplet::popupIcon() const
123{
124 return d->icon ? d->icon->icon() : QIcon();
125}
126
127QWidget *PopupApplet::widget()
128{
129 return d->widget;
130}
131
132void PopupApplet::setWidget(QWidget *widget)
133{
134 if (d->widget) {
135 Plasma::Dialog *dialog = d->dialogPtr.data();
136 if (dialog) {
137 dialog->setGraphicsWidget(0);
138 QVBoxLayout *lay = 0;
139
140 QLayout *existingLayout = dialog->layout();
141 if (existingLayout) {
142 lay = dynamic_cast<QVBoxLayout *>(existingLayout);
143 if (!lay) {
144 delete existingLayout;
145 }
146 }
147
148 if (!lay) {
149 lay = new QVBoxLayout;
150 dialog->setLayout(lay);
151 }
152
153 lay->removeWidget(d->widget);
154 lay->addWidget(widget);
155 } else if (d->proxy) {
156 d->proxy.data()->setWidget(widget);
157 }
158 }
159
160 d->widget = widget;
161}
162
163QGraphicsWidget *PopupApplet::graphicsWidget()
164{
165 if (d->graphicsWidget) {
166 return d->graphicsWidget.data();
167 } else {
168 return static_cast<Applet*>(this)->d->extender.data();
169 }
170}
171
172void PopupApplet::setGraphicsWidget(QGraphicsWidget *graphicsWidget)
173{
174 if (d->graphicsWidget) {
175 if (d->dialogPtr) {
176 d->dialogPtr.data()->setGraphicsWidget(graphicsWidget);
177 } else if (layout()) {
178 QGraphicsLinearLayout *lay = static_cast<QGraphicsLinearLayout *>(layout());
179 lay->removeAt(0);
180 if (graphicsWidget) {
181 lay->addItem(graphicsWidget);
182 }
183 }
184 }
185
186 d->graphicsWidget = graphicsWidget;
187}
188
189void PopupAppletPrivate::checkExtenderAppearance(Plasma::FormFactor f)
190{
191 Extender *extender = qobject_cast<Extender*>(q->graphicsWidget());
192 if (extender) {
193 if (f != Plasma::Horizontal && f != Plasma::Vertical) {
194 extender->setAppearance(Extender::NoBorders);
195 } else if (q->location() == TopEdge) {
196 extender->setAppearance(Extender::TopDownStacked);
197 } else {
198 extender->setAppearance(Extender::BottomUpStacked);
199 }
200
201 if (dialogPtr) {
202 dialogPtr.data()->setGraphicsWidget(extender);
203 }
204 }
205}
206
207void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints)
208{
209 Plasma::FormFactor f = q->formFactor();
210
211 if (constraints & Plasma::LocationConstraint) {
212 checkExtenderAppearance(f);
213 }
214
215 if (constraints & Plasma::FormFactorConstraint ||
216 constraints & Plasma::StartupCompletedConstraint ||
217 (constraints & Plasma::SizeConstraint &&
218 (f == Plasma::Vertical || f == Plasma::Horizontal))) {
219 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
220
221 if (icon && lay && lay->count() > 0) {
222 lay->removeAt(0);
223 }
224
225 QSizeF minimum;
226 QSizeF parentSize;
227
228 QGraphicsWidget *gWidget = q->graphicsWidget();
229 //kDebug() << "graphics widget is" << (QObject*)gWidget;
230 QWidget *qWidget = q->widget();
231
232 if (gWidget) {
233 minimum = gWidget->minimumSize();
234 // our layout may have been replaced on us in the call to graphicsWidget!
235 lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
236
237 if (!(constraints & LocationConstraint)) {
238 checkExtenderAppearance(f);
239 }
240 } else if (qWidget) {
241 minimum = qWidget->minimumSizeHint();
242 }
243
244 //99% of the times q->parentWidget() is the containment, but using it we can also manage the applet-in-applet case (i.e. systray)
245 //there are also cases where the parentlayoutitem is bigger than the containment (e.g. newspaper)
246 QDeclarativeItem *di = qobject_cast<QDeclarativeItem *>(q->parentObject());
247 if (q->parentLayoutItem()) {
248 parentSize = q->parentLayoutItem()->geometry().size();
249 } else if (q->parentWidget()) {
250 parentSize = q->parentWidget()->size();
251 } else if (di) {
252 parentSize = QSizeF(di->width(), di->height());
253 }
254
255 //check if someone did the nasty trick of applets in applets, in this case we always want to be collapsed
256 QGraphicsWidget *candidateParentApplet = q;
257 Plasma::Applet *parentApplet = 0;
258 //this loop should be executed normally a single time, at most 2-3 times for quite complex containments
259 while (candidateParentApplet) {
260 candidateParentApplet = candidateParentApplet->parentWidget();
261 parentApplet = qobject_cast<Plasma::Applet *>(candidateParentApplet);
262 if (parentApplet) {
263 break;
264 }
265 }
266
267 //If the popup is more than two times larger than the applet
268 //center it (or taller in case of vertical panels
269 if (f == Plasma::Vertical) {
270 if ((gWidget && gWidget->size().height() > q->size().height() / 2) ||
271 (qWidget && qWidget->size().height() > q->size().height() / 2)) {
272 popupAlignment = Qt::AlignCenter;
273 } else {
274 popupAlignment = Qt::AlignLeft;
275 }
276 } else {
277 if ((gWidget && gWidget->size().width() > q->size().width() / 2) ||
278 (qWidget && qWidget->size().width() > q->size().width() / 2)) {
279 popupAlignment = Qt::AlignCenter;
280 } else {
281 popupAlignment = Qt::AlignLeft;
282 }
283 }
284
285 //Applet on desktop
286 if (((!parentApplet || parentApplet->isContainment() ) && icon && (!icon->svg().isEmpty() || !icon->icon().isNull()) && ((f != Plasma::Vertical && f != Plasma::Horizontal)) ||
287 (((f == Plasma::Vertical && parentSize.width() >= minimum.width()) ||
288 (f == Plasma::Horizontal && parentSize.height() >= minimum.height())) &&
289 (!q->parentWidget() || (q->parentWidget()->size().width() >= minimum.width() && q->parentWidget()->size().height() >= minimum.height()))))) {
290 //kDebug() << "we are expanding the popupapplet";
291
292
293 // we only switch to expanded if we aren't horiz/vert constrained and
294 // this applet has an icon.
295 // otherwise, we leave it up to the applet itself to figure it out
296 if (icon) {
297 icon->hide();
298 }
299
300 if (savedAspectRatio != Plasma::InvalidAspectRatioMode) {
301 q->setAspectRatioMode(savedAspectRatio);
302 }
303
304 Dialog *dialog = dialogPtr.data();
305 if (dialog) {
306 if (dialog->layout() && qWidget) {
307 //we don't want to delete Widget inside the dialog layout
308 dialog->layout()->removeWidget(qWidget);
309 }
310
311 if (qWidget) {
312 qWidget->setParent(0);
313 }
314
315 delete dialog;
316 }
317
318 if (!lay) {
319 lay = new QGraphicsLinearLayout();
320 lay->setContentsMargins(0, 0, 0, 0);
321 lay->setSpacing(0);
322 lay->setOrientation(Qt::Horizontal);
323 q->setLayout(lay);
324 }
325
326 QSize prefSize;
327
328 if (gWidget) {
329 if (proxy) {
330 proxy.data()->setWidget(0);
331 delete proxy.data();
332 }
333
334 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
335
336 if (corona) {
337 corona->removeOffscreenWidget(gWidget);
338 }
339
340 lay->addItem(gWidget);
341 prefSize = gWidget->preferredSize().toSize();
342 } else if (qWidget) {
343 if (!proxy) {
344 proxy = new QGraphicsProxyWidget(q);
345 proxy.data()->setWidget(qWidget);
346 proxy.data()->show();
347 }
348
349 lay->addItem(proxy.data());
350 prefSize = qWidget->sizeHint();
351 }
352
353 //we could be on a big panel, but in that case we will be able to resize
354 //more than the natural minimum size, because we'll transform into an icon
355 if (f == Plasma::Horizontal) {
356 minimum.setHeight(0);
357 } else if (f == Plasma::Vertical) {
358 minimum.setWidth(0);
359 }
360
361 qreal left, top, right, bottom;
362 q->getContentsMargins(&left, &top, &right, &bottom);
363 QSizeF oldSize(q->size());
364
365 //size not saved/invalid size saved
366 if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) {
367 q->resize(prefSize);
368 emit q->appletTransformedItself();
369 }
370 } else {
371 //Applet on popup
372 if (icon && lay) {
373 lay->addItem(icon);
374 }
375
376 //kDebug() << "about to switch to a popup";
377 if (!qWidget && !gWidget) {
378 delete dialogPtr.data();
379 return;
380 }
381
382 //there was already a dialog? don't make the switch again
383 if (dialogPtr) {
384 return;
385 }
386
387 if (proxy) {
388 proxy.data()->setWidget(0); // prevent it from deleting our widget!
389 delete proxy.data();
390 }
391
392 //save the aspect ratio mode in case we drag'n drop in the Desktop later
393 savedAspectRatio = q->aspectRatioMode();
394
395 if (icon) {
396 icon->show();
397 q->setAspectRatioMode(Plasma::ConstrainedSquare);
398 }
399
400 Dialog *dialog = new Dialog();
401 dialog->d->appletPtr = q;
402 dialogPtr = dialog;
403
404 if (icon) {
405 dialog->setAspectRatioMode(savedAspectRatio);
406 }
407
408 //no longer use Qt::Popup since that seems to cause a lot of problem when you drag
409 //stuff out of your Dialog (extenders). Monitor WindowDeactivate events so we can
410 //emulate the same kind of behavior as Qt::Popup (close when you click somewhere
411 //else.
412
413 if (gWidget) {
414 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
415 if (!corona) {
416 corona = qobject_cast<Corona *>(q->scene());
417 }
418
419 if (corona) {
420 corona->addOffscreenWidget(gWidget);
421 }
422
423 gWidget->show();
424 dialog->setGraphicsWidget(gWidget);
425 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (gWidget->windowFlags() & Qt::X11BypassWindowManagerHint));
426 } else if (qWidget) {
427 QVBoxLayout *l_layout = new QVBoxLayout(dialog);
428 l_layout->setSpacing(0);
429 l_layout->setMargin(0);
430 l_layout->addWidget(qWidget);
431 dialog->adjustSize();
432 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (qWidget->windowFlags() & Qt::X11BypassWindowManagerHint));
433 } else {
434 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
435 }
436
437 restoreDialogSize();
438 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
439 dialog->installEventFilter(q);
440
441 QObject::connect(dialog, SIGNAL(dialogResized()), q, SLOT(dialogSizeChanged()));
442 QObject::connect(dialog, SIGNAL(dialogVisible(bool)), q, SLOT(dialogStatusChanged(bool)));
443 }
444 }
445
446 if (constraints & Plasma::PopupConstraint) {
447 updateDialogPosition();
448 }
449
450 if (icon) {
451 // emit the size hint changing stuff for our applet as we are handling
452 // the size changings
453 emit q->sizeHintChanged(Qt::PreferredSize);
454 }
455}
456
457void PopupAppletPrivate::appletActivated()
458{
459 internalTogglePopup(true);
460}
461
462QSizeF PopupApplet::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
463{
464 if (!d->dialogPtr || which != Qt::PreferredSize) {
465 return Applet::sizeHint(which, constraint);
466 }
467
468 switch (formFactor()) {
469 case Vertical:
470 case Horizontal: {
471 const int size = IconSize(KIconLoader::Panel);
472 return QSizeF(size, size);
473 }
474 default:
475 break;
476 }
477
478 const int size = IconSize(KIconLoader::Desktop);
479 return QSizeF(size, size);
480}
481
482void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event)
483{
484 if (!d->icon && !d->popupLostFocus && event->buttons() == Qt::LeftButton) {
485 d->clicked = scenePos().toPoint();
486 event->setAccepted(true);
487 return;
488 } else {
489 d->popupLostFocus = false;
490 Applet::mousePressEvent(event);
491 }
492}
493
494void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
495{
496 if (!d->icon &&
497 (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
498 d->internalTogglePopup();
499 } else {
500 Applet::mouseReleaseEvent(event);
501 }
502}
503
504bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
505{
506 if (!d->passive && watched == d->dialogPtr.data() && (event->type() == QEvent::WindowDeactivate)) {
507 d->popupLostFocus = true;
508 QTimer::singleShot(100, this, SLOT(clearPopupLostFocus()));
509 }
510
511 if (watched == d->dialogPtr.data() && event->type() == QEvent::ContextMenu) {
512 //pass it up to the applet
513 //well, actually we have to pass it to the *containment*
514 //because all the code for showing an applet's contextmenu is actually in Containment.
515 Containment *c = containment();
516 if (c) {
517 Applet *applet = this;
518 Dialog *dialog = d->dialogPtr.data();
519 if (dialog && dialog->graphicsWidget()) {
520 int left, top, right, bottom;
521 dialog->getContentsMargins(&left, &top, &right, &bottom);
522 const QPoint eventPos = static_cast<QContextMenuEvent*>(event)->pos() - QPoint(left, top);
523 QPointF pos = dialog->graphicsWidget()->mapToScene(eventPos);
524
525 if (Applet *actual = c->d->appletAt(pos)) {
526 applet = actual;
527 }
528 }
529
530 KMenu desktopMenu;
531 c->d->addAppletActions(desktopMenu, applet, event);
532
533 if (!desktopMenu.isEmpty()) {
534 desktopMenu.exec(static_cast<QContextMenuEvent*>(event)->globalPos());
535 return true;
536 }
537
538 return false;
539 }
540 }
541
542 return Applet::eventFilter(watched, event);
543}
544
545//FIXME: some duplication between the drag events... maybe add some simple helper function?
546void PopupApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
547{
548 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
549 const ExtenderItemMimeData *mimeData =
550 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
551 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
552 event->accept();
553 showPopup();
554 }
555 }
556}
557
558void PopupApplet::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
559{
560 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
561 const ExtenderItemMimeData *mimeData =
562 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
563 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
564 //We want to hide the popup if we're not moving onto the popup AND it is not the popup
565 //we started.
566 if (d->dialogPtr && !d->dialogPtr.data()->geometry().contains(event->screenPos()) &&
567 mimeData->extenderItem()->extender() != qobject_cast<Extender*>(graphicsWidget())) {
568 //We actually try to hide the popup, with a call to showPopup, with a smal timeout,
569 //so if the user moves into the popup fast enough, it remains open (the extender
570 //will call showPopup which will cancel the timeout.
571 showPopup(250);
572 }
573 }
574 }
575}
576
577void PopupApplet::dropEvent(QGraphicsSceneDragDropEvent *event)
578{
579 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
580 const ExtenderItemMimeData *mimeData =
581 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
582 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
583 mimeData->extenderItem()->setExtender(extender());
584 QApplication::restoreOverrideCursor();
585 }
586 }
587}
588
589void PopupApplet::showPopup(uint popupDuration)
590{
591 // use autohideTimer to store when the next show should be
592 if (popupDuration > 0 || d->autohideTimer) {
593 if (!d->autohideTimer) {
594 d->autohideTimer = new QTimer(this);
595 d->autohideTimer->setSingleShot(true);
596 connect(d->autohideTimer, SIGNAL(timeout()), this, SLOT(hideTimedPopup()));
597 }
598
599 d->autohideTimer->stop();
600 d->autohideTimer->setInterval(popupDuration);
601 }
602
603 //kDebug() << "starting delayed show, duration for popup is" << popupDuration;
604 d->delayedShowTimer.start(0, this);
605}
606
607void PopupApplet::timerEvent(QTimerEvent *event)
608{
609 if (event->timerId() == d->delayedShowTimer.timerId()) {
610 d->delayedShowTimer.stop();
611 Dialog *dialog = d->dialogPtr.data();
612 if (dialog) {
613 // move the popup before its fist show, even if the show isn't triggered by
614 // a click, this should fix the first random position seen in some widgets
615 if (!dialog->isVisible()) {
616 d->internalTogglePopup();
617 }
618
619 const int popupDuration = d->autohideTimer ? d->autohideTimer->interval() : 0;
620 //kDebug() << "popupDuration is:" << (d->autohideTimer ? d->autohideTimer->interval() : 0);
621 if (popupDuration > 0) {
622 d->autohideTimer->start();
623 } else if (d->autohideTimer) {
624 d->autohideTimer->stop();
625 }
626 }
627 } else if (event->timerId() == d->showDialogTimer.timerId()) {
628 d->showDialogTimer.stop();
629 d->showDialog();
630 } else {
631 Applet::timerEvent(event);
632 }
633}
634
635void PopupApplet::hidePopup()
636{
637 d->showDialogTimer.stop();
638 d->delayedShowTimer.stop();
639
640 Dialog *dialog = d->dialogPtr.data();
641 if (dialog && dialog->isVisible()) {
642 if (location() != Floating) {
643 dialog->animatedHide(locationToInverseDirection(location()));
644 } else {
645 dialog->hide();
646 }
647 }
648}
649
650void PopupApplet::togglePopup()
651{
652 d->internalTogglePopup();
653}
654
655Plasma::PopupPlacement PopupApplet::popupPlacement() const
656{
657 return d->popupPlacement;
658}
659
660void PopupApplet::setPopupAlignment(Qt::AlignmentFlag alignment)
661{
662 d->popupAlignment = alignment;
663}
664
665Qt::AlignmentFlag PopupApplet::popupAlignment() const
666{
667 return d->popupAlignment;
668}
669
670void PopupApplet::popupEvent(bool popped)
671{
672 if (Applet::d->script) {
673 emit Applet::d->script->popupEvent(popped);
674 }
675}
676
677void PopupApplet::setPassivePopup(bool passive)
678{
679 d->passive = passive;
680}
681
682bool PopupApplet::isPassivePopup() const
683{
684 return d->passive;
685}
686
687bool PopupApplet::isPopupShowing() const
688{
689 return d->dialogPtr && d->dialogPtr.data()->isVisible();
690}
691
692bool PopupApplet::isIconified() const
693{
694 return d->dialogPtr;
695}
696
697PopupAppletPrivate::PopupAppletPrivate(PopupApplet *applet)
698 : q(applet),
699 icon(0),
700 widget(0),
701 popupPlacement(Plasma::FloatingPopup),
702 popupAlignment(Qt::AlignLeft),
703 savedAspectRatio(Plasma::InvalidAspectRatioMode),
704 autohideTimer(0),
705 preShowStatus(UnknownStatus),
706 popupLostFocus(false),
707 passive(false)
708{
709 int iconSize = IconSize(KIconLoader::Desktop);
710 q->resize(iconSize, iconSize);
711 q->setAcceptDrops(true);
712 QObject::disconnect(q, SIGNAL(activate()), static_cast<Applet*>(q), SLOT(setFocus()));
713 QObject::connect(q, SIGNAL(activate()), q, SLOT(appletActivated()));
714 QObject::connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), q, SLOT(iconSizeChanged(int)));
715}
716
717PopupAppletPrivate::~PopupAppletPrivate()
718{
719 if (proxy) {
720 proxy.data()->setWidget(0);
721 }
722
723 delete dialogPtr.data();
724 delete icon;
725}
726
727void PopupAppletPrivate::iconSizeChanged(int group)
728{
729 if (icon && (group == KIconLoader::Desktop || group == KIconLoader::Panel)) {
730 q->updateGeometry();
731 }
732}
733
734void PopupAppletPrivate::internalTogglePopup(bool fromActivatedSignal)
735{
736 if (autohideTimer) {
737 autohideTimer->stop();
738 }
739
740 delayedShowTimer.stop();
741
742 Plasma::Dialog *dialog = dialogPtr.data();
743 if (!dialog) {
744 q->setFocus(Qt::ShortcutFocusReason);
745 if (!fromActivatedSignal) {
746 QObject::disconnect(q, SIGNAL(activate()), q, SLOT(appletActivated()));
747 emit q->activate();
748 QObject::connect(q, SIGNAL(activate()), q, SLOT(appletActivated()));
749 }
750 return;
751 }
752
753 if (!q->view()) {
754 return;
755 }
756
757 if (dialog->isVisible()) {
758 if (q->location() != Floating) {
759 dialog->animatedHide(locationToInverseDirection(q->location()));
760 } else {
761 dialog->hide();
762 }
763
764 dialog->clearFocus();
765 } else {
766 if (q->graphicsWidget() &&
767 q->graphicsWidget() == static_cast<Applet*>(q)->d->extender.data() &&
768 static_cast<Applet*>(q)->d->extender.data()->isEmpty()) {
769 // we have nothing to show, so let's not.
770 if (!fromActivatedSignal) {
771 QObject::disconnect(q, SIGNAL(activate()), q, SLOT(appletActivated()));
772 emit q->activate();
773 QObject::connect(q, SIGNAL(activate()), q, SLOT(appletActivated()));
774 }
775 return;
776 }
777
778 ToolTipManager::self()->hide(q);
779 showDialogTimer.start(0, q);
780 }
781}
782
783void PopupAppletPrivate::showDialog()
784{
785 Plasma::Dialog *dialog = dialogPtr.data();
786 if (!dialog) {
787 return;
788 }
789
790 updateDialogPosition();
791
792 KWindowSystem::setOnAllDesktops(dialog->winId(), true);
793 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
794
795 if (icon) {
796 dialog->setAspectRatioMode(savedAspectRatio);
797 }
798
799 if (q->location() != Floating) {
800 dialog->animatedShow(locationToDirection(q->location()));
801 } else {
802 dialog->show();
803 }
804
805 if (!(dialog->windowFlags() & Qt::X11BypassWindowManagerHint)) {
806 KWindowSystem::activateWindow(dialog->winId());
807 }
808}
809
810void PopupAppletPrivate::hideTimedPopup()
811{
812 autohideTimer->stop();
813 q->hidePopup();
814}
815
816void PopupAppletPrivate::clearPopupLostFocus()
817{
818 if (!icon || !icon->isDown()) {
819 q->hidePopup();
820 }
821
822 popupLostFocus = false;
823}
824
825KConfigGroup PopupAppletPrivate::popupConfigGroup()
826{
827 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
828 return KConfigGroup(mainGroup, "PopupApplet");
829}
830
831void PopupAppletPrivate::dialogSizeChanged()
832{
833 //Reposition the dialog
834 Plasma::Dialog *dialog = dialogPtr.data();
835 if (dialog) {
836 KConfigGroup sizeGroup = popupConfigGroup();
837 sizeGroup.writeEntry("DialogHeight", dialog->height());
838 sizeGroup.writeEntry("DialogWidth", dialog->width());
839
840 updateDialogPosition(!dialog->isUserResizing());
841
842 emit q->configNeedsSaving();
843 emit q->appletTransformedByUser();
844 }
845}
846
847void PopupAppletPrivate::dialogStatusChanged(bool shown)
848{
849 if (shown) {
850 preShowStatus = q->status();
851 q->setStatus(NeedsAttentionStatus);
852 QObject::connect(q, SIGNAL(newStatus(Plasma::ItemStatus)),
853 q, SLOT(statusChangeWhileShown(Plasma::ItemStatus)),
854 Qt::UniqueConnection);
855 } else {
856 QObject::disconnect(q, SIGNAL(newStatus(Plasma::ItemStatus)),
857 q, SLOT(statusChangeWhileShown(Plasma::ItemStatus)));
858 q->setStatus(preShowStatus);
859 }
860
861 q->popupEvent(shown);
862}
863
864void PopupAppletPrivate::statusChangeWhileShown(Plasma::ItemStatus status)
865{
866 preShowStatus = status;
867}
868
869void PopupAppletPrivate::createIconWidget()
870{
871 if (icon) {
872 return;
873 }
874
875 icon = new Plasma::IconWidget(q);
876 QObject::connect(icon, SIGNAL(clicked()), q, SLOT(internalTogglePopup()));
877
878 QGraphicsLinearLayout *layout = new QGraphicsLinearLayout();
879 layout->setContentsMargins(0, 0, 0, 0);
880 layout->setSpacing(0);
881 layout->setOrientation(Qt::Horizontal);
882 layout->addItem(icon);
883 layout->setAlignment(icon, Qt::AlignCenter);
884 q->setLayout(layout);
885}
886
887void PopupAppletPrivate::restoreDialogSize()
888{
889 Plasma::Dialog *dialog = dialogPtr.data();
890 if (!dialog) {
891 return;
892 }
893
894 Corona *corona = qobject_cast<Corona *>(q->scene());
895 if (!corona) {
896 return;
897 }
898
899 KConfigGroup sizeGroup = popupConfigGroup();
900
901 int preferredWidth = 0;
902 int preferredHeight = 0;
903 QGraphicsWidget *gWidget = dialog->graphicsWidget();
904 if (gWidget) {
905 preferredWidth = gWidget->preferredSize().width();
906 preferredHeight = gWidget->preferredSize().height();
907 }
908
909 const int width = qMin(sizeGroup.readEntry("DialogWidth", preferredWidth),
910 corona->screenGeometry(-1).width() - 50);
911 const int height = qMin(sizeGroup.readEntry("DialogHeight", preferredHeight),
912 corona->screenGeometry(-1).height() - 50);
913
914 QSize saved(width, height);
915
916 if (saved.isNull()) {
917 saved = dialog->sizeHint();
918 } else {
919 saved = saved.expandedTo(dialog->minimumSizeHint());
920 }
921
922 if (saved.width() != dialog->width() || saved.height() != dialog->height()) {
923 dialog->resize(saved);
924 /*if (gWidget) {
925 gWidget->resize(saved);
926 }*/
927 }
928}
929
930void PopupAppletPrivate::updateDialogPosition(bool move)
931{
932 Plasma::Dialog *dialog = dialogPtr.data();
933 if (!dialog) {
934 return;
935 }
936
937 Corona *corona = qobject_cast<Corona *>(q->scene());
938 if (!corona) {
939 return;
940 }
941
942 QGraphicsView *view = q->view();
943 if (!view) {
944 return;
945 }
946
947 const QPoint appletPos = view->mapToGlobal(view->mapFromScene(q->scenePos()));
948
949 QPoint dialogPos = dialog->pos();
950 if (move) {
951 if (!q->containment() || view == q->containment()->view()) {
952 dialogPos = corona->popupPosition(q, dialog->size(), popupAlignment);
953 } else {
954 dialogPos = corona->popupPosition(q->parentItem(), dialog->size(), popupAlignment);
955 }
956 }
957
958 bool reverse = false;
959 if (q->formFactor() == Plasma::Vertical) {
960 reverse = (appletPos.y() + (q->size().height() / 2)) < (dialogPos.y() + (dialog->size().height() / 2));
961 dialog->setMinimumResizeLimits(-1, appletPos.y(), -1, appletPos.y() + q->size().height());
962 } else {
963 reverse = (appletPos.x() + (q->size().width() / 2)) < (dialogPos.x() + (dialog->size().width() / 2));
964 dialog->setMinimumResizeLimits(appletPos.x(), -1, appletPos.x() + q->size().width(), -1);
965 }
966
967 Dialog::ResizeCorners resizeCorners = Dialog::NoCorner;
968 switch (q->location()) {
969 case BottomEdge:
970 resizeCorners = Dialog::NorthEast | Dialog::NorthWest;
971 popupPlacement = reverse ? TopPosedLeftAlignedPopup : TopPosedRightAlignedPopup;
972 break;
973 case TopEdge:
974 resizeCorners = Dialog::SouthEast | Dialog::SouthWest;
975 popupPlacement = reverse ? Plasma::BottomPosedLeftAlignedPopup : Plasma::BottomPosedRightAlignedPopup;
976 break;
977 case LeftEdge:
978 resizeCorners = Dialog::SouthEast | Dialog::NorthEast;
979 popupPlacement = reverse ? RightPosedTopAlignedPopup : RightPosedBottomAlignedPopup;
980 break;
981
982 case RightEdge:
983 resizeCorners = Dialog::SouthWest | Dialog::NorthWest;
984 popupPlacement = reverse ? LeftPosedTopAlignedPopup : LeftPosedBottomAlignedPopup;
985 break;
986
987 default:
988 popupPlacement = FloatingPopup;
989 resizeCorners = Dialog::All;
990 break;
991 }
992
993 dialog->setResizeHandleCorners(resizeCorners);
994 if (move) {
995 dialog->move(dialogPos);
996 }
997}
998
999} // Plasma namespace
1000
1001#include "popupapplet.moc"
1002
1003