00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "popupapplet.h"
00022 #include "private/popupapplet_p.h"
00023
00024 #include <QApplication>
00025 #include <QGraphicsProxyWidget>
00026 #include <QGraphicsLinearLayout>
00027 #include <QTimer>
00028 #include <QVBoxLayout>
00029
00030 #ifdef Q_WS_X11
00031 #include <QX11Info>
00032 #endif
00033
00034 #include <kicon.h>
00035 #include <kiconloader.h>
00036 #include <kwindowsystem.h>
00037 #include <kglobalsettings.h>
00038 #include <netwm.h>
00039
00040 #include "plasma/private/applet_p.h"
00041 #include "plasma/private/extenderitemmimedata_p.h"
00042 #include "plasma/corona.h"
00043 #include "plasma/containment.h"
00044 #include "plasma/dialog.h"
00045 #include "plasma/extender.h"
00046 #include "plasma/extenderitem.h"
00047 #include "plasma/tooltipmanager.h"
00048 #include "plasma/widgets/iconwidget.h"
00049
00050 namespace Plasma
00051 {
00052
00053 PopupApplet::PopupApplet(QObject *parent, const QVariantList &args)
00054 : Plasma::Applet(parent, args),
00055 d(new PopupAppletPrivate(this))
00056 {
00057 int iconSize = IconSize(KIconLoader::Desktop);
00058 resize(iconSize, iconSize);
00059 disconnect(this, SIGNAL(activate()), (Applet*)this, SLOT(setFocus()));
00060 connect(this, SIGNAL(activate()), this, SLOT(internalTogglePopup()));
00061 setAcceptDrops(true);
00062 }
00063
00064 PopupApplet::~PopupApplet()
00065 {
00066 delete widget();
00067 delete d;
00068 }
00069
00070 void PopupApplet::setPopupIcon(const QIcon &icon)
00071 {
00072 if (icon.isNull()) {
00073 if (d->icon) {
00074 delete d->icon;
00075 d->icon = 0;
00076 setLayout(0);
00077 setAspectRatioMode(d->savedAspectRatio);
00078 }
00079
00080 return;
00081 }
00082
00083 if (!d->icon) {
00084 d->icon = new Plasma::IconWidget(icon, QString(), this);
00085 connect(d->icon, SIGNAL(clicked()), this, SLOT(internalTogglePopup()));
00086
00087 QGraphicsLinearLayout *layout = new QGraphicsLinearLayout();
00088 layout->setContentsMargins(0, 0, 0, 0);
00089 layout->setSpacing(0);
00090 layout->setOrientation(Qt::Horizontal);
00091
00092 if (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal ) {
00093 d->savedAspectRatio = aspectRatioMode();
00094 setAspectRatioMode(Plasma::ConstrainedSquare);
00095 }
00096
00097 setLayout(layout);
00098 } else {
00099 d->icon->setIcon(icon);
00100 }
00101 }
00102
00103 void PopupApplet::setPopupIcon(const QString &iconName)
00104 {
00105 setPopupIcon(KIcon(iconName));
00106 }
00107
00108 QIcon PopupApplet::popupIcon() const
00109 {
00110 return d->icon ? d->icon->icon() : QIcon();
00111 }
00112
00113 QWidget *PopupApplet::widget()
00114 {
00115 return d->widget;
00116 }
00117
00118 void PopupApplet::setWidget(QWidget * widget)
00119 {
00120 d->widget = widget;
00121 }
00122
00123 QGraphicsWidget *PopupApplet::graphicsWidget()
00124 {
00125 if (d->graphicsWidget != 0) {
00126 return d->graphicsWidget;
00127 } else {
00128 return static_cast<Applet*>(this)->d->extender;
00129 }
00130 }
00131
00132 void PopupApplet::setGraphicsWidget(QGraphicsWidget * graphicsWidget)
00133 {
00134 d->graphicsWidget = graphicsWidget;
00135 }
00136
00137 void PopupAppletPrivate::checkExtenderAppearance(Plasma::FormFactor f)
00138 {
00139 Extender *extender = qobject_cast<Extender*>(q->graphicsWidget());
00140 if (extender) {
00141 if (f != Plasma::Horizontal && f != Plasma::Vertical) {
00142 extender->setAppearance(Extender::NoBorders);
00143 } else if (q->location() == TopEdge) {
00144 extender->setAppearance(Extender::TopDownStacked);
00145 } else {
00146 extender->setAppearance(Extender::BottomUpStacked);
00147 }
00148
00149 if (dialog) {
00150 dialog->setGraphicsWidget(extender);
00151 }
00152 }
00153 }
00154
00155 void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints)
00156 {
00157 Plasma::FormFactor f = q->formFactor();
00158
00159 if (constraints & Plasma::LocationConstraint) {
00160 checkExtenderAppearance(f);
00161 }
00162
00163 if (constraints & Plasma::FormFactorConstraint ||
00164 constraints & Plasma::StartupCompletedConstraint ||
00165 (constraints & Plasma::SizeConstraint &&
00166 (f == Plasma::Vertical || f == Plasma::Horizontal))) {
00167 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
00168
00169 if (icon && !icon->icon().isNull() && lay) {
00170 lay->removeAt(0);
00171 }
00172
00173 QSizeF minimum;
00174 QSizeF containmentSize;
00175
00176 QGraphicsWidget *gWidget = q->graphicsWidget();
00177
00178 QWidget *qWidget = q->widget();
00179
00180 if (gWidget) {
00181 minimum = gWidget->minimumSize();
00182
00183 lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout());
00184
00185 if (!(constraints & LocationConstraint)) {
00186 checkExtenderAppearance(f);
00187 }
00188 } else if (qWidget) {
00189 minimum = qWidget->minimumSizeHint();
00190 }
00191
00192 if (q->containment()) {
00193 containmentSize = q->containment()->size();
00194 }
00195
00196
00197 if (icon && !icon->icon().isNull() && ((f != Plasma::Vertical && f != Plasma::Horizontal) ||
00198 ((f == Plasma::Vertical && containmentSize.width() >= minimum.width()) ||
00199 (f == Plasma::Horizontal && containmentSize.height() >= minimum.height())))) {
00200
00201
00202
00203
00204
00205 if (icon) {
00206 icon->hide();
00207 }
00208
00209 if (savedAspectRatio != Plasma::InvalidAspectRatioMode) {
00210 q->setAspectRatioMode(savedAspectRatio);
00211 }
00212
00213 if (dialog) {
00214 if (dialog->layout() && qWidget) {
00215
00216 dialog->layout()->removeWidget(qWidget);
00217 }
00218
00219 if (qWidget) {
00220 qWidget->setParent(0);
00221 }
00222
00223 delete dialog;
00224 dialog = 0;
00225 }
00226
00227 if (!lay) {
00228 lay = new QGraphicsLinearLayout();
00229 lay->setContentsMargins(0, 0, 0, 0);
00230 lay->setSpacing(0);
00231 lay->setOrientation(Qt::Horizontal);
00232 q->setLayout(lay);
00233 }
00234
00235 QSize prefSize;
00236
00237 if (gWidget) {
00238 if (proxy) {
00239 proxy->setWidget(0);
00240 delete proxy;
00241 proxy = 0;
00242 }
00243
00244 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
00245
00246 if (corona) {
00247 corona->removeOffscreenWidget(gWidget);
00248 }
00249
00250 lay->addItem(gWidget);
00251 prefSize = gWidget->preferredSize().toSize();
00252 } else if (qWidget) {
00253 if (!proxy) {
00254 proxy = new QGraphicsProxyWidget(q);
00255 proxy->setWidget(qWidget);
00256 proxy->show();
00257 }
00258
00259 lay->addItem(proxy);
00260 prefSize = qWidget->sizeHint();
00261 }
00262
00263
00264
00265 if (f == Plasma::Horizontal) {
00266 minimum.setHeight(0);
00267 } else if (f == Plasma::Vertical) {
00268 minimum.setWidth(0);
00269 }
00270
00271 qreal left, top, right, bottom;
00272 q->getContentsMargins(&left, &top, &right, &bottom);
00273 QSizeF oldSize(q->size());
00274 q->setMinimumSize(minimum + QSizeF(left+right, top+bottom));
00275
00276 if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) {
00277 q->resize(prefSize);
00278 emit q->appletTransformedItself();
00279 }
00280
00281 } else {
00282
00283
00284 savedAspectRatio = q->aspectRatioMode();
00285
00286 if (icon) {
00287 icon->show();
00288 q->setAspectRatioMode(Plasma::ConstrainedSquare);
00289 }
00290
00291 if (proxy) {
00292 proxy->setWidget(0);
00293 delete proxy;
00294 proxy = 0;
00295 }
00296
00297 if (!dialog) {
00298 dialog = new Plasma::Dialog();
00299
00300
00301
00302
00303
00304
00305 q->setMinimumSize(QSize(0, 0));
00306 if (gWidget) {
00307 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
00308
00309
00310 if (corona) {
00311 corona->addOffscreenWidget(gWidget);
00312 dialog->setGraphicsWidget(gWidget);
00313 gWidget->resize(gWidget->preferredSize());
00314 }
00315
00316 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (gWidget->windowFlags() & Qt::X11BypassWindowManagerHint));
00317 } else if (qWidget) {
00318 QVBoxLayout *l_layout = new QVBoxLayout(dialog);
00319 l_layout->setSpacing(0);
00320 l_layout->setMargin(0);
00321 l_layout->addWidget(qWidget);
00322 dialog->adjustSize();
00323 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (qWidget->windowFlags() & Qt::X11BypassWindowManagerHint));
00324 } else {
00325 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
00326 }
00327
00328 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
00329 dialog->installEventFilter(q);
00330
00331 QObject::connect(dialog, SIGNAL(dialogResized()), q, SLOT(dialogSizeChanged()));
00332 QObject::connect(dialog, SIGNAL(dialogVisible(bool)), q, SLOT(dialogStatusChanged(bool)));
00333 }
00334
00335 if (icon && lay) {
00336 lay->addItem(icon);
00337 }
00338
00339 q->setMinimumSize(0,0);
00340 }
00341 }
00342 emit q->sizeHintChanged(Qt::PreferredSize);
00343 }
00344
00345 void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event)
00346 {
00347 if (!d->icon && !d->popupLostFocus && event->buttons() == Qt::LeftButton) {
00348 d->clicked = scenePos().toPoint();
00349 event->setAccepted(true);
00350 return;
00351 } else {
00352 d->popupLostFocus = false;
00353 Applet::mousePressEvent(event);
00354 }
00355 }
00356
00357 void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00358 {
00359 if (!d->icon &&
00360 (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
00361 d->internalTogglePopup();
00362 } else {
00363 Applet::mouseReleaseEvent(event);
00364 }
00365 }
00366
00367 bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
00368 {
00369 if (!d->passive && watched == d->dialog && (event->type() == QEvent::WindowDeactivate)) {
00370 d->popupLostFocus = true;
00371 hidePopup();
00372 QTimer::singleShot(100, this, SLOT(clearPopupLostFocus()));
00373 }
00374
00387 return Applet::eventFilter(watched, event);
00388 }
00389
00390
00391 void PopupApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00392 {
00393 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00394 const ExtenderItemMimeData *mimeData =
00395 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00396 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
00397 event->accept();
00398 showPopup();
00399 }
00400 }
00401 }
00402
00403 void PopupApplet::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
00404 {
00405 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00406 const ExtenderItemMimeData *mimeData =
00407 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00408 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
00409
00410
00411 if (d->dialog && !d->dialog->geometry().contains(event->screenPos()) &&
00412 mimeData->extenderItem()->extender() != qobject_cast<Extender*>(graphicsWidget())) {
00413
00414
00415
00416 showPopup(250);
00417 }
00418 }
00419 }
00420 }
00421
00422 void PopupApplet::dropEvent(QGraphicsSceneDragDropEvent *event)
00423 {
00424 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00425 const ExtenderItemMimeData *mimeData =
00426 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00427 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
00428 mimeData->extenderItem()->setExtender(extender());
00429 QApplication::restoreOverrideCursor();
00430 }
00431 }
00432 }
00433
00434 void PopupApplet::showPopup(uint popupDuration)
00435 {
00436 if (d->dialog) {
00437
00438
00439 if (!d->dialog->isVisible()) {
00440 d->internalTogglePopup();
00441 }
00442
00443 if (d->timer) {
00444 d->timer->stop();
00445 }
00446
00447 if (popupDuration > 0) {
00448 if (!d->timer) {
00449 d->timer = new QTimer(this);
00450 connect(d->timer, SIGNAL(timeout()), this, SLOT(hideTimedPopup()));
00451 }
00452
00453 d->timer->start(popupDuration);
00454 }
00455 }
00456 }
00457
00458 void PopupApplet::hidePopup()
00459 {
00460 if (d->dialog) {
00461 if (location() != Floating) {
00462 d->dialog->animatedHide(locationToInverseDirection(location()));
00463 } else {
00464 d->dialog->hide();
00465 }
00466 }
00467 }
00468
00469 void PopupApplet::togglePopup()
00470 {
00471 d->internalTogglePopup();
00472 }
00473
00474 Plasma::PopupPlacement PopupApplet::popupPlacement() const
00475 {
00476 return d->popupPlacement;
00477 }
00478
00479 void PopupApplet::popupEvent(bool)
00480 {
00481 }
00482
00483 void PopupApplet::setPassivePopup(bool passive)
00484 {
00485 d->passive = passive;
00486 }
00487
00488 bool PopupApplet::isPassivePopup() const
00489 {
00490 return d->passive;
00491 }
00492
00493 bool PopupApplet::isPopupShowing() const
00494 {
00495 return d->dialog && d->dialog->isVisible();
00496 }
00497
00498 PopupAppletPrivate::PopupAppletPrivate(PopupApplet *applet)
00499 : q(applet),
00500 icon(0),
00501 dialog(0),
00502 proxy(0),
00503 widget(0),
00504 graphicsWidget(0),
00505 popupPlacement(Plasma::FloatingPopup),
00506 savedAspectRatio(Plasma::InvalidAspectRatioMode),
00507 timer(0),
00508 popupLostFocus(false),
00509 passive(false)
00510 {
00511 }
00512
00513 PopupAppletPrivate::~PopupAppletPrivate()
00514 {
00515 if (proxy) {
00516 proxy->setWidget(0);
00517 }
00518
00519 delete dialog;
00520 delete icon;
00521 }
00522
00523 void PopupAppletPrivate::internalTogglePopup()
00524 {
00525 if (!dialog) {
00526 q->setFocus(Qt::ShortcutFocusReason);
00527 return;
00528 }
00529
00530 if (!q->view()) {
00531 return;
00532 }
00533
00534 if (timer) {
00535 timer->stop();
00536 }
00537
00538 if (dialog->isVisible()) {
00539 if (q->location() != Floating) {
00540 dialog->animatedHide(locationToInverseDirection(q->location()));
00541 } else {
00542 dialog->hide();
00543 }
00544
00545 dialog->clearFocus();
00546 } else {
00547 if (q->graphicsWidget() &&
00548 q->graphicsWidget() == static_cast<Applet*>(q)->d->extender &&
00549 static_cast<Applet*>(q)->d->extender->isEmpty()) {
00550
00551 return;
00552 }
00553
00554 ToolTipManager::self()->hide(q);
00555 updateDialogPosition();
00556
00557 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
00558
00578 if (q->location() != Floating) {
00579 dialog->animatedShow(locationToDirection(q->location()));
00580 } else {
00581 dialog->show();
00582 }
00583
00584 if (!(dialog->windowFlags() & Qt::X11BypassWindowManagerHint)) {
00585 KWindowSystem::activateWindow(dialog->winId());
00586 }
00587 }
00588 }
00589
00590 void PopupAppletPrivate::hideTimedPopup()
00591 {
00592 timer->stop();
00593 q->hidePopup();
00594 }
00595
00596 void PopupAppletPrivate::clearPopupLostFocus()
00597 {
00598 popupLostFocus = false;
00599 }
00600
00601 void PopupAppletPrivate::dialogSizeChanged()
00602 {
00603
00604 if (dialog) {
00605 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
00606 KConfigGroup sizeGroup(mainGroup, "PopupApplet");
00607 sizeGroup.writeEntry("DialogHeight", dialog->height());
00608 sizeGroup.writeEntry("DialogWidth", dialog->width());
00609
00610 updateDialogPosition();
00611
00612 emit q->configNeedsSaving();
00613 }
00614 }
00615
00616 void PopupAppletPrivate::dialogStatusChanged(bool status)
00617 {
00618 q->popupEvent(status);
00619 }
00620
00621 void PopupAppletPrivate::updateDialogPosition()
00622 {
00623 if (!dialog) {
00624 return;
00625 }
00626
00627 QGraphicsView *view = q->view();
00628
00629 if (!view) {
00630 return;
00631 }
00632
00633 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
00634 KConfigGroup sizeGroup(mainGroup, "PopupApplet");
00635
00636 Q_ASSERT(q->containment());
00637 Q_ASSERT(q->containment()->corona());
00638
00639 int preferredWidth = 0;
00640 int preferredHeight = 0;
00641 if (dialog->graphicsWidget()) {
00642 preferredWidth = dialog->graphicsWidget()->preferredSize().width();
00643 preferredHeight = dialog->graphicsWidget()->preferredSize().height();
00644 }
00645
00646 const int width = qMin(sizeGroup.readEntry("DialogWidth", preferredWidth),
00647 q->containment()->corona()->screenGeometry(-1).width() - 50);
00648 const int height = qMin(sizeGroup.readEntry("DialogHeight", preferredHeight),
00649 q->containment()->corona()->screenGeometry(-1).height() - 50);
00650
00651 QSize saved(width, height);
00652
00653 if (saved.isNull()) {
00654 saved = dialog->sizeHint();
00655 } else {
00656 saved = saved.expandedTo(dialog->minimumSizeHint());
00657 }
00658
00659 if (saved.width() != dialog->width() || saved.height() != dialog->height()) {
00660 dialog->resize(saved);
00661 }
00662
00663 QSize s = dialog->size();
00664 QPoint pos = view->mapFromScene(q->scenePos());
00665
00666
00667 Corona *corona = qobject_cast<Corona *>(q->scene());
00668 if (corona) {
00669 pos = corona->popupPosition(q, s);
00670 }
00671
00672 bool reverse = false;
00673 if (q->formFactor() == Plasma::Vertical) {
00674 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).y() + q->size().height()/2 < pos.y() + dialog->size().width()/2) {
00675 reverse = true;
00676 }
00677 } else {
00678 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).x() + q->size().width()/2 < pos.x() + dialog->size().width()/2) {
00679 reverse = true;
00680 }
00681 }
00682
00683 switch (q->location()) {
00684 case BottomEdge:
00685 if (pos.x() >= q->pos().x()) {
00686 dialog->setResizeHandleCorners(Dialog::NorthEast);
00687 } else {
00688 dialog->setResizeHandleCorners(Dialog::NorthWest);
00689 }
00690
00691 if (reverse) {
00692 popupPlacement = Plasma::TopPosedLeftAlignedPopup;
00693 } else {
00694 popupPlacement = Plasma::TopPosedRightAlignedPopup;
00695 }
00696 break;
00697 case TopEdge:
00698 if (pos.x() >= q->pos().x()) {
00699 dialog->setResizeHandleCorners(Dialog::SouthEast);
00700 } else {
00701 dialog->setResizeHandleCorners(Dialog::SouthWest);
00702 }
00703
00704 if (reverse) {
00705 popupPlacement = Plasma::BottomPosedLeftAlignedPopup;
00706 } else {
00707 popupPlacement = Plasma::BottomPosedRightAlignedPopup;
00708 }
00709 break;
00710 case LeftEdge:
00711 if (pos.y() >= q->pos().y()) {
00712 dialog->setResizeHandleCorners(Dialog::SouthEast);
00713 } else {
00714 dialog->setResizeHandleCorners(Dialog::NorthEast);
00715 }
00716
00717 if (reverse) {
00718 popupPlacement = Plasma::RightPosedTopAlignedPopup;
00719 } else {
00720 popupPlacement = Plasma::RightPosedBottomAlignedPopup;
00721 }
00722 break;
00723
00724 case RightEdge:
00725 if (pos.y() >= q->pos().y()) {
00726 dialog->setResizeHandleCorners(Dialog::SouthWest);
00727 } else {
00728 dialog->setResizeHandleCorners(Dialog::NorthWest);
00729 }
00730
00731 if (reverse) {
00732 popupPlacement = Plasma::LeftPosedTopAlignedPopup;
00733 } else {
00734 popupPlacement = Plasma::LeftPosedBottomAlignedPopup;
00735 }
00736 break;
00737 default:
00738 dialog->setResizeHandleCorners(Dialog::NorthEast);
00739 }
00740
00741 dialog->move(pos);
00742 }
00743
00744 }
00745
00746 #include "popupapplet.moc"
00747