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 #include "private/dialog_p.h" 00024 00025 #include <QApplication> 00026 #include <QGraphicsProxyWidget> 00027 #include <QGraphicsLinearLayout> 00028 #include <QTimer> 00029 #include <QVBoxLayout> 00030 00031 #ifdef Q_WS_X11 00032 #include <QX11Info> 00033 #endif 00034 00035 #include <kicon.h> 00036 #include <kiconloader.h> 00037 #include <kwindowsystem.h> 00038 #include <kglobalsettings.h> 00039 #include <netwm.h> 00040 00041 #include "plasma/private/applet_p.h" 00042 #include "plasma/private/extenderitemmimedata_p.h" 00043 #include "plasma/corona.h" 00044 #include "plasma/containment.h" 00045 #include "plasma/private/containment_p.h" 00046 #include "plasma/dialog.h" 00047 #include "plasma/extenders/extender.h" 00048 #include "plasma/extenders/extenderitem.h" 00049 #include "plasma/package.h" 00050 #include "plasma/theme.h" 00051 #include "plasma/scripting/appletscript.h" 00052 #include "plasma/tooltipmanager.h" 00053 #include "plasma/widgets/iconwidget.h" 00054 00055 namespace Plasma 00056 { 00057 00058 PopupApplet::PopupApplet(QObject *parent, const QVariantList &args) 00059 : Plasma::Applet(parent, args), 00060 d(new PopupAppletPrivate(this)) 00061 { 00062 } 00063 00064 PopupApplet::PopupApplet(const QString &packagePath, uint appletId, const QVariantList &args) 00065 : Plasma::Applet(packagePath, appletId, args), 00066 d(new PopupAppletPrivate(this)) 00067 { 00068 } 00069 00070 PopupApplet::~PopupApplet() 00071 { 00072 delete widget(); 00073 delete d; 00074 } 00075 00076 void PopupApplet::setPopupIcon(const QIcon &icon) 00077 { 00078 if (icon.isNull()) { 00079 if (d->icon) { 00080 delete d->icon; 00081 d->icon = 0; 00082 setLayout(0); 00083 setAspectRatioMode(d->savedAspectRatio); 00084 } 00085 00086 return; 00087 } 00088 00089 d->createIconWidget(); 00090 d->icon->setIcon(icon); 00091 } 00092 00093 void PopupApplet::setPopupIcon(const QString &iconName) 00094 { 00095 // Attempt 1: is it in the plasmoid package? 00096 if (package()) { 00097 const QString file = package()->filePath("images", iconName); 00098 if (!file.isEmpty()) { 00099 setPopupIcon(KIcon(file)); 00100 return; 00101 } 00102 } 00103 00104 // Attempt 2: is it a svg in the icons directory? 00105 QString name = QString("icons/") + iconName.split("-").first(); 00106 if (!Plasma::Theme::defaultTheme()->imagePath(name).isEmpty()) { 00107 d->createIconWidget(); 00108 d->icon->setSvg(name, iconName); 00109 if (d->icon->svg().isEmpty()) { 00110 setPopupIcon(KIcon(iconName)); 00111 } 00112 00113 return; 00114 } 00115 00116 // Final Attempt: use KIcon 00117 setPopupIcon(KIcon(iconName)); 00118 } 00119 00120 QIcon PopupApplet::popupIcon() const 00121 { 00122 return d->icon ? d->icon->icon() : QIcon(); 00123 } 00124 00125 QWidget *PopupApplet::widget() 00126 { 00127 return d->widget; 00128 } 00129 00130 void PopupApplet::setWidget(QWidget *widget) 00131 { 00132 if (d->widget) { 00133 Plasma::Dialog *dialog = d->dialogPtr.data(); 00134 if (dialog) { 00135 dialog->setGraphicsWidget(0); 00136 QVBoxLayout *lay = 0; 00137 00138 QLayout *existingLayout = dialog->layout(); 00139 if (existingLayout) { 00140 lay = dynamic_cast<QVBoxLayout *>(existingLayout); 00141 if (!lay) { 00142 delete existingLayout; 00143 } 00144 } 00145 00146 if (!lay) { 00147 lay = new QVBoxLayout; 00148 dialog->setLayout(lay); 00149 } 00150 00151 lay->removeWidget(d->widget); 00152 lay->addWidget(widget); 00153 } else if (d->proxy) { 00154 d->proxy.data()->setWidget(widget); 00155 } 00156 } 00157 00158 d->widget = widget; 00159 } 00160 00161 QGraphicsWidget *PopupApplet::graphicsWidget() 00162 { 00163 if (d->graphicsWidget != 0) { 00164 return d->graphicsWidget; 00165 } else { 00166 return static_cast<Applet*>(this)->d->extender.data(); 00167 } 00168 } 00169 00170 void PopupApplet::setGraphicsWidget(QGraphicsWidget *graphicsWidget) 00171 { 00172 if (d->graphicsWidget) { 00173 if (d->dialogPtr) { 00174 d->dialogPtr.data()->setGraphicsWidget(graphicsWidget); 00175 } else { 00176 QGraphicsLinearLayout *lay = static_cast<QGraphicsLinearLayout *>(layout()); 00177 lay->removeAt(0); 00178 lay->addItem(graphicsWidget); 00179 } 00180 } 00181 00182 d->graphicsWidget = graphicsWidget; 00183 } 00184 00185 void PopupAppletPrivate::checkExtenderAppearance(Plasma::FormFactor f) 00186 { 00187 Extender *extender = qobject_cast<Extender*>(q->graphicsWidget()); 00188 if (extender) { 00189 if (f != Plasma::Horizontal && f != Plasma::Vertical) { 00190 extender->setAppearance(Extender::NoBorders); 00191 } else if (q->location() == TopEdge) { 00192 extender->setAppearance(Extender::TopDownStacked); 00193 } else { 00194 extender->setAppearance(Extender::BottomUpStacked); 00195 } 00196 00197 if (dialogPtr) { 00198 dialogPtr.data()->setGraphicsWidget(extender); 00199 } 00200 } 00201 } 00202 00203 void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints) 00204 { 00205 Plasma::FormFactor f = q->formFactor(); 00206 00207 if (constraints & Plasma::LocationConstraint) { 00208 checkExtenderAppearance(f); 00209 } 00210 00211 if (constraints & Plasma::FormFactorConstraint || 00212 constraints & Plasma::StartupCompletedConstraint || 00213 (constraints & Plasma::SizeConstraint && 00214 (f == Plasma::Vertical || f == Plasma::Horizontal))) { 00215 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout()); 00216 00217 if (icon && lay && lay->count() > 0) { 00218 lay->removeAt(0); 00219 } 00220 00221 QSizeF minimum; 00222 QSizeF parentSize; 00223 00224 QGraphicsWidget *gWidget = q->graphicsWidget(); 00225 //kDebug() << "graphics widget is" << (QObject*)gWidget; 00226 QWidget *qWidget = q->widget(); 00227 00228 if (gWidget) { 00229 minimum = gWidget->minimumSize(); 00230 // our layout may have been replaced on us in the call to graphicsWidget! 00231 lay = dynamic_cast<QGraphicsLinearLayout *>(q->layout()); 00232 00233 if (!(constraints & LocationConstraint)) { 00234 checkExtenderAppearance(f); 00235 } 00236 } else if (qWidget) { 00237 minimum = qWidget->minimumSizeHint(); 00238 } 00239 00240 //99% of the times q->parentWidget() is the containment, but using it we can also manage the applet-in-applet case (i.e. systray) 00241 //there are also cases where the parentlayoutitem is bigger than the containment (e.g. newspaper) 00242 if (q->parentLayoutItem()) { 00243 parentSize = q->parentLayoutItem()->geometry().size(); 00244 } else if (q->parentWidget()) { 00245 parentSize = q->parentWidget()->size(); 00246 } 00247 00248 //check if someone did the nasty trick of applets in applets, in this case we always want to be collapsed 00249 QGraphicsWidget *candidateParentApplet = q; 00250 Plasma::Applet *parentApplet = 0; 00251 //this loop should be executed normally a single time, at most 2-3 times for quite complex containments 00252 while (candidateParentApplet) { 00253 candidateParentApplet = candidateParentApplet->parentWidget(); 00254 parentApplet = qobject_cast<Plasma::Applet *>(candidateParentApplet); 00255 if (parentApplet) { 00256 break; 00257 } 00258 } 00259 00260 //Applet on desktop 00261 if ((!parentApplet || parentApplet->isContainment() ) && icon && (!icon->svg().isEmpty() || !icon->icon().isNull()) && ((f != Plasma::Vertical && f != Plasma::Horizontal) || 00262 ((f == Plasma::Vertical && parentSize.width() >= minimum.width()) || 00263 (f == Plasma::Horizontal && parentSize.height() >= minimum.height())))) { 00264 //kDebug() << "we are expanding the popupapplet"; 00265 00266 00267 // we only switch to expanded if we aren't horiz/vert constrained and 00268 // this applet has an icon. 00269 // otherwise, we leave it up to the applet itself to figure it out 00270 if (icon) { 00271 icon->hide(); 00272 } 00273 00274 if (savedAspectRatio != Plasma::InvalidAspectRatioMode) { 00275 q->setAspectRatioMode(savedAspectRatio); 00276 } 00277 00278 Dialog *dialog = dialogPtr.data(); 00279 if (dialog) { 00280 if (dialog->layout() && qWidget) { 00281 //we don't want to delete Widget inside the dialog layout 00282 dialog->layout()->removeWidget(qWidget); 00283 } 00284 00285 if (qWidget) { 00286 qWidget->setParent(0); 00287 } 00288 00289 delete dialog; 00290 } 00291 00292 if (!lay) { 00293 lay = new QGraphicsLinearLayout(); 00294 lay->setContentsMargins(0, 0, 0, 0); 00295 lay->setSpacing(0); 00296 lay->setOrientation(Qt::Horizontal); 00297 q->setLayout(lay); 00298 } 00299 00300 QSize prefSize; 00301 00302 if (gWidget) { 00303 if (proxy) { 00304 proxy.data()->setWidget(0); 00305 delete proxy.data(); 00306 } 00307 00308 Corona *corona = qobject_cast<Corona *>(gWidget->scene()); 00309 00310 if (corona) { 00311 corona->removeOffscreenWidget(gWidget); 00312 } 00313 00314 lay->addItem(gWidget); 00315 prefSize = gWidget->preferredSize().toSize(); 00316 } else if (qWidget) { 00317 if (!proxy) { 00318 proxy = new QGraphicsProxyWidget(q); 00319 proxy.data()->setWidget(qWidget); 00320 proxy.data()->show(); 00321 } 00322 00323 lay->addItem(proxy.data()); 00324 prefSize = qWidget->sizeHint(); 00325 } 00326 00327 //we could be on a big panel, but in that case we will be able to resize 00328 //more than the natural minimum size, because we'll transform into an icon 00329 if (f == Plasma::Horizontal) { 00330 minimum.setHeight(0); 00331 } else if (f == Plasma::Vertical) { 00332 minimum.setWidth(0); 00333 } 00334 00335 qreal left, top, right, bottom; 00336 q->getContentsMargins(&left, &top, &right, &bottom); 00337 QSizeF oldSize(q->size()); 00338 00339 //size not saved/invalid size saved 00340 if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) { 00341 q->resize(prefSize); 00342 emit q->appletTransformedItself(); 00343 } 00344 //Applet on popup 00345 } else { 00346 if (icon && lay) { 00347 lay->addItem(icon); 00348 } 00349 00350 //kDebug() << "about to switch to a popup"; 00351 if (!qWidget && !gWidget) { 00352 delete dialogPtr.data(); 00353 return; 00354 } 00355 00356 //there was already a dialog? don't make the switch again 00357 if (dialogPtr) { 00358 return; 00359 } 00360 00361 if (proxy) { 00362 proxy.data()->setWidget(0); // prevent it from deleting our widget! 00363 delete proxy.data(); 00364 } 00365 00366 if (!dialogPtr) { 00367 //save the aspect ratio mode in case we drag'n drop in the Desktop later 00368 savedAspectRatio = q->aspectRatioMode(); 00369 00370 if (icon) { 00371 icon->show(); 00372 q->setAspectRatioMode(Plasma::ConstrainedSquare); 00373 } 00374 00375 Dialog *dialog = new Dialog(); 00376 dialog->d->appletPtr = q; 00377 dialogPtr = dialog; 00378 00379 if (icon) { 00380 dialog->setAspectRatioMode(savedAspectRatio); 00381 } 00382 00383 //no longer use Qt::Popup since that seems to cause a lot of problem when you drag 00384 //stuff out of your Dialog (extenders). Monitor WindowDeactivate events so we can 00385 //emulate the same kind of behavior as Qt::Popup (close when you click somewhere 00386 //else. 00387 00388 if (gWidget) { 00389 Corona *corona = qobject_cast<Corona *>(gWidget->scene()); 00390 00391 if (corona) { 00392 corona->addOffscreenWidget(gWidget); 00393 } 00394 00395 dialog->setGraphicsWidget(gWidget); 00396 //gWidget->resize(gWidget->preferredSize()); 00397 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (gWidget->windowFlags() & Qt::X11BypassWindowManagerHint)); 00398 } else if (qWidget) { 00399 QVBoxLayout *l_layout = new QVBoxLayout(dialog); 00400 l_layout->setSpacing(0); 00401 l_layout->setMargin(0); 00402 l_layout->addWidget(qWidget); 00403 dialog->adjustSize(); 00404 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | (qWidget->windowFlags() & Qt::X11BypassWindowManagerHint)); 00405 } else { 00406 dialog->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); 00407 } 00408 00409 restoreDialogSize(); 00410 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager); 00411 dialog->installEventFilter(q); 00412 00413 QObject::connect(dialog, SIGNAL(dialogResized()), q, SLOT(dialogSizeChanged())); 00414 QObject::connect(dialog, SIGNAL(dialogVisible(bool)), q, SLOT(dialogStatusChanged(bool))); 00415 } 00416 } 00417 } 00418 00419 if (constraints & Plasma::PopupConstraint) { 00420 updateDialogPosition(); 00421 } 00422 00423 if (icon) { 00424 // emit the size hint changing stuff for our applet as we are handling 00425 // the size changings 00426 emit q->sizeHintChanged(Qt::PreferredSize); 00427 } 00428 } 00429 00430 void PopupAppletPrivate::appletActivated() 00431 { 00432 internalTogglePopup(true); 00433 } 00434 00435 QSizeF PopupApplet::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const 00436 { 00437 if (!d->dialogPtr || which != Qt::PreferredSize) { 00438 return Applet::sizeHint(which, constraint); 00439 } 00440 00441 switch (formFactor()) { 00442 case Vertical: 00443 case Horizontal: { 00444 const int size = IconSize(KIconLoader::Panel); 00445 return QSizeF(size, size); 00446 break; 00447 } 00448 default: 00449 break; 00450 } 00451 00452 const int size = IconSize(KIconLoader::Desktop); 00453 return QSizeF(size, size); 00454 } 00455 00456 void PopupApplet::mousePressEvent(QGraphicsSceneMouseEvent *event) 00457 { 00458 if (!d->icon && !d->popupLostFocus && event->buttons() == Qt::LeftButton) { 00459 d->clicked = scenePos().toPoint(); 00460 event->setAccepted(true); 00461 return; 00462 } else { 00463 d->popupLostFocus = false; 00464 Applet::mousePressEvent(event); 00465 } 00466 } 00467 00468 void PopupApplet::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 00469 { 00470 if (!d->icon && 00471 (d->clicked - scenePos().toPoint()).manhattanLength() < KGlobalSettings::dndEventDelay()) { 00472 d->internalTogglePopup(); 00473 } else { 00474 Applet::mouseReleaseEvent(event); 00475 } 00476 } 00477 00478 bool PopupApplet::eventFilter(QObject *watched, QEvent *event) 00479 { 00480 if (!d->passive && watched == d->dialogPtr.data() && (event->type() == QEvent::WindowDeactivate)) { 00481 d->popupLostFocus = true; 00482 QTimer::singleShot(100, this, SLOT(clearPopupLostFocus())); 00483 } 00484 00485 if (watched == d->dialogPtr.data() && event->type() == QEvent::ContextMenu) { 00486 //pass it up to the applet 00487 //well, actually we have to pass it to the *containment* 00488 //because all the code for showing an applet's contextmenu is actually in Containment. 00489 Containment *c = containment(); 00490 if (c) { 00491 Applet *applet = this; 00492 Dialog *dialog = d->dialogPtr.data(); 00493 if (dialog && dialog->graphicsWidget()) { 00494 int left, top, right, bottom; 00495 dialog->getContentsMargins(&left, &top, &right, &bottom); 00496 const QPoint eventPos = static_cast<QContextMenuEvent*>(event)->pos() - QPoint(left, top); 00497 QPointF pos = dialog->graphicsWidget()->mapToScene(eventPos); 00498 00499 if (Applet *actual = c->d->appletAt(pos)) { 00500 applet = actual; 00501 } 00502 } 00503 00504 KMenu desktopMenu; 00505 c->d->addAppletActions(desktopMenu, applet, event); 00506 00507 if (!desktopMenu.isEmpty()) { 00508 desktopMenu.exec(static_cast<QContextMenuEvent*>(event)->globalPos()); 00509 return true; 00510 } 00511 00512 return false; 00513 } 00514 } 00515 00516 return Applet::eventFilter(watched, event); 00517 } 00518 00519 //FIXME: some duplication between the drag events... maybe add some simple helper function? 00520 void PopupApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event) 00521 { 00522 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) { 00523 const ExtenderItemMimeData *mimeData = 00524 qobject_cast<const ExtenderItemMimeData*>(event->mimeData()); 00525 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) { 00526 event->accept(); 00527 showPopup(); 00528 } 00529 } 00530 } 00531 00532 void PopupApplet::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) 00533 { 00534 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) { 00535 const ExtenderItemMimeData *mimeData = 00536 qobject_cast<const ExtenderItemMimeData*>(event->mimeData()); 00537 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) { 00538 //We want to hide the popup if we're not moving onto the popup AND it is not the popup 00539 //we started. 00540 if (d->dialogPtr && !d->dialogPtr.data()->geometry().contains(event->screenPos()) && 00541 mimeData->extenderItem()->extender() != qobject_cast<Extender*>(graphicsWidget())) { 00542 //We actually try to hide the popup, with a call to showPopup, with a smal timeout, 00543 //so if the user moves into the popup fast enough, it remains open (the extender 00544 //will call showPopup which will cancel the timeout. 00545 showPopup(250); 00546 } 00547 } 00548 } 00549 } 00550 00551 void PopupApplet::dropEvent(QGraphicsSceneDragDropEvent *event) 00552 { 00553 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) { 00554 const ExtenderItemMimeData *mimeData = 00555 qobject_cast<const ExtenderItemMimeData*>(event->mimeData()); 00556 if (mimeData && qobject_cast<Extender*>(graphicsWidget())) { 00557 mimeData->extenderItem()->setExtender(extender()); 00558 QApplication::restoreOverrideCursor(); 00559 } 00560 } 00561 } 00562 00563 void PopupApplet::showPopup(uint popupDuration) 00564 { 00565 // use autohideTimer to store when the next show should be 00566 if (popupDuration > 0 || d->autohideTimer) { 00567 if (!d->autohideTimer) { 00568 d->autohideTimer = new QTimer(this); 00569 d->autohideTimer->setSingleShot(true); 00570 connect(d->autohideTimer, SIGNAL(timeout()), this, SLOT(hideTimedPopup())); 00571 } 00572 00573 d->autohideTimer->stop(); 00574 d->autohideTimer->setInterval(popupDuration); 00575 } 00576 00577 //kDebug() << "starting delayed show, duration for popup is" << popupDuration; 00578 d->delayedShowTimer.start(0, this); 00579 } 00580 00581 void PopupApplet::timerEvent(QTimerEvent *event) 00582 { 00583 if (event->timerId() == d->delayedShowTimer.timerId()) { 00584 d->delayedShowTimer.stop(); 00585 Dialog *dialog = d->dialogPtr.data(); 00586 if (dialog) { 00587 // move the popup before its fist show, even if the show isn't triggered by 00588 // a click, this should fix the first random position seen in some widgets 00589 if (!dialog->isVisible()) { 00590 d->internalTogglePopup(); 00591 } 00592 00593 const int popupDuration = d->autohideTimer ? d->autohideTimer->interval() : 0; 00594 //kDebug() << "popupDuration is:" << (d->autohideTimer ? d->autohideTimer->interval() : 0); 00595 if (popupDuration > 0) { 00596 d->autohideTimer->start(); 00597 } else if (d->autohideTimer) { 00598 d->autohideTimer->stop(); 00599 } 00600 } 00601 } else { 00602 Applet::timerEvent(event); 00603 } 00604 } 00605 00606 void PopupApplet::hidePopup() 00607 { 00608 d->delayedShowTimer.stop(); 00609 00610 Dialog *dialog = d->dialogPtr.data(); 00611 if (dialog && dialog->isVisible()) { 00612 if (location() != Floating) { 00613 dialog->animatedHide(locationToInverseDirection(location())); 00614 } else { 00615 dialog->hide(); 00616 } 00617 } 00618 } 00619 00620 void PopupApplet::togglePopup() 00621 { 00622 d->internalTogglePopup(); 00623 } 00624 00625 Plasma::PopupPlacement PopupApplet::popupPlacement() const 00626 { 00627 return d->popupPlacement; 00628 } 00629 00630 void PopupApplet::setPopupAlignment(Qt::AlignmentFlag alignment) 00631 { 00632 d->popupAlignment = alignment; 00633 } 00634 00635 Qt::AlignmentFlag PopupApplet::popupAlignment() const 00636 { 00637 return d->popupAlignment; 00638 } 00639 00640 void PopupApplet::popupEvent(bool popped) 00641 { 00642 if (Applet::d->script) { 00643 emit Applet::d->script->popupEvent(popped); 00644 } 00645 } 00646 00647 void PopupApplet::setPassivePopup(bool passive) 00648 { 00649 d->passive = passive; 00650 } 00651 00652 bool PopupApplet::isPassivePopup() const 00653 { 00654 return d->passive; 00655 } 00656 00657 bool PopupApplet::isPopupShowing() const 00658 { 00659 return d->dialogPtr && d->dialogPtr.data()->isVisible(); 00660 } 00661 00662 bool PopupApplet::isIconified() const 00663 { 00664 return d->dialogPtr; 00665 } 00666 00667 PopupAppletPrivate::PopupAppletPrivate(PopupApplet *applet) 00668 : q(applet), 00669 icon(0), 00670 widget(0), 00671 graphicsWidget(0), 00672 popupPlacement(Plasma::FloatingPopup), 00673 popupAlignment(Qt::AlignLeft), 00674 savedAspectRatio(Plasma::InvalidAspectRatioMode), 00675 autohideTimer(0), 00676 preShowStatus(UnknownStatus), 00677 popupLostFocus(false), 00678 passive(false) 00679 { 00680 int iconSize = IconSize(KIconLoader::Desktop); 00681 q->resize(iconSize, iconSize); 00682 q->setAcceptDrops(true); 00683 QObject::disconnect(q, SIGNAL(activate()), static_cast<Applet*>(q), SLOT(setFocus())); 00684 QObject::connect(q, SIGNAL(activate()), q, SLOT(appletActivated())); 00685 QObject::connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), q, SLOT(iconSizeChanged(int))); 00686 } 00687 00688 PopupAppletPrivate::~PopupAppletPrivate() 00689 { 00690 if (proxy) { 00691 proxy.data()->setWidget(0); 00692 } 00693 00694 delete dialogPtr.data(); 00695 delete icon; 00696 } 00697 00698 void PopupAppletPrivate::iconSizeChanged(int group) 00699 { 00700 if (icon && (group == KIconLoader::Desktop || group == KIconLoader::Panel)) { 00701 q->updateGeometry(); 00702 } 00703 } 00704 00705 void PopupAppletPrivate::internalTogglePopup(bool fromActivatedSignal) 00706 { 00707 if (autohideTimer) { 00708 autohideTimer->stop(); 00709 } 00710 00711 delayedShowTimer.stop(); 00712 00713 Plasma::Dialog *dialog = dialogPtr.data(); 00714 if (!dialog) { 00715 q->setFocus(Qt::ShortcutFocusReason); 00716 if (!fromActivatedSignal) { 00717 QObject::disconnect(q, SIGNAL(activate()), q, SLOT(appletActivated())); 00718 emit q->activate(); 00719 QObject::connect(q, SIGNAL(activate()), q, SLOT(appletActivated())); 00720 } 00721 return; 00722 } 00723 00724 if (!q->view()) { 00725 return; 00726 } 00727 00728 if (dialog->isVisible()) { 00729 if (q->location() != Floating) { 00730 dialog->animatedHide(locationToInverseDirection(q->location())); 00731 } else { 00732 dialog->hide(); 00733 } 00734 00735 dialog->clearFocus(); 00736 } else { 00737 if (q->graphicsWidget() && 00738 q->graphicsWidget() == static_cast<Applet*>(q)->d->extender.data() && 00739 static_cast<Applet*>(q)->d->extender.data()->isEmpty()) { 00740 // we have nothing to show, so let's not. 00741 if (!fromActivatedSignal) { 00742 QObject::disconnect(q, SIGNAL(activate()), q, SLOT(appletActivated())); 00743 emit q->activate(); 00744 QObject::connect(q, SIGNAL(activate()), q, SLOT(appletActivated())); 00745 } 00746 return; 00747 } 00748 00749 ToolTipManager::self()->hide(q); 00750 updateDialogPosition(); 00751 00752 KWindowSystem::setOnAllDesktops(dialog->winId(), true); 00753 KWindowSystem::setState(dialog->winId(), NET::SkipTaskbar | NET::SkipPager); 00754 00755 if (icon) { 00756 dialog->setAspectRatioMode(savedAspectRatio); 00757 } 00758 00759 if (q->location() != Floating) { 00760 dialog->animatedShow(locationToDirection(q->location())); 00761 } else { 00762 dialog->show(); 00763 } 00764 00765 if (!(dialog->windowFlags() & Qt::X11BypassWindowManagerHint)) { 00766 KWindowSystem::activateWindow(dialog->winId()); 00767 } 00768 } 00769 } 00770 00771 void PopupAppletPrivate::hideTimedPopup() 00772 { 00773 autohideTimer->stop(); 00774 q->hidePopup(); 00775 } 00776 00777 void PopupAppletPrivate::clearPopupLostFocus() 00778 { 00779 if (!icon || !icon->isDown()) { 00780 q->hidePopup(); 00781 } 00782 00783 popupLostFocus = false; 00784 } 00785 00786 KConfigGroup PopupAppletPrivate::popupConfigGroup() 00787 { 00788 KConfigGroup *mainGroup = static_cast<Applet*>(q)->d->mainConfigGroup(); 00789 return KConfigGroup(mainGroup, "PopupApplet"); 00790 } 00791 00792 void PopupAppletPrivate::dialogSizeChanged() 00793 { 00794 //Reposition the dialog 00795 Plasma::Dialog *dialog = dialogPtr.data(); 00796 if (dialog) { 00797 KConfigGroup sizeGroup = popupConfigGroup(); 00798 sizeGroup.writeEntry("DialogHeight", dialog->height()); 00799 sizeGroup.writeEntry("DialogWidth", dialog->width()); 00800 00801 updateDialogPosition(); 00802 00803 emit q->configNeedsSaving(); 00804 emit q->appletTransformedByUser(); 00805 } 00806 } 00807 00808 void PopupAppletPrivate::dialogStatusChanged(bool shown) 00809 { 00810 if (shown) { 00811 preShowStatus = q->status(); 00812 q->setStatus(NeedsAttentionStatus); 00813 QObject::connect(q, SIGNAL(newStatus(Plasma::ItemStatus)), 00814 q, SLOT(statusChangeWhileShown(Plasma::ItemStatus)), 00815 Qt::UniqueConnection); 00816 } else { 00817 QObject::disconnect(q, SIGNAL(newStatus(Plasma::ItemStatus)), 00818 q, SLOT(statusChangeWhileShown(Plasma::ItemStatus))); 00819 q->setStatus(preShowStatus); 00820 } 00821 00822 q->popupEvent(shown); 00823 } 00824 00825 void PopupAppletPrivate::statusChangeWhileShown(Plasma::ItemStatus status) 00826 { 00827 preShowStatus = status; 00828 } 00829 00830 void PopupAppletPrivate::createIconWidget() 00831 { 00832 if (icon) { 00833 return; 00834 } 00835 00836 icon = new Plasma::IconWidget(q); 00837 QObject::connect(icon, SIGNAL(clicked()), q, SLOT(internalTogglePopup())); 00838 00839 QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(); 00840 layout->setContentsMargins(0, 0, 0, 0); 00841 layout->setSpacing(0); 00842 layout->setOrientation(Qt::Horizontal); 00843 layout->addItem(icon); 00844 layout->setAlignment(icon, Qt::AlignCenter); 00845 q->setLayout(layout); 00846 } 00847 00848 void PopupAppletPrivate::restoreDialogSize() 00849 { 00850 Plasma::Dialog *dialog = dialogPtr.data(); 00851 if (!dialog) { 00852 return; 00853 } 00854 00855 Corona *corona = qobject_cast<Corona *>(q->scene()); 00856 if (!corona) { 00857 return; 00858 } 00859 00860 KConfigGroup sizeGroup = popupConfigGroup(); 00861 00862 int preferredWidth = 0; 00863 int preferredHeight = 0; 00864 QGraphicsWidget *gWidget = dialog->graphicsWidget(); 00865 if (gWidget) { 00866 preferredWidth = gWidget->preferredSize().width(); 00867 preferredHeight = gWidget->preferredSize().height(); 00868 } 00869 00870 const int width = qMin(sizeGroup.readEntry("DialogWidth", preferredWidth), 00871 corona->screenGeometry(-1).width() - 50); 00872 const int height = qMin(sizeGroup.readEntry("DialogHeight", preferredHeight), 00873 corona->screenGeometry(-1).height() - 50); 00874 00875 QSize saved(width, height); 00876 00877 if (saved.isNull()) { 00878 saved = dialog->sizeHint(); 00879 } else { 00880 saved = saved.expandedTo(dialog->minimumSizeHint()); 00881 } 00882 00883 if (saved.width() != dialog->width() || saved.height() != dialog->height()) { 00884 dialog->resize(saved); 00885 /*if (gWidget) { 00886 gWidget->resize(saved); 00887 }*/ 00888 } 00889 } 00890 00891 void PopupAppletPrivate::updateDialogPosition() 00892 { 00893 Plasma::Dialog *dialog = dialogPtr.data(); 00894 if (!dialog) { 00895 return; 00896 } 00897 00898 Corona *corona = qobject_cast<Corona *>(q->scene()); 00899 if (!corona) { 00900 return; 00901 } 00902 00903 QGraphicsView *view = q->view(); 00904 if (!view) { 00905 return; 00906 } 00907 00908 QSize s = dialog->size(); 00909 QPoint pos = view->mapFromScene(q->scenePos()); 00910 00911 if (!q->containment() || view == q->containment()->view()) { 00912 pos = corona->popupPosition(q, s, popupAlignment); 00913 } else { 00914 pos = corona->popupPosition(q->parentItem(), s, popupAlignment); 00915 } 00916 00917 bool reverse = false; 00918 if (q->formFactor() == Plasma::Vertical) { 00919 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).y() + q->size().height()/2 < pos.y() + dialog->size().width()/2) { 00920 reverse = true; 00921 } 00922 } else { 00923 if (view->mapToGlobal(view->mapFromScene(q->scenePos())).x() + q->size().width()/2 < pos.x() + dialog->size().width()/2) { 00924 reverse = true; 00925 } 00926 } 00927 00928 switch (q->location()) { 00929 case BottomEdge: 00930 if (pos.x() >= q->pos().x()) { 00931 dialog->setResizeHandleCorners(Dialog::NorthEast); 00932 } else { 00933 dialog->setResizeHandleCorners(Dialog::NorthWest); 00934 } 00935 00936 if (reverse) { 00937 popupPlacement = Plasma::TopPosedLeftAlignedPopup; 00938 } else { 00939 popupPlacement = Plasma::TopPosedRightAlignedPopup; 00940 } 00941 break; 00942 case TopEdge: 00943 if (pos.x() >= q->pos().x()) { 00944 dialog->setResizeHandleCorners(Dialog::SouthEast); 00945 } else { 00946 dialog->setResizeHandleCorners(Dialog::SouthWest); 00947 } 00948 00949 if (reverse) { 00950 popupPlacement = Plasma::BottomPosedLeftAlignedPopup; 00951 } else { 00952 popupPlacement = Plasma::BottomPosedRightAlignedPopup; 00953 } 00954 break; 00955 case LeftEdge: 00956 if (pos.y() >= q->pos().y()) { 00957 dialog->setResizeHandleCorners(Dialog::SouthEast); 00958 } else { 00959 dialog->setResizeHandleCorners(Dialog::NorthEast); 00960 } 00961 00962 if (reverse) { 00963 popupPlacement = Plasma::RightPosedTopAlignedPopup; 00964 } else { 00965 popupPlacement = Plasma::RightPosedBottomAlignedPopup; 00966 } 00967 break; 00968 00969 case RightEdge: 00970 if (pos.y() >= q->pos().y()) { 00971 dialog->setResizeHandleCorners(Dialog::SouthWest); 00972 } else { 00973 dialog->setResizeHandleCorners(Dialog::NorthWest); 00974 } 00975 00976 if (reverse) { 00977 popupPlacement = Plasma::LeftPosedTopAlignedPopup; 00978 } else { 00979 popupPlacement = Plasma::LeftPosedBottomAlignedPopup; 00980 } 00981 break; 00982 default: 00983 dialog->setResizeHandleCorners(Dialog::NorthEast); 00984 } 00985 00986 dialog->move(pos); 00987 } 00988 00989 } // Plasma namespace 00990 00991 #include "popupapplet.moc" 00992
KDE 4.7 API Reference