• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

popupapplet.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2008 by Montel Laurent <montel@kde.org>
00003  * Copyright 2008 by Marco Martin <notmart@gmail.com>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2.1 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor,
00018  * Boston, MA  02110-1301  USA
00019  */
00020 
00021 #include "popupapplet.h"
00022 #include "private/popupapplet_p.h"
00023 
00024 #include <QGraphicsProxyWidget>
00025 #include <QGraphicsLinearLayout>
00026 #include <QVBoxLayout>
00027 #include <QTimer>
00028 #include <QApplication>
00029 
00030 #include <kicon.h>
00031 #include <kiconloader.h>
00032 #include <kwindowsystem.h>
00033 #include <kglobalsettings.h>
00034 
00035 #include "plasma/private/applet_p.h"
00036 #include "plasma/corona.h"
00037 #include "plasma/containment.h"
00038 #include "plasma/dialog.h"
00039 #include "plasma/extender.h"
00040 #include "plasma/tooltipmanager.h"
00041 #include "plasma/widgets/iconwidget.h"
00042 
00043 namespace Plasma
00044 {
00045 
00046 PopupApplet::PopupApplet(QObject *parent, const QVariantList &args)
00047     : Plasma::Applet(parent, args),
00048       d(new PopupAppletPrivate(this))
00049 {
00050     int iconSize = IconSize(KIconLoader::Desktop);
00051     resize(iconSize, iconSize);
00052     connect(this, SIGNAL(activate()), this, SLOT(internalTogglePopup()));
00053 }
00054 
00055 PopupApplet::~PopupApplet()
00056 {
00057     delete widget();
00058     delete d;
00059 }
00060 
00061 void PopupApplet::setPopupIcon(const QIcon &icon)
00062 {
00063     if (icon.isNull()) {
00064         if (d->icon) {
00065             delete d->icon;
00066             d->icon = 0;
00067             setLayout(0);
00068             setAspectRatioMode(d->savedAspectRatio);
00069         }
00070 
00071         return;
00072     }
00073 
00074     if (!d->icon) {
00075         d->icon = new Plasma::IconWidget(icon, QString(), this);
00076         connect(d->icon, SIGNAL(clicked()), this, SLOT(internalTogglePopup()));
00077 
00078         QGraphicsLinearLayout *layout = new QGraphicsLinearLayout();
00079         layout->setContentsMargins(0, 0, 0, 0);
00080         layout->setSpacing(0);
00081         layout->setOrientation(Qt::Horizontal);
00082 
00083         if (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal ) {
00084             d->savedAspectRatio = aspectRatioMode();
00085             setAspectRatioMode(Plasma::ConstrainedSquare);
00086         }
00087 
00088         setLayout(layout);
00089     } else {
00090         d->icon->setIcon(icon);
00091     }
00092 }
00093 
00094 void PopupApplet::setPopupIcon(const QString &iconName)
00095 {
00096     setPopupIcon(KIcon(iconName));
00097 }
00098 
00099 QIcon PopupApplet::popupIcon() const
00100 {
00101     return d->icon ? d->icon->icon() : QIcon();
00102 }
00103 
00104 QWidget *PopupApplet::widget()
00105 {
00106     return 0;
00107 }
00108 
00109 QGraphicsWidget *PopupApplet::graphicsWidget()
00110 {
00111     return static_cast<Applet*>(this)->d->extender;
00112 }
00113 
00114 void PopupAppletPrivate::checkExtenderAppearance(Plasma::FormFactor f)
00115 {
00116     Extender *extender = qobject_cast<Extender*>(q->graphicsWidget());
00117     if (extender) {
00118         if (f != Plasma::Horizontal && f != Plasma::Vertical) {
00119             extender->setAppearance(Extender::NoBorders);
00120         } else if (q->location() == TopEdge) {
00121             extender->setAppearance(Extender::TopDownStacked);
00122         } else {
00123             extender->setAppearance(Extender::BottomUpStacked);
00124         }
00125 
00126         if (dialog) {
00127             dialog->setGraphicsWidget(extender);
00128         }
00129     }
00130 }
00131 
00132 void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints)
00133 {
00134     if (constraints & Plasma::StartupCompletedConstraint) {
00135         startupComplete = true;
00136     }
00137 
00138     if (!startupComplete) {
00139         return;
00140     }
00141 
00142     Plasma::FormFactor f = q->formFactor();
00143 
00144     if (constraints & Plasma::LocationConstraint) {
00145         checkExtenderAppearance(f);
00146     }
00147 
00148     if (constraints & Plasma::FormFactorConstraint ||
00149         constraints & Plasma::StartupCompletedConstraint ||
00150         (constraints & Plasma::SizeConstraint &&
00151          (f == Plasma::Vertical || f == Plasma::Horizontal))) {
00152         QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
00153 
00154         if (icon && !icon->icon().isNull() && lay) {
00155             lay->removeAt(0);
00156         }
00157 
00158         QSizeF minimum;
00159         QSizeF containmentSize;
00160 
00161         QGraphicsWidget *gWidget = q->graphicsWidget();
00162         kDebug() << "graphics widget is" << (QObject*)gWidget;
00163         QWidget *qWidget = q->widget();
00164 
00165         if (gWidget) {
00166             minimum = gWidget->minimumSize();
00167             // our layout may have been replaced on us in the call to graphicsWidget!
00168             lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
00169 
00170             if (!(constraints & LocationConstraint)) {
00171                 checkExtenderAppearance(f);
00172             }
00173         } else if (qWidget) {
00174             minimum = qWidget->minimumSizeHint();
00175         }
00176 
00177         if (q->containment()) {
00178             containmentSize = q->containment()->size();
00179         }
00180 
00181         //Applet on desktop
00182         if (icon && !icon->icon().isNull() && ((f != Plasma::Vertical && f != Plasma::Horizontal) ||
00183             ((f == Plasma::Vertical && containmentSize.width() >= minimum.width()) ||
00184              (f == Plasma::Horizontal && containmentSize.height() >= minimum.height())))) {
00185             kDebug() << "we are expanding the popupapplet";
00186 
00187             // we only switch to expanded if we aren't horiz/vert constrained and
00188             // this applet has an icon.
00189             // otherwise, we leave it up to the applet itself to figure it out
00190             if (icon) {
00191                 icon->hide();
00192             }
00193 
00194             if (savedAspectRatio != Plasma::InvalidAspectRatioMode) {
00195                 q->setAspectRatioMode(savedAspectRatio);
00196             }
00197 
00198             if (dialog) {
00199                 if (dialog->layout() && qWidget) {
00200                     //we don't want to delete Widget inside the dialog layout
00201                     dialog->layout()->removeWidget(qWidget);
00202                 }
00203 
00204                 if (qWidget) {
00205                     qWidget->setParent(0);
00206                 }
00207 
00208                 delete dialog;
00209                 dialog = 0;
00210             }
00211 
00212             if (!lay) {
00213                 lay = new QGraphicsLinearLayout();
00214                 lay->setContentsMargins(0, 0, 0, 0);
00215                 lay->setSpacing(0);
00216                 lay->setOrientation(Qt::Horizontal);
00217                 q->setLayout(lay);
00218             }
00219 
00220             QSize prefSize;
00221 
00222             if (gWidget) {
00223                 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
00224 
00225                 if (corona) {
00226                     corona->removeOffscreenWidget(gWidget);
00227                 }
00228 
00229                 lay->addItem(gWidget);
00230                 prefSize = gWidget->preferredSize().toSize();
00231             } else if (qWidget) {
00232                 if (!proxy) {
00233                     proxy = new QGraphicsProxyWidget(q);
00234                     proxy->setWidget(qWidget);
00235                     proxy->show();
00236                 }
00237 
00238                 lay->addItem(proxy);
00239                 prefSize = qWidget->sizeHint();
00240             }
00241 
00242             //we could be on a big panel, but in that case we will be able to resize
00243             //more than the natural minimum size, because we'll transform into an icon
00244             if (f == Plasma::Horizontal) {
00245                 minimum.setHeight(0);
00246             } else if (f == Plasma::Vertical) {
00247                 minimum.setWidth(0);
00248             }
00249 
00250             qreal left, top, right, bottom;
00251             q->getContentsMargins(&left, &top, &right, &bottom);
00252             QSizeF oldSize(q->size());
00253             q->setMinimumSize(minimum + QSizeF(left+right, top+bottom));
00254             //size not saved/invalid size saved
00255             if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) {
00256                 q->resize(prefSize);
00257             }
00258 
00259             //FIXME: this will be automatically propagated by the qgraphicslayout in the future
00260             lay->setPreferredSize(prefSize);
00261         //Applet on popup
00262         } else {
00263             kDebug() << "about to switch to a popup";
00264             //save the aspect ratio mode in case we drag'n drop in the Desktop later
00265             savedAspectRatio = q->aspectRatioMode();
00266 
00267             if (icon) {
00268                 icon->show();
00269                 q->setAspectRatioMode(Plasma::ConstrainedSquare);
00270             }
00271 
00272             if (proxy) {
00273                 proxy->setWidget(0); // prevent it from deleting our widget!
00274                 delete proxy;
00275                 proxy = 0;
00276             }
00277 
00278             if (!dialog) {
00279                 dialog = new Plasma::Dialog();
00280 
00281                 //no longer use Qt::Popup since that seems to cause a lot of problem when you drag
00282                 //stuff out of your Dialog (extenders). Monitor WindowDeactivate events so we can
00283                 //emulate the same kind of behavior as Qt::Popup (close when you click somewhere
00284                 //else.
00285                 Qt::WindowFlags wflags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
00286 
00287                 if (passive) {
00288                     wflags |= Qt::X11BypassWindowManagerHint;
00289                 }
00290 
00291                 dialog->setWindowFlags(wflags);
00292                 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
00293                 dialog->installEventFilter(q);
00294 
00295                 q->setMinimumSize(QSize(0, 0));
00296                 if (gWidget) {
00297                     Corona *corona = qobject_cast<Corona *>(gWidget->scene());
00298 
00299                     //could that cast ever fail??
00300                     if (corona) {
00301                         corona->addOffscreenWidget(gWidget);
00302                         dialog->setGraphicsWidget(gWidget);
00303                     }
00304                 } else if (qWidget) {
00305                     QVBoxLayout *l_layout = new QVBoxLayout(dialog);
00306                     l_layout->setSpacing(0);
00307                     l_layout->setMargin(0);
00308                     l_layout->addWidget(qWidget);
00309                     dialog->adjustSize();
00310                 }
00311 
00312                 QObject::connect(dialog, SIGNAL(dialogResized()), q, SLOT(dialogSizeChanged()));
00313                 QObject::connect(dialog, SIGNAL(dialogVisible(bool)), q, SLOT(dialogStatusChanged(bool)));
00314             }
00315 
00316             if (icon && lay) {
00317                 lay->addItem(icon);
00318             }
00319 
00320             q->setMinimumSize(0,0);
00321         }
00322     }
00323 }
00324 
00325 void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event)
00326 {
00327     if (!d->icon && !d->popupLostFocus && event->buttons() == Qt::LeftButton) {
00328         d->clicked = scenePos().toPoint();
00329         event->setAccepted(true);
00330         return;
00331     } else {
00332         d->popupLostFocus = false;
00333         Applet::mousePressEvent(event);
00334     }
00335 }
00336 
00337 void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00338 {
00339     if (!d->icon &&
00340         (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
00341         d->internalTogglePopup();
00342     } else {
00343         Applet::mouseReleaseEvent(event);
00344     }
00345 }
00346 
00347 bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
00348 {
00349     if (watched == d->dialog && (event->type() == QEvent::WindowDeactivate)) {
00350         d->popupLostFocus = true;
00351         hidePopup();
00352         QTimer::singleShot(100, this, SLOT(clearPopupLostFocus()));
00353     }
00354 
00367     return Applet::eventFilter(watched, event);
00368 }
00369 
00370 void PopupApplet::showPopup(uint popupDuration)
00371 {
00372     if (d->dialog) {
00373         // move the popup before its fist show, even if the show isn't triggered by
00374         // a click, this should fix the first random position seen in some widgets
00375         if (!d->dialog->isVisible()) {
00376             d->internalTogglePopup();
00377         }
00378 
00379         if (d->timer) {
00380             d->timer->stop();
00381         }
00382 
00383         if (popupDuration > 0) {
00384             if (!d->timer) {
00385                 d->timer = new QTimer(this);
00386                 connect(d->timer, SIGNAL(timeout()), this, SLOT(hideTimedPopup()));
00387             }
00388 
00389             d->timer->start(popupDuration);
00390         }
00391     }
00392 }
00393 
00394 void PopupApplet::hidePopup()
00395 {
00396     if (d->dialog) {
00397         d->dialog->hide();
00398     }
00399 }
00400 
00401 void PopupApplet::togglePopup()
00402 {
00403     d->internalTogglePopup();
00404 }
00405 
00406 Plasma::PopupPlacement PopupApplet::popupPlacement() const
00407 {
00408     return d->popupPlacement;
00409 }
00410 
00411 void PopupApplet::popupEvent(bool)
00412 {
00413 }
00414 
00415 void PopupApplet::setPassivePopup(bool passive)
00416 {
00417     d->passive = passive;
00418 
00419     if (d->dialog) {
00420         Qt::WindowFlags wflags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint;
00421 
00422         if (d->passive) {
00423             wflags |= Qt::X11BypassWindowManagerHint;
00424         }
00425 
00426         d->dialog->setWindowFlags(wflags);
00427     }
00428 }
00429 
00430 bool PopupApplet::isPassivePopup() const
00431 {
00432     return d->passive;
00433 }
00434 
00435 bool PopupApplet::isPopupShowing() const
00436 {
00437     return !d->dialog || d->dialog->isVisible();
00438 }
00439 
00440 PopupAppletPrivate::PopupAppletPrivate(PopupApplet *applet)
00441         : q(applet),
00442           icon(0),
00443           dialog(0),
00444           proxy(0),
00445           popupPlacement(Plasma::FloatingPopup),
00446           savedAspectRatio(Plasma::InvalidAspectRatioMode),
00447           timer(0),
00448           startupComplete(false),
00449           popupLostFocus(false),
00450           passive(false)
00451 {
00452 }
00453 
00454 PopupAppletPrivate::~PopupAppletPrivate()
00455 {
00456     if (proxy) {
00457         proxy->setWidget(0);
00458     }
00459 
00460     delete dialog;
00461     delete icon;
00462 }
00463 
00464 void PopupAppletPrivate::internalTogglePopup()
00465 {
00466     if (!dialog) {
00467         return;
00468     }
00469 
00470     if (timer) {
00471         timer->stop();
00472     }
00473 
00474     if (dialog->isVisible()) {
00475         dialog->hide();
00476     } else {
00477         ToolTipManager::self()->hide(q);
00478         updateDialogPosition();
00479         KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
00480         dialog->show();
00481         dialog->resize(dialog->size());
00482     }
00483 
00484     dialog->clearFocus();
00485 }
00486 
00487 void PopupAppletPrivate::hideTimedPopup()
00488 {
00489     timer->stop();
00490     q->hidePopup();
00491 }
00492 
00493 void PopupAppletPrivate::clearPopupLostFocus()
00494 {
00495     popupLostFocus = false;
00496 }
00497 
00498 void PopupAppletPrivate::dialogSizeChanged()
00499 {
00500     //Reposition the dialog
00501     if (dialog) {
00502         KConfigGroup sizeGroup = q->config();
00503         sizeGroup = KConfigGroup(&sizeGroup, "PopupApplet");
00504         sizeGroup.writeEntry("DialogHeight", dialog->height());
00505         sizeGroup.writeEntry("DialogWidth", dialog->width());
00506 
00507         updateDialogPosition();
00508 
00509         emit q->configNeedsSaving();
00510     }
00511 }
00512 
00513 void PopupAppletPrivate::dialogStatusChanged(bool status)
00514 {
00515     q->popupEvent(status);
00516 }
00517 
00518 void PopupAppletPrivate::updateDialogPosition()
00519 {
00520     QGraphicsView *view = q->view();
00521 
00522     if (!view) {
00523         return;
00524     }
00525 
00526     KConfigGroup sizeGroup = q->config();
00527     sizeGroup = KConfigGroup(&sizeGroup, "PopupApplet");
00528 
00529     Q_ASSERT(q->containment());
00530     Q_ASSERT(q->containment()->corona());
00531 
00532     int preferredWidth = 0;
00533     int preferredHeight = 0;
00534     if (dialog->graphicsWidget()) {
00535         preferredWidth = dialog->graphicsWidget()->preferredSize().width();
00536         preferredHeight = dialog->graphicsWidget()->preferredSize().height();
00537     }
00538 
00539     const int width = qMin(sizeGroup.readEntry("DialogWidth", preferredWidth),
00540                            q->containment()->corona()->screenGeometry(-1).width() - 50);
00541     const int height = qMin(sizeGroup.readEntry("DialogHeight", preferredHeight),
00542                             q->containment()->corona()->screenGeometry(-1).height() - 50);
00543 
00544     QSize saved(width, height);
00545 
00546     if (saved.isNull()) {
00547         dialog->adjustSize();
00548     } else {
00549         saved = saved.expandedTo(dialog->minimumSizeHint());
00550         dialog->resize(saved);
00551     }
00552 
00553     QSize s = dialog->size();
00554     QPoint pos = view->mapFromScene(q->scenePos());
00555     //try to access a corona
00556     if (q->containment() && q->containment()->corona()) {
00557         pos = q->containment()->corona()->popupPosition(q, s);
00558     } else {
00559         Corona *corona = qobject_cast<Corona *>(q->scene());
00560         if (corona) {
00561             pos = corona->popupPosition(q, s);
00562         }
00563     }
00564 
00565     bool reverse = false;
00566     if (q->formFactor() == Plasma::Vertical) {
00567         if (view->mapToGlobal(view->mapFromScene(q->scenePos())).y() + q->size().height()/2 < pos.y() + dialog->size().width()/2) {
00568             reverse = true;
00569         }
00570     } else {
00571         if (view->mapToGlobal(view->mapFromScene(q->scenePos())).x() + q->size().width()/2 < pos.x() + dialog->size().width()/2) {
00572             reverse = true;
00573         }
00574     }
00575 
00576     switch (q->location()) {
00577     case BottomEdge:
00578         if (pos.x() >= q->pos().x()) {
00579             dialog->setResizeHandleCorners(Dialog::NorthEast);
00580         } else {
00581             dialog->setResizeHandleCorners(Dialog::NorthWest);
00582         }
00583 
00584         if (reverse) {
00585             popupPlacement = Plasma::TopPosedLeftAlignedPopup;
00586         } else {
00587             popupPlacement = Plasma::TopPosedRightAlignedPopup;
00588         }
00589         break;
00590     case TopEdge:
00591         if (pos.x() >= q->pos().x()) {
00592             dialog->setResizeHandleCorners(Dialog::SouthEast);
00593         } else {
00594             dialog->setResizeHandleCorners(Dialog::SouthWest);
00595         }
00596 
00597         if (reverse) {
00598             popupPlacement = Plasma::BottomPosedLeftAlignedPopup;
00599         } else {
00600             popupPlacement = Plasma::BottomPosedRightAlignedPopup;
00601         }
00602         break;
00603     case LeftEdge:
00604         if (pos.y() >= q->pos().y()) {
00605             dialog->setResizeHandleCorners(Dialog::SouthEast);
00606         } else {
00607             dialog->setResizeHandleCorners(Dialog::NorthEast);
00608         }
00609 
00610         if (reverse) {
00611             popupPlacement = Plasma::RightPosedTopAlignedPopup;
00612         } else {
00613             popupPlacement = Plasma::RightPosedBottomAlignedPopup;
00614         }
00615         break;
00616 
00617     case RightEdge:
00618         if (pos.y() >= q->pos().y()) {
00619             dialog->setResizeHandleCorners(Dialog::SouthWest);
00620         } else {
00621             dialog->setResizeHandleCorners(Dialog::NorthWest);
00622         }
00623 
00624         if (reverse) {
00625             popupPlacement = Plasma::LeftPosedTopAlignedPopup;
00626         } else {
00627             popupPlacement = Plasma::LeftPosedBottomAlignedPopup;
00628         }
00629         break;
00630     default:
00631         dialog->setResizeHandleCorners(Dialog::NorthEast);
00632     }
00633 
00634     dialog->move(pos);
00635 }
00636 } // Plasma namespace
00637 
00638 #include "popupapplet.moc"
00639 

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal