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

Plasma

extenderitem.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin St, Fifth Floor,
00017  * Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include "extenderitem.h"
00021 
00022 #include <QApplication>
00023 #include <QAction>
00024 #include <QBitmap>
00025 #include <QGraphicsSceneResizeEvent>
00026 #include <QGraphicsSceneMouseEvent>
00027 #include <QGraphicsLinearLayout>
00028 #include <QLayout>
00029 #include <QPainter>
00030 #include <QTimer>
00031 
00032 #include <kdebug.h>
00033 #include <kicon.h>
00034 #include <kwindowsystem.h>
00035 #include <kglobalsettings.h>
00036 
00037 #include "applet.h"
00038 #include "containment.h"
00039 #include "corona.h"
00040 #include "dialog.h"
00041 #include "extender.h"
00042 #include "framesvg.h"
00043 #include "popupapplet.h"
00044 #include "theme.h"
00045 #include "view.h"
00046 
00047 #include "private/applet_p.h"
00048 #include "private/extender_p.h"
00049 #include "private/extenderitem_p.h"
00050 
00051 #include "widgets/iconwidget.h"
00052 
00053 namespace Plasma
00054 {
00055 
00056 class ExtenderItemView : public QGraphicsView
00057 {
00058 public:
00059     ExtenderItemView(QGraphicsScene *scene, QWidget *parent = 0)
00060         : QGraphicsView(scene, parent)
00061     {
00062         //since this view witll have a really short lifespan it can be checked a single time
00063         composite = KWindowSystem::compositingActive();
00064     }
00065 
00066     ~ExtenderItemView()
00067     {}
00068 
00069     void drawBackground(QPainter *painter, const QRectF &rect)
00070     {
00071         if (composite) {
00072             painter->setCompositionMode(QPainter::CompositionMode_Source);
00073             painter->fillRect(rect.toAlignedRect(), Qt::transparent);
00074         } else {
00075             QGraphicsView::drawBackground(painter, rect);
00076         }
00077     }
00078 
00079 private:
00080     bool composite;
00081 };
00082 
00083 ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
00084         : QGraphicsWidget(hostExtender),
00085           d(new ExtenderItemPrivate(this, hostExtender))
00086 {
00087     Q_ASSERT(hostExtender);
00088 
00089     //set the extenderId
00090     if (extenderItemId) {
00091         d->extenderItemId = extenderItemId;
00092         ExtenderItemPrivate::s_maxExtenderItemId =
00093             qMax(ExtenderItemPrivate::s_maxExtenderItemId, extenderItemId);
00094     } else {
00095         d->extenderItemId = ++ExtenderItemPrivate::s_maxExtenderItemId;
00096     }
00097 
00098     //create items's configgroup
00099     KConfigGroup cg = hostExtender->d->applet->config("ExtenderItems");
00100     KConfigGroup dg = KConfigGroup(&cg, QString::number(d->extenderItemId));
00101 
00102     uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
00103 
00104     //check if we're creating a new item or reinstantiating an existing one.
00105     if (!sourceAppletId) {
00106         //The item is new
00107         dg.writeEntry("sourceAppletPluginName", hostExtender->d->applet->pluginName());
00108         dg.writeEntry("sourceAppletId", hostExtender->d->applet->id());
00109         dg.writeEntry("extenderIconName", hostExtender->d->applet->icon());
00110         d->sourceApplet = hostExtender->d->applet;
00111         d->collapseIcon = new IconWidget(KIcon(hostExtender->d->applet->icon()), "", this);
00112     } else {
00113         //The item already exists.
00114         d->name = dg.readEntry("extenderItemName", "");
00115         d->title = dg.readEntry("extenderTitle", "");
00116         setCollapsed(dg.readEntry("isCollapsed", false));
00117 
00118         QString iconName = dg.readEntry("extenderIconName", "utilities-desktop-extra");
00119         if (iconName.isEmpty()) {
00120             iconName = "utilities-desktop-extra";
00121         }
00122         d->collapseIcon = new IconWidget(KIcon(iconName), "", this);
00123 
00124         //Find the sourceapplet.
00125         Corona *corona = hostExtender->d->applet->containment()->corona();
00126         foreach (Containment *containment, corona->containments()) {
00127             foreach (Applet *applet, containment->applets()) {
00128                 if (applet->id() == sourceAppletId &&
00129                         applet->pluginName() == dg.readEntry("sourceAppletPluginName", "")) {
00130                     d->sourceApplet = applet;
00131                 }
00132             }
00133         }
00134     }
00135 
00136     //make sure we keep monitoring if the source applet still exists, so the return to source icon
00137     //can be hidden if it is removed.
00138     connect(d->sourceApplet, SIGNAL(destroyed()), this, SLOT(sourceAppletRemoved()));
00139     connect(d->collapseIcon, SIGNAL(clicked()), this, SLOT(toggleCollapse()));
00140 
00141     //create the toolbox.
00142     d->toolbox = new QGraphicsWidget(this);
00143     d->toolboxLayout = new QGraphicsLinearLayout(d->toolbox);
00144     d->toolbox->setLayout(d->toolboxLayout);
00145 
00146     //set the extender we want to move to.
00147     setExtender(hostExtender);
00148 
00149     //show or hide the return to source icon.
00150     d->updateToolBox();
00151 
00152     //set the image paths, image sizes and collapseIcon position.
00153     d->themeChanged();
00154 
00155     setAcceptsHoverEvents(true);
00156 
00157     connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
00158 }
00159 
00160 ExtenderItem::~ExtenderItem()
00161 {
00162     //make sure the original mousepointer always get's restored.
00163     if (d->mouseOver) {
00164         QApplication::restoreOverrideCursor();
00165     }
00166     delete d;
00167 }
00168 
00169 KConfigGroup ExtenderItem::config() const
00170 {
00171     KConfigGroup cg = d->extender->d->applet->config("ExtenderItems");
00172     return KConfigGroup(&cg, QString::number(d->extenderItemId));
00173 }
00174 
00175 void ExtenderItem::setTitle(const QString &title)
00176 {
00177     d->title = title;
00178     config().writeEntry("extenderTitle", title);
00179     update();
00180 }
00181 
00182 QString ExtenderItem::title() const
00183 {
00184     return d->title;
00185 }
00186 
00187 void ExtenderItem::setName(const QString &name)
00188 {
00189     d->name = name;
00190     config().writeEntry("extenderItemName", name);
00191 }
00192 
00193 QString ExtenderItem::name() const
00194 {
00195     return d->name;
00196 }
00197 
00198 void ExtenderItem::setWidget(QGraphicsItem *widget)
00199 {
00200     widget->setParentItem(this);
00201 
00202     QSizeF panelSize(QSizeF(size().width() - d->bgLeft - d->bgRight,
00203                      d->iconSize() + d->dragTop + d->dragBottom));
00204     widget->setPos(QPointF(d->bgLeft + d->dragLeft, panelSize.height() +
00205                                                     d->bgTop + d->dragTop));
00206     d->widget = widget;
00207     setCollapsed(isCollapsed()); //updates the size hints.
00208 }
00209 
00210 QGraphicsItem *ExtenderItem::widget() const
00211 {
00212     return d->widget;
00213 }
00214 
00215 void ExtenderItem::setIcon(const QIcon &icon)
00216 {
00217     d->iconName.clear();
00218     d->collapseIcon->setIcon(icon);
00219 }
00220 
00221 void ExtenderItem::setIcon(const QString &icon)
00222 {
00223     if (icon != d->iconName) {
00224         d->collapseIcon->setIcon(icon);
00225         d->iconName = icon;
00226         config().writeEntry("extenderIconName", icon);
00227     }
00228 }
00229 
00230 QIcon ExtenderItem::icon() const
00231 {
00232     return d->collapseIcon->icon();
00233 }
00234 
00235 void ExtenderItem::setExtender(Extender *extender, const QPointF &pos)
00236 {
00237     Q_ASSERT(extender);
00238 
00239     if (extender == d->extender) {
00240         //We're not moving between extenders, so just insert this item back into the layout.
00241         setParentItem(extender);
00242         extender->d->addExtenderItem(this, pos);
00243         return;
00244     }
00245 
00246     //We are switching extender...
00247     //first remove this item from the old extender.
00248     d->extender->d->removeExtenderItem(this);
00249     emit d->extender->itemDetached(this);
00250 
00251     //move the configuration.
00252     if (d->hostApplet() && (extender != d->extender)) {
00253         KConfigGroup c = extender->d->applet->config("ExtenderItems");
00254         config().reparent(&c);
00255     }
00256 
00257     d->extender = extender;
00258 
00259     //change parent.
00260     setParentItem(extender);
00261     extender->d->addExtenderItem(this, pos);
00262 
00263     //cancel the timer.
00264     if (d->expirationTimer && isDetached()) {
00265         d->expirationTimer->stop();
00266         delete d->expirationTimer;
00267         d->expirationTimer = 0;
00268     }
00269 
00270     //set the background svg to that of the extender we're moving to.
00271     d->themeChanged();
00272 
00273     //we might have to enable or disable the returnToSource button.
00274     d->updateToolBox();
00275 }
00276 
00277 Extender *ExtenderItem::extender() const
00278 {
00279     return d->extender;
00280 }
00281 
00282 bool ExtenderItem::isCollapsed() const
00283 {
00284     return d->collapsed;
00285 }
00286 
00287 void ExtenderItem::setAutoExpireDelay(uint time)
00288 {
00289     if (!time) {
00290         if (d->expirationTimer) {
00291             d->expirationTimer->stop();
00292             delete d->expirationTimer;
00293             d->expirationTimer = 0;
00294         }
00295         return;
00296     }
00297 
00298     if (!isDetached()) {
00299         if (!d->expirationTimer) {
00300             d->expirationTimer = new QTimer(this);
00301             connect(d->expirationTimer, SIGNAL(timeout()), this, SLOT(destroy()));
00302         }
00303 
00304         d->expirationTimer->stop();
00305         d->expirationTimer->setSingleShot(true);
00306         d->expirationTimer->setInterval(time);
00307         d->expirationTimer->start();
00308     }
00309 }
00310 
00311 uint ExtenderItem::autoExpireDelay() const
00312 {
00313     if (d->expirationTimer) {
00314         return d->expirationTimer->interval();
00315     } else {
00316         return 0;
00317     }
00318 }
00319 
00320 bool ExtenderItem::isDetached() const
00321 {
00322     if (d->hostApplet()) {
00323         return (d->sourceApplet != d->hostApplet());
00324     } else {
00325         return false;
00326     }
00327 }
00328 
00329 void ExtenderItem::addAction(const QString &name, QAction *action)
00330 {
00331     Q_ASSERT(action);
00332     if (d->actionsInOrder.contains(action)) {
00333         return;
00334     }
00335 
00336     d->actions.insert(name, action);
00337     d->actionsInOrder.append(action);
00338     connect(action, SIGNAL(changed()), this, SLOT(updateToolBox()));
00339     connect(action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
00340     d->updateToolBox();
00341 }
00342 
00343 QAction *ExtenderItem::action(const QString &name) const
00344 {
00345     return d->actions.value(name, 0);
00346 }
00347 
00348 void ExtenderItem::showCloseButton()
00349 {
00350     if (d->destroyActionVisibility) {
00351         return;
00352     }
00353 
00354     d->destroyActionVisibility = true;
00355     d->updateToolBox();
00356 }
00357 
00358 void ExtenderItem::hideCloseButton()
00359 {
00360     if (!d->destroyActionVisibility) {
00361         return;
00362     }
00363 
00364     d->destroyActionVisibility = false;
00365     d->updateToolBox();
00366 }
00367 
00368 void ExtenderItem::destroy()
00369 {
00370     if (d->mousePressed) {
00371         //avoid being destroyed while we're being dragged.
00372         return;
00373     }
00374 
00375     d->hostApplet()->config("ExtenderItems").deleteGroup(QString::number(d->extenderItemId));
00376     d->extender->d->removeExtenderItem(this);
00377     deleteLater();
00378 }
00379 
00380 void ExtenderItem::setCollapsed(bool collapsed)
00381 {
00382     config().writeEntry("isCollapsed", collapsed);
00383     d->collapsed = collapsed;
00384 
00385     qreal marginWidth = d->bgLeft + d->bgRight + d->dragLeft + d->dragRight;
00386     qreal marginHeight = d->bgTop + d->bgBottom + 2 * d->dragTop + 2 * d->dragBottom;
00387 
00388     if (!d->widget) {
00389         setPreferredSize(QSizeF(200, d->dragHandleRect().height()));
00390         setMinimumSize(QSizeF(0, d->dragHandleRect().height()));
00391         //FIXME: wasn't there some sort of QWIDGETMAXSIZE thingy?
00392         setMaximumSize(QSizeF(1000, d->dragHandleRect().height()));
00393         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
00394         updateGeometry();
00395         return;
00396     }
00397 
00398     d->widget->setVisible(!collapsed);
00399 
00400     QSizeF min;
00401     QSizeF pref;
00402     QSizeF max;
00403 
00404     if (d->widget->isWidget()) {
00405         QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
00406         min = graphicsWidget->minimumSize();
00407         pref = graphicsWidget->preferredSize();
00408         max = graphicsWidget->maximumSize();
00409     } else {
00410         min = d->widget->boundingRect().size();
00411         pref = d->widget->boundingRect().size();
00412         max = d->widget->boundingRect().size();
00413     }
00414 
00415     if (collapsed) {
00416         setPreferredSize(QSizeF(pref.width() + marginWidth,
00417                                 d->dragHandleRect().height() + marginHeight));
00418         setMinimumSize(QSizeF(min.width() + marginWidth,
00419                               d->dragHandleRect().height() + marginHeight));
00420         setMaximumSize(QSizeF(max.width() + marginWidth,
00421                               d->dragHandleRect().height() + marginHeight));
00422         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
00423 
00424         if (d->collapseIcon) {
00425             d->collapseIcon->setToolTip(i18n("Expand this widget"));
00426         }
00427     } else {
00428         setPreferredSize(QSizeF(pref.width() + marginWidth,
00429                          pref.height() + d->dragHandleRect().height() + marginHeight));
00430         setMinimumSize(QSizeF(min.width() + marginWidth,
00431                               min.height() + d->dragHandleRect().height() + marginHeight));
00432         setMaximumSize(QSizeF(max.width() + marginWidth,
00433                               max.height() + d->dragHandleRect().height() + marginHeight));
00434 
00435         if (d->widget->isWidget()) {
00436             setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00437         } else {
00438             setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
00439         }
00440 
00441         if (d->collapseIcon) {
00442             d->collapseIcon->setToolTip(i18n("Collapse this widget"));
00443         }
00444     }
00445 
00446     updateGeometry();
00447     d->extender->d->adjustSizeHints();
00448 }
00449 
00450 void ExtenderItem::returnToSource()
00451 {
00452     if (!d->sourceApplet) {
00453         return;
00454     }
00455     setExtender(d->sourceApplet->d->extender);
00456 }
00457 
00458 void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
00459                          QWidget *widget)
00460 {
00461     Q_UNUSED(option);
00462     Q_UNUSED(widget);
00463 
00464     painter->setRenderHint(QPainter::TextAntialiasing, true);
00465     painter->setRenderHint(QPainter::Antialiasing, true);
00466 
00467     if (d->background->enabledBorders() != (FrameSvg::LeftBorder | FrameSvg::RightBorder) &&
00468         d->background->enabledBorders()) {
00469         //Don't paint if only the left and right borders are enabled, we only use the left and right
00470         //border in this situation to set the correct margins on this item.
00471         d->background->paintFrame(painter, QPointF(0, 0));
00472     }
00473 
00474     d->dragger->paintFrame(painter, QPointF(d->bgLeft, d->bgTop));
00475 
00476     //We don't need to paint a title if there is no title.
00477     if (d->title.isEmpty()) {
00478         return;
00479     }
00480 
00481     //set the font for the title.
00482     Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00483     QFont font = theme->font(Plasma::Theme::DefaultFont);
00484     font.setWeight(QFont::Bold);
00485 
00486     //create a pixmap with the title that is faded out at the right side of the titleRect.
00487     QRectF rect = QRectF(d->titleRect().width() - 30, 0, 30, d->titleRect().height());
00488 
00489     QPixmap pixmap(d->titleRect().size().toSize());
00490     pixmap.fill(Qt::transparent);
00491 
00492     QPainter p(&pixmap);
00493     p.setPen(theme->color(Plasma::Theme::TextColor));
00494     p.setFont(font);
00495     p.drawText(QRectF(QPointF(0, 0), d->titleRect().size()),
00496                Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignLeft,
00497                d->title);
00498 
00499     // Create the alpha gradient for the fade out effect
00500     QLinearGradient alphaGradient(0, 0, 1, 0);
00501     alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
00502     //TODO: correct handling of right to left text.
00503     alphaGradient.setColorAt(0, QColor(0, 0, 0, 255));
00504     alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
00505 
00506     p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00507     p.fillRect(rect, alphaGradient);
00508 
00509     p.end();
00510 
00511     painter->drawPixmap(d->titleRect().topLeft(), pixmap);
00512 }
00513 
00514 void ExtenderItem::moveEvent(QGraphicsSceneMoveEvent *event)
00515 {
00516     Q_UNUSED(event);
00517 
00518     if (d->toplevel) {
00519         d->toplevel->setSceneRect(sceneBoundingRect());
00520         d->toplevel->setMask(d->background->mask());
00521     }
00522 }
00523 
00524 void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
00525 {
00526     d->resizeContent(event->newSize());
00527 
00528     if (d->toplevel) {
00529         d->toplevel->setSceneRect(sceneBoundingRect());
00530         d->toplevel->setMask(d->background->mask());
00531     }
00532 }
00533 
00534 void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
00535 {
00536     if (!(d->dragHandleRect().contains(event->pos())) ||
00537         d->extender->d->applet->immutability() != Plasma::Mutable) {
00538         event->ignore();
00539         return;
00540     }
00541 
00542     QApplication::setOverrideCursor(Qt::ClosedHandCursor);
00543 
00544     d->mousePos = event->pos().toPoint();
00545     d->deltaScene = pos();
00546     d->mousePressed = true;
00547 }
00548 
00549 void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00550 {
00551     if (d->mousePressed && !d->dragStarted &&
00552         (d->mousePos - event->pos().toPoint()).manhattanLength() >= QApplication::startDragDistance()) {
00553         //start the drag:
00554         d->dragStarted = true;
00555 
00556         //set the zValue to the maximum.
00557         d->hostApplet()->raise();
00558         setZValue(d->hostApplet()->zValue());
00559 
00560         QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
00561         if (!mousePos.isNull()) {
00562             d->extender->itemHoverMoveEvent(this, d->extender->mapFromScene(mousePos));
00563         }
00564         d->extender->d->removeExtenderItem(this);
00565 
00566         d->themeChanged();
00567     } else if (!d->dragStarted) {
00568         return;
00569     }
00570 
00571     //keep track of the movement in scene coordinates. we use this to set the position of the
00572     //applet when remaining in the current view.
00573     d->deltaScene += event->scenePos() - event->lastScenePos();
00574 
00575     //set a rect in screencoordinates so we can check when we need to move to a toplevel
00576     //view.
00577     QRect screenRect = QRect();
00578     screenRect.setTopLeft(event->screenPos() - d->mousePos);
00579     screenRect.setSize(d->screenRect().size());
00580 
00581     Corona *corona = d->hostApplet()->containment()->corona();
00582 
00583     if (d->leaveCurrentView(screenRect)) {
00584         //we're moving the applet to a toplevel view, so place it somewhere out of sight
00585         //first: in the topleft quadrant.
00586 
00587         if (!d->toplevel) {
00588             //FIXME: duplication from applethandle
00589             //create a toplevel view and aim it at the applet.
00590             corona->addOffscreenWidget(this);
00591 
00592             d->toplevel = new ExtenderItemView(scene(), 0);
00593 
00594             d->toplevel->setWindowFlags(
00595                 Qt::ToolTip | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
00596             d->toplevel->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00597             d->toplevel->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00598             d->toplevel->setFrameShape(QFrame::NoFrame);
00599 
00600             d->toplevel->setSceneRect(sceneBoundingRect());
00601             d->toplevel->setGeometry(screenRect);
00602             d->toplevel->setMask(d->background->mask());
00603 
00604             d->toplevel->show();
00605         }
00606 
00607         d->toplevel->setSceneRect(sceneBoundingRect());
00608         d->toplevel->setGeometry(screenRect);
00609         d->toplevel->setMask(d->background->mask());
00610     } else {
00611         corona->removeOffscreenWidget(this);
00612         setParentItem(d->hostApplet());
00613         setPos(d->deltaScene);
00614 
00615        //remove the toplevel view.
00616         delete d->toplevel;
00617         d->toplevel = 0;
00618     }
00619 
00620     //let's insert spacers in applets we're hovering over for some useful visual feedback.
00621     //check which view we're hovering over and use that information to get the mouse
00622     //position in scene coordinates (event->scenePos won't work, since it doesn't take in
00623     //consideration that you're leaving the current view).
00624     QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
00625 
00626     //find the extender we're hovering over.
00627     Extender *targetExtender = 0;
00628 
00629     if (!mousePos.isNull()) {
00630         foreach (Containment *containment, corona->containments()) {
00631             foreach (Applet *applet, containment->applets()) {
00632                 if (applet->d->extender &&
00633                         (applet->sceneBoundingRect().contains(mousePos) ||
00634                          applet->d->extender->sceneBoundingRect().contains(mousePos))) {
00635                     targetExtender = applet->d->extender;
00636 
00637                     //check if we're hovering over an popupapplet, and open it up in case it does.
00638                     PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet);
00639                     if (popupApplet && (applet->formFactor() == Plasma::Horizontal ||
00640                                 applet->formFactor() == Plasma::Vertical)) {
00641                         popupApplet->showPopup();
00642                     }
00643                 }
00644             }
00645 
00646             if (containment->sceneBoundingRect().contains(mousePos) && !targetExtender) {
00647                 containment->showDropZone(containment->mapFromScene(mousePos).toPoint());
00648             } else {
00649                 containment->showDropZone(QPoint());
00650             }
00651         }
00652     }
00653 
00654     //remove any previous spacers.
00655     if (targetExtender != d->previousTargetExtender) {
00656         if (d->previousTargetExtender) {
00657             d->previousTargetExtender->itemHoverLeaveEvent(this);
00658             d->previousTargetExtender->disconnect(this, SIGNAL(destroyed(QObject*)));
00659         }
00660 
00661         d->previousTargetExtender = targetExtender;
00662 
00663         //monitor the current previousTargetExtender, so we don't accidently try to remove it's
00664         //spacer after the extender has been destoryed.
00665         if (targetExtender) {
00666             connect(targetExtender, SIGNAL(destroyed(QObject*)),
00667                     this, SLOT(previousTargetExtenderDestroyed(QObject*)));
00668         }
00669 
00670         if (targetExtender) {
00671             targetExtender->itemHoverEnterEvent(this);
00672         }
00673     }
00674 
00675     //insert a spacer if the applet accepts detachables.
00676     if (targetExtender) {
00677         if (targetExtender->sceneBoundingRect().contains(mousePos)) {
00678             targetExtender->itemHoverMoveEvent(this, targetExtender->mapFromScene(mousePos));
00679         }
00680     }
00681 }
00682 
00683 void ExtenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
00684 {
00685     if (d->titleRect().contains(event->pos())) {
00686         d->toggleCollapse();
00687     }
00688 }
00689 
00690 void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
00691 {
00692     if (d->titleRect().contains(event->pos()) &&
00693         d->extender->d->applet->immutability() == Plasma::Mutable) {
00694         if (!d->mouseOver) {
00695             QApplication::setOverrideCursor(Qt::OpenHandCursor);
00696             d->mouseOver = true;
00697         }
00698     } else {
00699         if (d->mouseOver) {
00700             QApplication::restoreOverrideCursor();
00701             d->mouseOver = false;
00702         }
00703     }
00704 }
00705 
00706 void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
00707 {
00708     Q_UNUSED(event);
00709 
00710     if (d->mouseOver) {
00711         QApplication::restoreOverrideCursor();
00712         d->mouseOver = false;
00713     }
00714 }
00715 
00716 void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00717 {
00718     if (d->mousePressed) {
00719         QApplication::restoreOverrideCursor();
00720     }
00721 
00722     d->mousePressed = false;
00723 
00724     if (d->dragStarted) {
00725         d->dragStarted = false;
00726         d->previousTargetExtender = 0;
00727 
00728         //remove the toplevel view
00729         if (d->toplevel) {
00730             delete d->toplevel;
00731             d->toplevel = 0;
00732         }
00733 
00734         //let's insert spacers in applets we're hovering over for some useful visual feedback.
00735         //check which view we're hovering over and use that information to get the mouse
00736         //position in scene coordinates (event->scenePos won't work, since it doesn't take in
00737         //consideration that you're leaving the current view).
00738         QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
00739 
00740         //find the extender we're hovering over.
00741         Extender *targetExtender = 0;
00742         Corona *corona = qobject_cast<Corona*>(d->extender->d->applet->scene());
00743 
00744         corona->removeOffscreenWidget(this);
00745 
00746         if (!mousePos.isNull()) {
00747             foreach (Containment *containment, corona->containments()) {
00748                 containment->showDropZone(QPoint());
00749                 foreach (Applet *applet, containment->applets()) {
00750                     if (applet->d->extender &&
00751                         (applet->sceneBoundingRect().contains(mousePos) ||
00752                          applet->d->extender->sceneBoundingRect().contains(mousePos))) {
00753                         targetExtender = applet->d->extender;
00754                     }
00755                 }
00756             }
00757         }
00758 
00759         //are we hovering over an applet that accepts extender items?
00760         if (targetExtender) {
00761             if (targetExtender->sceneBoundingRect().contains(mousePos)) {
00762                 setExtender(targetExtender, targetExtender->mapFromScene(mousePos));
00763             } else {
00764                 setExtender(targetExtender);
00765             }
00766         } else {
00767             //apparently, it is not, so instantiate a new ExtenderApplet.
00768             bool extenderCreated = false;
00769             mousePos = d->scenePosFromScreenPos(event->screenPos());
00770             if (!mousePos.isNull()) {
00771                 foreach (Containment *containment, corona->containments()) {
00772                     if (containment->sceneBoundingRect().contains(mousePos)) {
00773                         kDebug() << "Instantiate a new ExtenderApplet";
00774 
00775                         //when on a desktopcontainment, we use the widgets topleft corner to
00776                         //position the applet in the containment, on other containments, we use the
00777                         //actual mouse position.
00778                         QPointF position;
00779                         if (containment->type() == Plasma::Containment::DesktopContainment) {
00780                             position = containment->mapFromScene(
00781                                        d->scenePosFromScreenPos(event->screenPos() - d->mousePos));
00782                         } else {
00783                             position = containment->mapFromScene(mousePos);
00784                         }
00785 
00786                         Applet *applet = containment->addApplet("internal:extender",
00787                                 QVariantList(),
00788                                 QRectF(containment->mapFromScene(mousePos), size()));
00789 
00790                         if (applet) {
00791                             setExtender(applet->d->extender);
00792                             QSizeF margin = applet->size() - applet->contentsRect().size();
00793                             applet->setMinimumSize(minimumSize() + margin);
00794                             applet->setPreferredSize(preferredSize() + margin);
00795                             applet->resize(preferredSize());
00796 
00797                             extenderCreated = true;
00798                         } else {
00799                             kDebug() << "Creating internal:extender applet failed, probably "
00800                                      << "because widgets are locked.";
00801                         }
00802                     }
00803                 }
00804             }
00805 
00806             //if no containment is at the position where the item is dropped, just move the item
00807             //back to where it came from.
00808             if (!extenderCreated) {
00809                 setExtender(extender());
00810                 kDebug() << "Move the item back to where it came from.";
00811             }
00812         }
00813     }
00814 }
00815 
00816 ExtenderItemPrivate::ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
00817     : q(extenderItem),
00818       widget(0),
00819       toolbox(0),
00820       toplevel(0),
00821       previousTargetExtender(0),
00822       extender(hostExtender),
00823       sourceApplet(0),
00824       dragger(new FrameSvg(extenderItem)),
00825       background(new FrameSvg(extenderItem)),
00826       collapseIcon(0),
00827       title(QString()),
00828       mousePressed(false),
00829       mouseOver(false),
00830       dragStarted(false),
00831       destroyActionVisibility(false),
00832       collapsed(false),
00833       expirationTimer(0)
00834 {
00835     dragLeft = dragTop = dragRight = dragBottom = 0;
00836     bgLeft = bgTop = bgRight = bgBottom = 0;
00837 }
00838 
00839 ExtenderItemPrivate::~ExtenderItemPrivate()
00840 {
00841     delete widget;
00842     widget = 0;
00843     delete toplevel;
00844 }
00845 
00846 //returns a Rect containing the area of the detachable where the draghandle will be drawn.
00847 QRectF ExtenderItemPrivate::dragHandleRect()
00848 {
00849     QSizeF panelSize(QSizeF(q->size().width() - bgLeft - bgRight,
00850                      iconSize() + dragTop + dragBottom));
00851     return QRectF(QPointF(bgLeft, bgTop), panelSize);
00852 }
00853 
00854 QRectF ExtenderItemPrivate::titleRect()
00855 {
00856     return dragHandleRect().adjusted(dragLeft + collapseIcon->size().width() + 1, dragTop,
00857                                      -toolbox->size().width() - dragRight, -dragBottom);
00858 }
00859 
00860 bool ExtenderItemPrivate::leaveCurrentView(const QRect &rect)
00861 {
00862     if ((q->sceneBoundingRect().left() < 0)) {
00863         //always leaveCurrentView when the item is in a Plasma::Dialog which can easy be
00864         //seen by checking if it is in topleft quadrant and it's not just there because it's
00865         //being viewed by the toplevel view.
00866         return true;
00867     }
00868 
00869     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
00870         if (widget->geometry().intersects(rect) && widget->isVisible() && widget != toplevel) {
00871             //is this widget a plasma view, a different view then our current one,
00872             //AND not a dashboardview?
00873             QGraphicsView *v = qobject_cast<QGraphicsView*>(widget);
00874             QGraphicsView *currentV = 0;
00875 
00876             if (hostApplet()) {
00877                 currentV = qobject_cast<QGraphicsView*>(hostApplet()->containment()->view());
00878             }
00879 
00880             if (v && v != currentV) {
00881                 return true;
00882             }
00883         }
00884     }
00885 
00886     return false;
00887 }
00888 
00889 QRect ExtenderItemPrivate::screenRect()
00890 {
00891     return hostApplet()->containment()->view()
00892                        ->mapFromScene(q->sceneBoundingRect()).boundingRect();
00893 }
00894 
00895 void ExtenderItemPrivate::toggleCollapse()
00896 {
00897     q->setCollapsed(!q->isCollapsed());
00898 }
00899 
00900 void ExtenderItemPrivate::updateToolBox()
00901 {
00902     Q_ASSERT(toolbox);
00903     Q_ASSERT(dragger);
00904     Q_ASSERT(toolboxLayout);
00905 
00906     uint iconHeight = iconSize();
00907 
00908     //TODO: only delete items that actually have to be deleted, current performance is horrible.
00909     while (toolboxLayout->count()) {
00910         QGraphicsLayoutItem *icon = toolboxLayout->itemAt(0);
00911         QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(icon);
00912         widget->deleteLater();
00913         toolboxLayout->removeAt(0);
00914     }
00915 
00916     //add the actions that are actually set to visible.
00917     foreach (QAction *action, actionsInOrder) {
00918         if (action->isVisible()) {
00919             IconWidget *icon = new IconWidget(q);
00920             icon->setAction(action);
00921             QSizeF iconSize = icon->sizeFromIconSize(iconHeight);
00922             icon->setMinimumSize(iconSize);
00923             icon->setMaximumSize(iconSize);
00924             toolboxLayout->addItem(icon);
00925         }
00926     }
00927 
00928     //add the returntosource icon if we are detached, and have a source applet.
00929     if (q->isDetached() && sourceApplet) {
00930         IconWidget *returnToSource = new IconWidget(q);
00931         returnToSource->setSvg("widgets/configuration-icons", "return-to-source");
00932         QSizeF iconSize = returnToSource->sizeFromIconSize(iconHeight);
00933         returnToSource->setMinimumSize(iconSize);
00934         returnToSource->setMaximumSize(iconSize);
00935 
00936         toolboxLayout->addItem(returnToSource);
00937         QObject::connect(returnToSource, SIGNAL(clicked()), q, SLOT(returnToSource()));
00938     }
00939 
00940     //add the close icon if desired.
00941     if (destroyActionVisibility) {
00942         IconWidget *destroyAction = new IconWidget(q);
00943         destroyAction->setSvg("widgets/configuration-icons", "close");
00944         QSizeF iconSize = destroyAction->sizeFromIconSize(iconHeight);
00945         destroyAction->setMinimumSize(iconSize);
00946         destroyAction->setMaximumSize(iconSize);
00947 
00948         toolboxLayout->addItem(destroyAction);
00949         QObject::connect(destroyAction, SIGNAL(clicked()), q, SLOT(destroy()));
00950     }
00951 
00952     toolboxLayout->updateGeometry();
00953 
00954     //position the toolbox correctly.
00955     QSizeF minimum = toolboxLayout->minimumSize();
00956     toolbox->resize(minimum);
00957     repositionToolbox();
00958 }
00959 
00960 void ExtenderItemPrivate::repositionToolbox()
00961 {
00962     QSizeF minimum = toolboxLayout->minimumSize();
00963     toolbox->setPos(q->size().width() - minimum.width() - bgRight,
00964                     (dragHandleRect().height()/2) -
00965                     (minimum.height()/2) + bgTop);
00966 }
00967 
00968 //TODO: something like this as static function in corona might be a good idea.
00969 QPointF ExtenderItemPrivate::scenePosFromScreenPos(const QPoint &pos) const
00970 {
00971     //get the stacking order of the toplevel windows and remove the toplevel view that's
00972     //only here while dragging, since we're not interested in finding that.
00973     QList<WId> order = KWindowSystem::stackingOrder();
00974     if (toplevel) {
00975         order.removeOne(toplevel->winId());
00976     }
00977 
00978     QGraphicsView *found = 0;
00979     foreach (QWidget *w, QApplication::topLevelWidgets()) {
00980         QGraphicsView *v = 0;
00981 
00982         //first check if we're over a Dialog.
00983         Dialog *dialog = qobject_cast<Dialog*>(w);
00984         if (dialog) {
00985             if (dialog->isVisible() && dialog->geometry().contains(pos)) {
00986                 v = qobject_cast<QGraphicsView*>(dialog->layout()->itemAt(0)->widget());
00987                 if (v) {
00988                     return v->mapToScene(v->mapFromGlobal(pos));
00989                 }
00990             }
00991         } else {
00992             v = qobject_cast<QGraphicsView *>(w);
00993         }
00994 
00995         //else check if it is a QGV:
00996         if (v && w->isVisible() && w->geometry().contains(pos)) {
00997             if (found && order.contains(found->winId())) {
00998                 if (order.indexOf(found->winId()) < order.indexOf(v->winId())) {
00999                     found = v;
01000                 }
01001             } else {
01002                 found = v;
01003             }
01004         }
01005     }
01006 
01007     if (!found) {
01008         return QPointF();
01009     }
01010 
01011     return found->mapToScene(found->mapFromGlobal(pos));
01012 }
01013 
01014 Applet *ExtenderItemPrivate::hostApplet() const
01015 {
01016     if (extender) {
01017         return extender->d->applet;
01018     } else {
01019         return 0;
01020     }
01021 }
01022 
01023 void ExtenderItemPrivate::themeChanged()
01024 {
01025     background->setImagePath("widgets/extender-background");
01026     if (mousePressed) {
01027         background->setEnabledBorders(FrameSvg::AllBorders);
01028     } else {
01029         background->setEnabledBorders(extender->enabledBordersForItem(q));
01030     }
01031     background->getMargins(bgLeft, bgTop, bgRight, bgBottom);
01032 
01033     dragger->setImagePath("widgets/extender-dragger");
01034     dragger->getMargins(dragLeft, dragTop, dragRight, dragBottom);
01035 
01036     QSizeF panelSize(QSizeF(q->size().width() - bgLeft - bgRight,
01037                      iconSize() + dragTop + dragBottom));
01038 
01039     //resize the collapse icon.
01040     collapseIcon->resize(collapseIcon->sizeFromIconSize(iconSize()));
01041 
01042     //reposition the collapse icon based on the new margins and size.
01043     collapseIcon->setPos(bgLeft + dragLeft,
01044                          panelSize.height()/2 -
01045                          collapseIcon->size().height()/2 + bgTop);
01046 
01047     //reposition the widget based on the new margins.
01048     if (widget) {
01049         widget->setPos(QPointF(bgLeft + dragLeft, panelSize.height() +
01050                                                   bgTop + dragTop));
01051     }
01052 
01053     //reposition the toolbox.
01054     repositionToolbox();
01055 
01056     //setCollapsed recalculates size hints.
01057     q->setCollapsed(q->isCollapsed());
01058 
01059     if (!q->size().isEmpty())
01060         resizeContent(q->size());
01061 }
01062 
01063 void ExtenderItemPrivate::sourceAppletRemoved()
01064 {
01065     //the original source applet is removed, set the pointer to 0 and no longer show the return to
01066     //source icon.
01067     sourceApplet = 0;
01068     updateToolBox();
01069 }
01070 
01071 qreal ExtenderItemPrivate::iconSize()
01072 {
01073     //read the icon size hint, and enforce a minimum size of 16x16
01074     QSizeF size = dragger->elementSize("hint-preferred-icon-size");
01075     size = size.expandedTo(QSizeF(16,16));
01076 
01077     //return the size of the text, if that is larger then the recommended icon size
01078     Plasma::Theme *theme = Plasma::Theme::defaultTheme();
01079     QFont font = theme->font(Plasma::Theme::DefaultFont);
01080     QFontMetrics fm(font);
01081 
01082     return qMax(size.height(), (qreal) fm.height());
01083 }
01084 
01085 void ExtenderItemPrivate::resizeContent(const QSizeF &newSize)
01086 {
01087     qreal width = newSize.width();
01088     qreal height = newSize.height();
01089 
01090     //resize the dragger
01091     dragger->resizeFrame(QSizeF(width - bgLeft - bgRight,
01092                          iconSize() + dragTop + dragBottom));
01093 
01094     //resize the applet background
01095     background->resizeFrame(newSize);
01096 
01097     //resize the widget
01098     if (widget && widget->isWidget()) {
01099         QSizeF newWidgetSize(width - bgLeft - bgRight - dragLeft - dragRight,
01100                              height - dragHandleRect().height() - bgTop - bgBottom -
01101                              2 * dragTop - 2 * dragBottom);
01102 
01103         QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(widget);
01104         graphicsWidget->resize(newWidgetSize);
01105     }
01106 
01107     //reposition the toolbox.
01108     repositionToolbox();
01109 
01110     q->update();
01111 }
01112 
01113 void ExtenderItemPrivate::previousTargetExtenderDestroyed(QObject *o)
01114 {
01115     Q_UNUSED(o)
01116     previousTargetExtender = 0;
01117 }
01118 
01119 void ExtenderItemPrivate::actionDestroyed(QObject *o)
01120 {
01121     QAction *action = static_cast<QAction *>(o);
01122     QMutableHashIterator<QString, QAction *> hit(actions);
01123     while (hit.hasNext()) {
01124         if (hit.next().value() == action) {
01125             hit.remove();
01126             break;
01127         }
01128     }
01129 
01130     QMutableListIterator<QAction *> lit(actionsInOrder);
01131     while (lit.hasNext()) {
01132         if (lit.next() == action) {
01133             lit.remove();
01134             break;
01135         }
01136     }
01137 }
01138 
01139 uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
01140 
01141 } // namespace Plasma
01142 
01143 #include "extenderitem.moc"

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