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 <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
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
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
00188
00189
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
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
00243
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
00255 if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) {
00256 q->resize(prefSize);
00257 }
00258
00259
00260 lay->setPreferredSize(prefSize);
00261
00262 } else {
00263 kDebug() << "about to switch to a popup";
00264
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);
00274 delete proxy;
00275 proxy = 0;
00276 }
00277
00278 if (!dialog) {
00279 dialog = new Plasma::Dialog();
00280
00281
00282
00283
00284
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
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
00374
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
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
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 }
00637
00638 #include "popupapplet.moc"
00639