some more win32'fication to fix non-ascii filename handling
[kdelibs.git] / plasma / popupapplet.cpp
blobfc8507309a1e9229c5f675a25a8c5a65c42a3c99
1 /*
2 * Copyright 2008 by Montel Laurent <montel@kde.org>
3 * Copyright 2008 by Marco Martin <notmart@gmail.com>
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.
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.
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
21 #include "popupapplet.h"
22 #include "private/popupapplet_p.h"
24 #include <QGraphicsProxyWidget>
25 #include <QGraphicsLinearLayout>
26 #include <QVBoxLayout>
27 #include <QTimer>
28 #include <QApplication>
30 #include <kicon.h>
31 #include <kiconloader.h>
32 #include <kwindowsystem.h>
33 #include <kglobalsettings.h>
35 #include "plasma/private/applet_p.h"
36 #include "plasma/private/extenderitemmimedata_p.h"
37 #include "plasma/corona.h"
38 #include "plasma/containment.h"
39 #include "plasma/dialog.h"
40 #include "plasma/extender.h"
41 #include "plasma/extenderitem.h"
42 #include "plasma/tooltipmanager.h"
43 #include "plasma/widgets/iconwidget.h"
45 namespace Plasma
48 PopupApplet::PopupApplet(QObject *parent, const QVariantList &args)
49 : Plasma::Applet(parent, args),
50 d(new PopupAppletPrivate(this))
52 int iconSize = IconSize(KIconLoader::Desktop);
53 resize(iconSize, iconSize);
54 connect(this, SIGNAL(activate()), this, SLOT(internalTogglePopup()));
55 setAcceptDrops(true);
58 PopupApplet::~PopupApplet()
60 delete widget();
61 delete d;
64 void PopupApplet::setPopupIcon(const QIcon &icon)
66 if (icon.isNull()) {
67 if (d->icon) {
68 delete d->icon;
69 d->icon = 0;
70 setLayout(0);
71 setAspectRatioMode(d->savedAspectRatio);
74 return;
77 if (!d->icon) {
78 d->icon = new Plasma::IconWidget(icon, QString(), this);
79 connect(d->icon, SIGNAL(clicked()), this, SLOT(internalTogglePopup()));
81 QGraphicsLinearLayout *layout = new QGraphicsLinearLayout();
82 layout->setContentsMargins(0, 0, 0, 0);
83 layout->setSpacing(0);
84 layout->setOrientation(Qt::Horizontal);
86 if (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal ) {
87 d->savedAspectRatio = aspectRatioMode();
88 setAspectRatioMode(Plasma::ConstrainedSquare);
91 setLayout(layout);
92 } else {
93 d->icon->setIcon(icon);
97 void PopupApplet::setPopupIcon(const QString &iconName)
99 setPopupIcon(KIcon(iconName));
102 QIcon PopupApplet::popupIcon() const
104 return d->icon ? d->icon->icon() : QIcon();
107 QWidget *PopupApplet::widget()
109 return 0;
112 QGraphicsWidget *PopupApplet::graphicsWidget()
114 return static_cast<Applet*>(this)->d->extender;
117 void PopupAppletPrivate::checkExtenderAppearance(Plasma::FormFactor f)
119 Extender *extender = qobject_cast<Extender*>(q->graphicsWidget());
120 if (extender) {
121 if (f != Plasma::Horizontal && f != Plasma::Vertical) {
122 extender->setAppearance(Extender::NoBorders);
123 } else if (q->location() == TopEdge) {
124 extender->setAppearance(Extender::TopDownStacked);
125 } else {
126 extender->setAppearance(Extender::BottomUpStacked);
129 if (dialog) {
130 dialog->setGraphicsWidget(extender);
135 void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints)
137 Plasma::FormFactor f = q->formFactor();
139 if (constraints & Plasma::LocationConstraint) {
140 checkExtenderAppearance(f);
143 if (constraints & Plasma::FormFactorConstraint ||
144 constraints & Plasma::StartupCompletedConstraint ||
145 (constraints & Plasma::SizeConstraint &&
146 (f == Plasma::Vertical || f == Plasma::Horizontal))) {
147 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
149 if (icon && !icon->icon().isNull() && lay) {
150 lay->removeAt(0);
153 QSizeF minimum;
154 QSizeF containmentSize;
156 QGraphicsWidget *gWidget = q->graphicsWidget();
157 kDebug() << "graphics widget is" << (QObject*)gWidget;
158 QWidget *qWidget = q->widget();
160 if (gWidget) {
161 minimum = gWidget->minimumSize();
162 // our layout may have been replaced on us in the call to graphicsWidget!
163 lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
165 if (!(constraints & LocationConstraint)) {
166 checkExtenderAppearance(f);
168 } else if (qWidget) {
169 minimum = qWidget->minimumSizeHint();
172 if (q->containment()) {
173 containmentSize = q->containment()->size();
176 //Applet on desktop
177 if (icon && !icon->icon().isNull() && ((f != Plasma::Vertical && f != Plasma::Horizontal) ||
178 ((f == Plasma::Vertical && containmentSize.width() >= minimum.width()) ||
179 (f == Plasma::Horizontal && containmentSize.height() >= minimum.height())))) {
180 kDebug() << "we are expanding the popupapplet";
182 // we only switch to expanded if we aren't horiz/vert constrained and
183 // this applet has an icon.
184 // otherwise, we leave it up to the applet itself to figure it out
185 if (icon) {
186 icon->hide();
189 if (savedAspectRatio != Plasma::InvalidAspectRatioMode) {
190 q->setAspectRatioMode(savedAspectRatio);
193 if (dialog) {
194 if (dialog->layout() && qWidget) {
195 //we don't want to delete Widget inside the dialog layout
196 dialog->layout()->removeWidget(qWidget);
199 if (qWidget) {
200 qWidget->setParent(0);
203 delete dialog;
204 dialog = 0;
207 if (!lay) {
208 lay = new QGraphicsLinearLayout();
209 lay->setContentsMargins(0, 0, 0, 0);
210 lay->setSpacing(0);
211 lay->setOrientation(Qt::Horizontal);
212 q->setLayout(lay);
215 QSize prefSize;
217 if (gWidget) {
218 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
220 if (corona) {
221 corona->removeOffscreenWidget(gWidget);
224 lay->addItem(gWidget);
225 prefSize = gWidget->preferredSize().toSize();
226 } else if (qWidget) {
227 if (!proxy) {
228 proxy = new QGraphicsProxyWidget(q);
229 proxy->setWidget(qWidget);
230 proxy->show();
233 lay->addItem(proxy);
234 prefSize = qWidget->sizeHint();
237 //we could be on a big panel, but in that case we will be able to resize
238 //more than the natural minimum size, because we'll transform into an icon
239 if (f == Plasma::Horizontal) {
240 minimum.setHeight(0);
241 } else if (f == Plasma::Vertical) {
242 minimum.setWidth(0);
245 qreal left, top, right, bottom;
246 q->getContentsMargins(&left, &top, &right, &bottom);
247 QSizeF oldSize(q->size());
248 q->setMinimumSize(minimum + QSizeF(left+right, top+bottom));
249 //size not saved/invalid size saved
250 if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) {
251 q->resize(prefSize);
252 emit q->appletTransformedItself();
254 //Applet on popup
255 } else {
256 kDebug() << "about to switch to a popup";
257 //save the aspect ratio mode in case we drag'n drop in the Desktop later
258 savedAspectRatio = q->aspectRatioMode();
260 if (icon) {
261 icon->show();
262 q->setAspectRatioMode(Plasma::ConstrainedSquare);
265 if (proxy) {
266 proxy->setWidget(0); // prevent it from deleting our widget!
267 delete proxy;
268 proxy = 0;
271 if (!dialog) {
272 dialog = new Plasma::Dialog();
274 //no longer use Qt::Popup since that seems to cause a lot of problem when you drag
275 //stuff out of your Dialog (extenders). Monitor WindowDeactivate events so we can
276 //emulate the same kind of behavior as Qt::Popup (close when you click somewhere
277 //else.
278 Qt::WindowFlags wflags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
280 if (passive) {
281 wflags |= Qt::X11BypassWindowManagerHint;
284 dialog->setWindowFlags(wflags);
285 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
286 dialog->installEventFilter(q);
288 q->setMinimumSize(QSize(0, 0));
289 if (gWidget) {
290 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
292 //could that cast ever fail??
293 if (corona) {
294 corona->addOffscreenWidget(gWidget);
295 dialog->setGraphicsWidget(gWidget);
296 gWidget->resize(gWidget->preferredSize());
298 } else if (qWidget) {
299 QVBoxLayout *l_layout = new QVBoxLayout(dialog);
300 l_layout->setSpacing(0);
301 l_layout->setMargin(0);
302 l_layout->addWidget(qWidget);
303 dialog->adjustSize();
306 QObject::connect(dialog, SIGNAL(dialogResized()), q, SLOT(dialogSizeChanged()));
307 QObject::connect(dialog, SIGNAL(dialogVisible(bool)), q, SLOT(dialogStatusChanged(bool)));
310 if (icon && lay) {
311 lay->addItem(icon);
314 q->setMinimumSize(0,0);
319 void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event)
321 if (!d->icon && !d->popupLostFocus && event->buttons() == Qt::LeftButton) {
322 d->clicked = scenePos().toPoint();
323 event->setAccepted(true);
324 return;
325 } else {
326 d->popupLostFocus = false;
327 Applet::mousePressEvent(event);
331 void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
333 if (!d->icon &&
334 (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
335 d->internalTogglePopup();
336 } else {
337 Applet::mouseReleaseEvent(event);
341 bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
343 if (watched == d->dialog && (event->type() == QEvent::WindowDeactivate)) {
344 d->popupLostFocus = true;
345 hidePopup();
346 QTimer::singleShot(100, this, SLOT(clearPopupLostFocus()));
350 if (layout() && watched == graphicsWidget() && (event->type() == QEvent::GraphicsSceneResize)) {
351 //sizes are recalculated in the constraintsevent so let's just call that.
352 d->popupConstraintsEvent(Plasma::FormFactorConstraint);
354 //resize vertically if necesarry.
355 if (formFactor() == Plasma::MediaCenter || formFactor() == Plasma::Planar) {
356 resize(QSizeF(size().width(), minimumHeight()));
361 return Applet::eventFilter(watched, event);
364 //FIXME: some duplication between the drag events... maybe add some simple helper function?
365 void PopupApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
367 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
368 const ExtenderItemMimeData *mimeData =
369 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
370 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
371 event->accept();
372 showPopup();
377 void PopupApplet::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
379 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
380 const ExtenderItemMimeData *mimeData =
381 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
382 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
383 //We want to hide the popup if we're not moving onto the popup AND it is not the popup
384 //we started.
385 if (d->dialog && !d->dialog->geometry().contains(event->screenPos()) &&
386 mimeData->extenderItem()->extender() != qobject_cast<Extender*>(graphicsWidget())) {
387 //We actually try to hide the popup, with a call to showPopup, with a smal timeout,
388 //so if the user moves into the popup fast enough, it remains open (the extender
389 //will call showPopup which will cancel the timeout.
390 showPopup(250);
396 void PopupApplet::dropEvent(QGraphicsSceneDragDropEvent *event)
398 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
399 const ExtenderItemMimeData *mimeData =
400 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
401 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
402 mimeData->extenderItem()->setExtender(extender());
403 QApplication::restoreOverrideCursor();
408 void PopupApplet::showPopup(uint popupDuration)
410 if (d->dialog) {
411 // move the popup before its fist show, even if the show isn't triggered by
412 // a click, this should fix the first random position seen in some widgets
413 if (!d->dialog->isVisible()) {
414 d->internalTogglePopup();
417 if (d->timer) {
418 d->timer->stop();
421 if (popupDuration > 0) {
422 if (!d->timer) {
423 d->timer = new QTimer(this);
424 connect(d->timer, SIGNAL(timeout()), this, SLOT(hideTimedPopup()));
427 d->timer->start(popupDuration);
432 void PopupApplet::hidePopup()
434 if (d->dialog) {
435 d->dialog->hide();
439 void PopupApplet::togglePopup()
441 d->internalTogglePopup();
444 Plasma::PopupPlacement PopupApplet::popupPlacement() const
446 return d->popupPlacement;
449 void PopupApplet::popupEvent(bool)
453 void PopupApplet::setPassivePopup(bool passive)
455 d->passive = passive;
457 if (d->dialog) {
458 Qt::WindowFlags wflags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
460 if (d->passive) {
461 wflags |= Qt::X11BypassWindowManagerHint;
464 d->dialog->setWindowFlags(wflags);
468 bool PopupApplet::isPassivePopup() const
470 return d->passive;
473 bool PopupApplet::isPopupShowing() const
475 return d->dialog && d->dialog->isVisible();
478 PopupAppletPrivate::PopupAppletPrivate(PopupApplet *applet)
479 : q(applet),
480 icon(0),
481 dialog(0),
482 proxy(0),
483 popupPlacement(Plasma::FloatingPopup),
484 savedAspectRatio(Plasma::InvalidAspectRatioMode),
485 timer(0),
486 popupLostFocus(false),
487 passive(false)
491 PopupAppletPrivate::~PopupAppletPrivate()
493 if (proxy) {
494 proxy->setWidget(0);
497 delete dialog;
498 delete icon;
501 void PopupAppletPrivate::internalTogglePopup()
503 if (!dialog) {
504 return;
507 if (timer) {
508 timer->stop();
511 if (dialog->isVisible()) {
512 dialog->hide();
513 } else {
514 ToolTipManager::self()->hide(q);
515 updateDialogPosition();
516 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
517 dialog->show();
520 dialog->clearFocus();
523 void PopupAppletPrivate::hideTimedPopup()
525 timer->stop();
526 q->hidePopup();
529 void PopupAppletPrivate::clearPopupLostFocus()
531 popupLostFocus = false;
534 void PopupAppletPrivate::dialogSizeChanged()
536 //Reposition the dialog
537 if (dialog) {
538 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
539 KConfigGroup sizeGroup(mainGroup, "PopupApplet");
540 sizeGroup.writeEntry("DialogHeight", dialog->height());
541 sizeGroup.writeEntry("DialogWidth", dialog->width());
543 updateDialogPosition();
545 emit q->configNeedsSaving();
549 void PopupAppletPrivate::dialogStatusChanged(bool status)
551 q->popupEvent(status);
554 void PopupAppletPrivate::updateDialogPosition()
556 QGraphicsView *view = q->view();
558 if (!view) {
559 return;
562 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
563 KConfigGroup sizeGroup(mainGroup, "PopupApplet");
565 Q_ASSERT(q->containment());
566 Q_ASSERT(q->containment()->corona());
568 int preferredWidth = 0;
569 int preferredHeight = 0;
570 if (dialog->graphicsWidget()) {
571 preferredWidth = dialog->graphicsWidget()->preferredSize().width();
572 preferredHeight = dialog->graphicsWidget()->preferredSize().height();
575 const int width = qMin(sizeGroup.readEntry("DialogWidth", preferredWidth),
576 q->containment()->corona()->screenGeometry(-1).width() - 50);
577 const int height = qMin(sizeGroup.readEntry("DialogHeight", preferredHeight),
578 q->containment()->corona()->screenGeometry(-1).height() - 50);
580 QSize saved(width, height);
582 if (saved.isNull()) {
583 saved = dialog->sizeHint();
584 } else {
585 saved = saved.expandedTo(dialog->minimumSizeHint());
588 if (saved.width() != dialog->width() || saved.height() != dialog->height()) {
589 dialog->resize(saved);
592 QSize s = dialog->size();
593 QPoint pos = view->mapFromScene(q->scenePos());
595 //try to access a corona
596 Corona *corona = qobject_cast<Corona *>(q->scene());
597 if (corona) {
598 pos = corona->popupPosition(q, s);
601 bool reverse = false;
602 if (q->formFactor() == Plasma::Vertical) {
603 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).y() + q->size().height()/2 < pos.y() + dialog->size().width()/2) {
604 reverse = true;
606 } else {
607 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).x() + q->size().width()/2 < pos.x() + dialog->size().width()/2) {
608 reverse = true;
612 switch (q->location()) {
613 case BottomEdge:
614 if (pos.x() >= q->pos().x()) {
615 dialog->setResizeHandleCorners(Dialog::NorthEast);
616 } else {
617 dialog->setResizeHandleCorners(Dialog::NorthWest);
620 if (reverse) {
621 popupPlacement = Plasma::TopPosedLeftAlignedPopup;
622 } else {
623 popupPlacement = Plasma::TopPosedRightAlignedPopup;
625 break;
626 case TopEdge:
627 if (pos.x() >= q->pos().x()) {
628 dialog->setResizeHandleCorners(Dialog::SouthEast);
629 } else {
630 dialog->setResizeHandleCorners(Dialog::SouthWest);
633 if (reverse) {
634 popupPlacement = Plasma::BottomPosedLeftAlignedPopup;
635 } else {
636 popupPlacement = Plasma::BottomPosedRightAlignedPopup;
638 break;
639 case LeftEdge:
640 if (pos.y() >= q->pos().y()) {
641 dialog->setResizeHandleCorners(Dialog::SouthEast);
642 } else {
643 dialog->setResizeHandleCorners(Dialog::NorthEast);
646 if (reverse) {
647 popupPlacement = Plasma::RightPosedTopAlignedPopup;
648 } else {
649 popupPlacement = Plasma::RightPosedBottomAlignedPopup;
651 break;
653 case RightEdge:
654 if (pos.y() >= q->pos().y()) {
655 dialog->setResizeHandleCorners(Dialog::SouthWest);
656 } else {
657 dialog->setResizeHandleCorners(Dialog::NorthWest);
660 if (reverse) {
661 popupPlacement = Plasma::LeftPosedTopAlignedPopup;
662 } else {
663 popupPlacement = Plasma::LeftPosedBottomAlignedPopup;
665 break;
666 default:
667 dialog->setResizeHandleCorners(Dialog::NorthEast);
670 dialog->move(pos);
672 } // Plasma namespace
674 #include "popupapplet.moc"