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 if (icon) {
00306 q->setMinimumSize(QSize(0, 0));
00307 }
00308
00309 if (gWidget) {
00310 Corona *corona = qobject_cast<Corona *>(gWidget->scene());
00311
00312
00313 if (corona) {
00314 corona->addOffscreenWidget(gWidget);
00315 dialog->setGraphicsWidget(gWidget);
00316 gWidget->resize(gWidget->preferredSize());
00317 }
00318
00319 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (gWidget->windowFlags() & Qt::X11BypassWindowManagerHint));
00320 } else if (qWidget) {
00321 QVBoxLayout *l_layout = new QVBoxLayout(dialog);
00322 l_layout->setSpacing(0);
00323 l_layout->setMargin(0);
00324 l_layout->addWidget(qWidget);
00325 dialog->adjustSize();
00326 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (qWidget->windowFlags() & Qt::X11BypassWindowManagerHint));
00327 } else {
00328 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
00329 }
00330
00331 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
00332 dialog->installEventFilter(q);
00333
00334 QObject::connect(dialog, SIGNAL(dialogResized()), q, SLOT(dialogSizeChanged()));
00335 QObject::connect(dialog, SIGNAL(dialogVisible(bool)), q, SLOT(dialogStatusChanged(bool)));
00336 }
00337
00338 if (icon && lay) {
00339 lay->addItem(icon);
00340 }
00341
00342 if (icon) {
00343 q->setMinimumSize(0,0);
00344 }
00345 }
00346 }
00347 emit q->sizeHintChanged(Qt::PreferredSize);
00348 }
00349
00350 void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event)
00351 {
00352 if (!d->icon && !d->popupLostFocus && event->buttons() == Qt::LeftButton) {
00353 d->clicked = scenePos().toPoint();
00354 event->setAccepted(true);
00355 return;
00356 } else {
00357 d->popupLostFocus = false;
00358 Applet::mousePressEvent(event);
00359 }
00360 }
00361
00362 void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00363 {
00364 if (!d->icon &&
00365 (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) {
00366 d->internalTogglePopup();
00367 } else {
00368 Applet::mouseReleaseEvent(event);
00369 }
00370 }
00371
00372 bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
00373 {
00374 if (!d->passive && watched == d->dialog && (event->type() == QEvent::WindowDeactivate)) {
00375 d->popupLostFocus = true;
00376 hidePopup();
00377 QTimer::singleShot(100, this, SLOT(clearPopupLostFocus()));
00378 }
00379
00392 return Applet::eventFilter(watched, event);
00393 }
00394
00395
00396 void PopupApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00397 {
00398 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00399 const ExtenderItemMimeData *mimeData =
00400 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00401 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
00402 event->accept();
00403 showPopup();
00404 }
00405 }
00406 }
00407
00408 void PopupApplet::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
00409 {
00410 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00411 const ExtenderItemMimeData *mimeData =
00412 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00413 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
00414
00415
00416 if (d->dialog && !d->dialog->geometry().contains(event->screenPos()) &&
00417 mimeData->extenderItem()->extender() != qobject_cast<Extender*>(graphicsWidget())) {
00418
00419
00420
00421 showPopup(250);
00422 }
00423 }
00424 }
00425 }
00426
00427 void PopupApplet::dropEvent(QGraphicsSceneDragDropEvent *event)
00428 {
00429 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00430 const ExtenderItemMimeData *mimeData =
00431 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00432 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) {
00433 mimeData->extenderItem()->setExtender(extender());
00434 QApplication::restoreOverrideCursor();
00435 }
00436 }
00437 }
00438
00439 void PopupApplet::showPopup(uint popupDuration)
00440 {
00441 if (d->dialog) {
00442
00443
00444 if (!d->dialog->isVisible()) {
00445 d->internalTogglePopup();
00446 }
00447
00448 if (d->timer) {
00449 d->timer->stop();
00450 }
00451
00452 if (popupDuration > 0) {
00453 if (!d->timer) {
00454 d->timer = new QTimer(this);
00455 connect(d->timer, SIGNAL(timeout()), this, SLOT(hideTimedPopup()));
00456 }
00457
00458 d->timer->start(popupDuration);
00459 }
00460 }
00461 }
00462
00463 void PopupApplet::hidePopup()
00464 {
00465 if (d->dialog) {
00466 if (location() != Floating) {
00467 d->dialog->animatedHide(locationToInverseDirection(location()));
00468 } else {
00469 d->dialog->hide();
00470 }
00471 }
00472 }
00473
00474 void PopupApplet::togglePopup()
00475 {
00476 d->internalTogglePopup();
00477 }
00478
00479 Plasma::PopupPlacement PopupApplet::popupPlacement() const
00480 {
00481 return d->popupPlacement;
00482 }
00483
00484 void PopupApplet::popupEvent(bool)
00485 {
00486 }
00487
00488 void PopupApplet::setPassivePopup(bool passive)
00489 {
00490 d->passive = passive;
00491 }
00492
00493 bool PopupApplet::isPassivePopup() const
00494 {
00495 return d->passive;
00496 }
00497
00498 bool PopupApplet::isPopupShowing() const
00499 {
00500 return d->dialog && d->dialog->isVisible();
00501 }
00502
00503 PopupAppletPrivate::PopupAppletPrivate(PopupApplet *applet)
00504 : q(applet),
00505 icon(0),
00506 dialog(0),
00507 proxy(0),
00508 widget(0),
00509 graphicsWidget(0),
00510 popupPlacement(Plasma::FloatingPopup),
00511 savedAspectRatio(Plasma::InvalidAspectRatioMode),
00512 timer(0),
00513 popupLostFocus(false),
00514 passive(false)
00515 {
00516 }
00517
00518 PopupAppletPrivate::~PopupAppletPrivate()
00519 {
00520 if (proxy) {
00521 proxy->setWidget(0);
00522 }
00523
00524 delete dialog;
00525 delete icon;
00526 }
00527
00528 void PopupAppletPrivate::internalTogglePopup()
00529 {
00530 if (!dialog) {
00531 q->setFocus(Qt::ShortcutFocusReason);
00532 return;
00533 }
00534
00535 if (!q->view()) {
00536 return;
00537 }
00538
00539 if (timer) {
00540 timer->stop();
00541 }
00542
00543 if (dialog->isVisible()) {
00544 if (q->location() != Floating) {
00545 dialog->animatedHide(locationToInverseDirection(q->location()));
00546 } else {
00547 dialog->hide();
00548 }
00549
00550 dialog->clearFocus();
00551 } else {
00552 if (q->graphicsWidget() &&
00553 q->graphicsWidget() == static_cast<Applet*>(q)->d->extender &&
00554 static_cast<Applet*>(q)->d->extender->isEmpty()) {
00555
00556 return;
00557 }
00558
00559 ToolTipManager::self()->hide(q);
00560 updateDialogPosition();
00561
00562 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager);
00563
00583 if (q->location() != Floating) {
00584 dialog->animatedShow(locationToDirection(q->location()));
00585 } else {
00586 dialog->show();
00587 }
00588
00589 if (!(dialog->windowFlags() & Qt::X11BypassWindowManagerHint)) {
00590 KWindowSystem::activateWindow(dialog->winId());
00591 }
00592 }
00593 }
00594
00595 void PopupAppletPrivate::hideTimedPopup()
00596 {
00597 timer->stop();
00598 q->hidePopup();
00599 }
00600
00601 void PopupAppletPrivate::clearPopupLostFocus()
00602 {
00603 popupLostFocus = false;
00604 }
00605
00606 void PopupAppletPrivate::dialogSizeChanged()
00607 {
00608
00609 if (dialog) {
00610 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
00611 KConfigGroup sizeGroup(mainGroup, "PopupApplet");
00612 sizeGroup.writeEntry("DialogHeight", dialog->height());
00613 sizeGroup.writeEntry("DialogWidth", dialog->width());
00614
00615 updateDialogPosition();
00616
00617 emit q->configNeedsSaving();
00618 }
00619 }
00620
00621 void PopupAppletPrivate::dialogStatusChanged(bool status)
00622 {
00623 q->popupEvent(status);
00624 }
00625
00626 void PopupAppletPrivate::updateDialogPosition()
00627 {
00628 if (!dialog) {
00629 return;
00630 }
00631
00632 QGraphicsView *view = q->view();
00633
00634 if (!view) {
00635 return;
00636 }
00637
00638 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup();
00639 KConfigGroup sizeGroup(mainGroup, "PopupApplet");
00640
00641 Q_ASSERT(q->containment());
00642 Q_ASSERT(q->containment()->corona());
00643
00644 int preferredWidth = 0;
00645 int preferredHeight = 0;
00646 if (dialog->graphicsWidget()) {
00647 preferredWidth = dialog->graphicsWidget()->preferredSize().width();
00648 preferredHeight = dialog->graphicsWidget()->preferredSize().height();
00649 }
00650
00651 const int width = qMin(sizeGroup.readEntry("DialogWidth", preferredWidth),
00652 q->containment()->corona()->screenGeometry(-1).width() - 50);
00653 const int height = qMin(sizeGroup.readEntry("DialogHeight", preferredHeight),
00654 q->containment()->corona()->screenGeometry(-1).height() - 50);
00655
00656 QSize saved(width, height);
00657
00658 if (saved.isNull()) {
00659 saved = dialog->sizeHint();
00660 } else {
00661 saved = saved.expandedTo(dialog->minimumSizeHint());
00662 }
00663
00664 if (saved.width() != dialog->width() || saved.height() != dialog->height()) {
00665 dialog->resize(saved);
00666 }
00667
00668 QSize s = dialog->size();
00669 QPoint pos = view->mapFromScene(q->scenePos());
00670
00671
00672 Corona *corona = qobject_cast<Corona *>(q->scene());
00673 if (corona) {
00674 pos = corona->popupPosition(q, s);
00675 }
00676
00677 bool reverse = false;
00678 if (q->formFactor() == Plasma::Vertical) {
00679 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).y() + q->size().height()/2 < pos.y() + dialog->size().width()/2) {
00680 reverse = true;
00681 }
00682 } else {
00683 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).x() + q->size().width()/2 < pos.x() + dialog->size().width()/2) {
00684 reverse = true;
00685 }
00686 }
00687
00688 switch (q->location()) {
00689 case BottomEdge:
00690 if (pos.x() >= q->pos().x()) {
00691 dialog->setResizeHandleCorners(Dialog::NorthEast);
00692 } else {
00693 dialog->setResizeHandleCorners(Dialog::NorthWest);
00694 }
00695
00696 if (reverse) {
00697 popupPlacement = Plasma::TopPosedLeftAlignedPopup;
00698 } else {
00699 popupPlacement = Plasma::TopPosedRightAlignedPopup;
00700 }
00701 break;
00702 case TopEdge:
00703 if (pos.x() >= q->pos().x()) {
00704 dialog->setResizeHandleCorners(Dialog::SouthEast);
00705 } else {
00706 dialog->setResizeHandleCorners(Dialog::SouthWest);
00707 }
00708
00709 if (reverse) {
00710 popupPlacement = Plasma::BottomPosedLeftAlignedPopup;
00711 } else {
00712 popupPlacement = Plasma::BottomPosedRightAlignedPopup;
00713 }
00714 break;
00715 case LeftEdge:
00716 if (pos.y() >= q->pos().y()) {
00717 dialog->setResizeHandleCorners(Dialog::SouthEast);
00718 } else {
00719 dialog->setResizeHandleCorners(Dialog::NorthEast);
00720 }
00721
00722 if (reverse) {
00723 popupPlacement = Plasma::RightPosedTopAlignedPopup;
00724 } else {
00725 popupPlacement = Plasma::RightPosedBottomAlignedPopup;
00726 }
00727 break;
00728
00729 case RightEdge:
00730 if (pos.y() >= q->pos().y()) {
00731 dialog->setResizeHandleCorners(Dialog::SouthWest);
00732 } else {
00733 dialog->setResizeHandleCorners(Dialog::NorthWest);
00734 }
00735
00736 if (reverse) {
00737 popupPlacement = Plasma::LeftPosedTopAlignedPopup;
00738 } else {
00739 popupPlacement = Plasma::LeftPosedBottomAlignedPopup;
00740 }
00741 break;
00742 default:
00743 dialog->setResizeHandleCorners(Dialog::NorthEast);
00744 }
00745
00746 dialog->move(pos);
00747 }
00748
00749 }
00750
00751 #include "popupapplet.moc"
00752