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

Plasma

extenderitem.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2008, 2009 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 <QAction>
00023 #include <QApplication>
00024 #include <QBitmap>
00025 #include <QDrag>
00026 #include <QGraphicsSceneResizeEvent>
00027 #include <QGraphicsSceneMouseEvent>
00028 #include <QGraphicsLinearLayout>
00029 #include <QLayout>
00030 #include <QMimeData>
00031 #include <QPainter>
00032 #include <QTimer>
00033 
00034 #include <kdebug.h>
00035 #include <kicon.h>
00036 #include <kiconloader.h>
00037 
00038 #include "applet.h"
00039 #include "containment.h"
00040 #include "corona.h"
00041 #include "dialog.h"
00042 #include "extender.h"
00043 #include "extendergroup.h"
00044 #include "framesvg.h"
00045 #include "popupapplet.h"
00046 #include "theme.h"
00047 #include "view.h"
00048 
00049 #include "widgets/iconwidget.h"
00050 
00051 #include "private/applethandle_p.h"
00052 #include "private/extender_p.h"
00053 #include "private/extenderapplet_p.h"
00054 #include "private/extendergroup_p.h"
00055 #include "private/extenderitem_p.h"
00056 #include "private/extenderitemmimedata_p.h"
00057 
00058 namespace Plasma
00059 {
00060 
00061 ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
00062         : QGraphicsWidget(hostExtender),
00063           d(new ExtenderItemPrivate(this, hostExtender))
00064 {
00065     Q_ASSERT(hostExtender);
00066 
00067     //set the extenderId
00068     if (extenderItemId) {
00069         d->extenderItemId = extenderItemId;
00070         ExtenderItemPrivate::s_maxExtenderItemId =
00071             qMax(ExtenderItemPrivate::s_maxExtenderItemId, extenderItemId);
00072     } else {
00073         d->extenderItemId = ++ExtenderItemPrivate::s_maxExtenderItemId;
00074     }
00075 
00076     //create the toolbox.
00077     d->toolbox = new QGraphicsWidget(this);
00078     d->toolboxLayout = new QGraphicsLinearLayout(d->toolbox);
00079     d->toolbox->setLayout(d->toolboxLayout);
00080 
00081     //create items's configgroup
00082     KConfigGroup cg = hostExtender->d->applet->config("ExtenderItems");
00083     KConfigGroup dg = KConfigGroup(&cg, QString::number(d->extenderItemId));
00084 
00085     uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
00086 
00087     //check if we're creating a new item or reinstantiating an existing one.
00088     if (!sourceAppletId) {
00089         //The item is new
00090         dg.writeEntry("sourceAppletPluginName", hostExtender->d->applet->pluginName());
00091         dg.writeEntry("sourceAppletId", hostExtender->d->applet->id());
00092         dg.writeEntry("extenderIconName", hostExtender->d->applet->icon());
00093         d->sourceApplet = hostExtender->d->applet;
00094         d->collapseIcon = new IconWidget(KIcon(hostExtender->d->applet->icon()), "", this);
00095     } else {
00096         //The item already exists.
00097         d->name = dg.readEntry("extenderItemName", "");
00098         d->title = dg.readEntry("extenderTitle", "");
00099         setCollapsed(dg.readEntry("isCollapsed", false));
00100 
00101         QString iconName = dg.readEntry("extenderIconName", "utilities-desktop-extra");
00102         if (iconName.isEmpty()) {
00103             iconName = "utilities-desktop-extra";
00104         }
00105         d->collapseIcon = new IconWidget(KIcon(iconName), "", this);
00106 
00107         //Find the group if it's already there.
00108         QString groupName = dg.readEntry("group", "");
00109         d->group = hostExtender->d->findGroup(groupName);
00110 
00111         //Find the sourceapplet.
00112         Corona *corona = hostExtender->d->applet->containment()->corona();
00113         foreach (Containment *containment, corona->containments()) {
00114             foreach (Applet *applet, containment->applets()) {
00115                 if (applet->id() == sourceAppletId &&
00116                         applet->pluginName() == dg.readEntry("sourceAppletPluginName", "")) {
00117                     d->sourceApplet = applet;
00118                 }
00119             }
00120         }
00121     }
00122 
00123     //make sure we keep monitoring if the source applet still exists, so the return to source icon
00124     //can be hidden if it is removed.
00125     connect(d->sourceApplet, SIGNAL(destroyed()), this, SLOT(sourceAppletRemoved()));
00126     connect(d->collapseIcon, SIGNAL(clicked()), this, SLOT(toggleCollapse()));
00127 
00128     //set the extender we want to move to.
00129     setExtender(hostExtender);
00130 
00131     //show or hide the return to source icon.
00132     d->updateToolBox();
00133 
00134     //set the image paths, image sizes and collapseIcon position.
00135     d->themeChanged();
00136 
00137     setAcceptsHoverEvents(true);
00138 
00139     connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
00140 }
00141 
00142 ExtenderItem::~ExtenderItem()
00143 {
00144     //make sure the original mousepointer always get's restored.
00145     if (d->mouseOver) {
00146         QApplication::restoreOverrideCursor();
00147     }
00148     delete d;
00149 }
00150 
00151 KConfigGroup ExtenderItem::config() const
00152 {
00153     KConfigGroup cg = d->extender->d->applet->config("ExtenderItems");
00154     return KConfigGroup(&cg, QString::number(d->extenderItemId));
00155 }
00156 
00157 void ExtenderItem::setTitle(const QString &title)
00158 {
00159     if (d->title != title) {
00160         d->title = title;
00161         config().writeEntry("extenderTitle", title);
00162         update();
00163     }
00164 }
00165 
00166 QString ExtenderItem::title() const
00167 {
00168     return d->title;
00169 }
00170 
00171 void ExtenderItem::setName(const QString &name)
00172 {
00173     d->name = name;
00174     config().writeEntry("extenderItemName", name);
00175 }
00176 
00177 QString ExtenderItem::name() const
00178 {
00179     return d->name;
00180 }
00181 
00182 void ExtenderItem::setWidget(QGraphicsItem *widget)
00183 {
00184     if (d->widget) {
00185         d->widget->removeSceneEventFilter(this);
00186     }
00187 
00188     if (!widget) {
00189         d->widget = 0;
00190         return;
00191     }
00192 
00193     widget->setParentItem(this);
00194     widget->installSceneEventFilter(this);
00195 
00196     QSizeF panelSize(QSizeF(size().width() - d->bgLeft - d->bgRight,
00197                      d->iconSize + d->dragTop + d->dragBottom));
00198     widget->setPos(QPointF(d->bgLeft + d->dragLeft, panelSize.height() + d->bgTop));
00199     d->widget = widget;
00200     d->updateSizeHints();
00201 }
00202 
00203 QGraphicsItem *ExtenderItem::widget() const
00204 {
00205     return d->widget;
00206 }
00207 
00208 void ExtenderItem::setIcon(const QIcon &icon)
00209 {
00210     if (d->collapseIcon->icon().isNull() || icon.cacheKey() != d->collapseIcon->icon().cacheKey()) {
00211         d->iconName.clear();
00212         d->collapseIcon->setIcon(icon);
00213     }
00214 }
00215 
00216 void ExtenderItem::setIcon(const QString &icon)
00217 {
00218     if (icon != d->iconName) {
00219         d->collapseIcon->setIcon(icon);
00220         d->iconName = icon;
00221         config().writeEntry("extenderIconName", icon);
00222     }
00223 }
00224 
00225 QIcon ExtenderItem::icon() const
00226 {
00227     return d->collapseIcon->icon();
00228 }
00229 
00230 void ExtenderItem::setExtender(Extender *extender, const QPointF &pos)
00231 {
00232     Q_ASSERT(extender);
00233 
00234     //themeChanged() has to now that by now, we're no longer dragging, even though the QDrag has not
00235     //been entirely finished.
00236     d->dragStarted = false;
00237 
00238     ExtenderGroup *group = qobject_cast<ExtenderGroup*>(this);
00239     QList<ExtenderItem*> childItems;
00240     if (group) {
00241         childItems = group->items();
00242     }
00243 
00244     if (extender == d->extender) {
00245         //We're not moving between extenders, so just insert this item back into the layout.
00246         setParentItem(extender);
00247         extender->d->addExtenderItem(this, pos);
00248         return;
00249     }
00250 
00251     //We are switching extender...
00252     //first remove this item from the old extender.
00253     d->extender->d->removeExtenderItem(this);
00254 
00255     //move the configuration.
00256     if (d->hostApplet() && (extender != d->extender)) {
00257         KConfigGroup c = extender->d->applet->config("ExtenderItems");
00258         config().reparent(&c);
00259     }
00260 
00261     //and notify the applet of the item being detached, after the config has been moved.
00262     emit d->extender->itemDetached(this);
00263 
00264     d->extender = extender;
00265 
00266     //change parent.
00267     setParentItem(extender);
00268     extender->d->addExtenderItem(this, pos);
00269 
00270     //cancel the timer.
00271     if (d->expirationTimer && isDetached()) {
00272         d->expirationTimer->stop();
00273         delete d->expirationTimer;
00274         d->expirationTimer = 0;
00275     }
00276 
00277     Corona *corona = qobject_cast<Corona*>(scene());
00278     KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
00279 
00280     if (isDetached()) {
00281         kDebug() << "detached, adding entry to the global group";
00282         KConfigGroup itemConfig = extenderItemGroup.group(QString::number(d->extenderItemId));
00283         itemConfig.writeEntry("sourceAppletPluginName",
00284                 config().readEntry("sourceAppletPluginName", ""));
00285         itemConfig.writeEntry("sourceAppletId",
00286                 config().readEntry("sourceAppletId", 0));
00287         itemConfig.writeEntry("extenderItemName",
00288                 config().readEntry("extenderItemName", ""));
00289     } else if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
00290         kDebug() << "no longer detached, removing entry from the global group";
00291         extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
00292     }
00293 
00294     //we might have to enable or disable the returnToSource button.
00295     d->updateToolBox();
00296 
00297     //invoke setGroup on all items belonging to this group, to make sure all children move to the
00298     //new extender together with the group.
00299     if (group) {
00300         foreach (ExtenderItem *item, childItems) {
00301             item->setGroup(group);
00302         }
00303     }
00304 }
00305 
00306 Extender *ExtenderItem::extender() const
00307 {
00308     return d->extender;
00309 }
00310 
00311 void ExtenderItem::setGroup(ExtenderGroup *group)
00312 {
00313     if (isGroup()) {
00314         //nesting extender groups is just insane. I don't think we'd even want to support that.
00315         kWarning() << "Nesting ExtenderGroups is not supported";
00316         return;
00317     }
00318 
00319     d->group = group;
00320 
00321     if (group) {
00322         config().writeEntry("group", group->name());
00323         //TODO: move to another extender if the group we set is actually detached.
00324         if (group->extender() != extender()) {
00325             kDebug() << "moving to another extender because we're joining a detached group.";
00326             setExtender(group->extender());
00327         }
00328         group->d->addItemToGroup(this);
00329     } else {
00330         config().deleteEntry("group");
00331     }
00332 }
00333 
00334 ExtenderGroup *ExtenderItem::group() const
00335 {
00336     return d->group;
00337 }
00338 
00339 bool ExtenderItem::isGroup() const
00340 {
00341     return (config().readEntry("isGroup", false));
00342 }
00343 
00344 bool ExtenderItem::isCollapsed() const
00345 {
00346     return d->collapsed;
00347 }
00348 
00349 void ExtenderItem::setAutoExpireDelay(uint time)
00350 {
00351     if (!time) {
00352         if (d->expirationTimer) {
00353             d->expirationTimer->stop();
00354             delete d->expirationTimer;
00355             d->expirationTimer = 0;
00356         }
00357         return;
00358     }
00359 
00360     if (!isDetached()) {
00361         if (!d->expirationTimer) {
00362             d->expirationTimer = new QTimer(this);
00363             connect(d->expirationTimer, SIGNAL(timeout()), this, SLOT(destroy()));
00364         }
00365 
00366         d->expirationTimer->stop();
00367         d->expirationTimer->setSingleShot(true);
00368         d->expirationTimer->setInterval(time);
00369         d->expirationTimer->start();
00370     }
00371 }
00372 
00373 uint ExtenderItem::autoExpireDelay() const
00374 {
00375     if (d->expirationTimer) {
00376         return d->expirationTimer->interval();
00377     } else {
00378         return 0;
00379     }
00380 }
00381 
00382 bool ExtenderItem::isDetached() const
00383 {
00384     if (d->hostApplet()) {
00385         return (d->sourceApplet != d->hostApplet());
00386     } else {
00387         return false;
00388     }
00389 }
00390 
00391 void ExtenderItem::addAction(const QString &name, QAction *action)
00392 {
00393     Q_ASSERT(action);
00394     if (d->actionsInOrder.contains(action)) {
00395         return;
00396     }
00397 
00398     d->actions.insert(name, action);
00399     d->actionsInOrder.append(action);
00400     connect(action, SIGNAL(changed()), this, SLOT(updateToolBox()));
00401     connect(action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
00402     d->updateToolBox();
00403 }
00404 
00405 QAction *ExtenderItem::action(const QString &name) const
00406 {
00407     return d->actions.value(name, 0);
00408 }
00409 
00410 void ExtenderItem::showCloseButton()
00411 {
00412     if (d->destroyActionVisibility) {
00413         return;
00414     }
00415 
00416     d->destroyActionVisibility = true;
00417     d->updateToolBox();
00418 }
00419 
00420 void ExtenderItem::hideCloseButton()
00421 {
00422     if (!d->destroyActionVisibility) {
00423         return;
00424     }
00425 
00426     d->destroyActionVisibility = false;
00427     d->updateToolBox();
00428 }
00429 
00430 void ExtenderItem::destroy()
00431 {
00432     if (d->dragStarted) {
00433         //avoid being destroyed while we're being dragged.
00434         return;
00435     }
00436 
00437     //remove global entry if needed.
00438     Corona *corona = qobject_cast<Corona*>(scene());
00439     KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
00440     if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
00441         extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
00442     }
00443 
00444     d->hostApplet()->config("ExtenderItems").deleteGroup(QString::number(d->extenderItemId));
00445     d->extender->d->removeExtenderItem(this);
00446     emit d->extender->itemDetached(this);
00447     deleteLater();
00448 }
00449 
00450 void ExtenderItem::setCollapsed(bool collapsed)
00451 {
00452     config().writeEntry("isCollapsed", collapsed);
00453     d->collapsed = collapsed;
00454     if (d->widget) {
00455         d->widget->setVisible(!collapsed);
00456         d->updateSizeHints();
00457     }
00458 }
00459 
00460 void ExtenderItem::returnToSource()
00461 {
00462     if (!d || !d->sourceApplet) {
00463         return;
00464     }
00465     if (d->sourceApplet->d) {
00466         setExtender(d->sourceApplet->extender());
00467     }
00468 }
00469 
00470 void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
00471                          QWidget *widget)
00472 {
00473     Q_UNUSED(option);
00474     Q_UNUSED(widget);
00475 
00476     painter->setRenderHint(QPainter::TextAntialiasing, true);
00477     painter->setRenderHint(QPainter::Antialiasing, true);
00478 
00479     if (d->background->enabledBorders() != (FrameSvg::LeftBorder | FrameSvg::RightBorder) &&
00480         d->background->enabledBorders()) {
00481         //Don't paint if only the left and right borders are enabled, we only use the left and right
00482         //border in this situation to set the correct margins on this item.
00483         d->background->paintFrame(painter, QPointF(0, 0));
00484     }
00485 
00486     d->dragger->paintFrame(painter, QPointF(d->bgLeft, d->bgTop));
00487 
00488     //We don't need to paint a title if there is no title.
00489     if (d->title.isEmpty()) {
00490         return;
00491     }
00492 
00493     //set the font for the title.
00494     Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00495     QFont font = theme->font(Plasma::Theme::DefaultFont);
00496     font.setWeight(QFont::Bold);
00497 
00498     //create a pixmap with the title that is faded out at the right side of the titleRect.
00499     QRectF rect = QRectF(d->titleRect().width() - 30, 0, 30, d->titleRect().height());
00500 
00501     QPixmap pixmap(d->titleRect().size().toSize());
00502     pixmap.fill(Qt::transparent);
00503 
00504     QPainter p(&pixmap);
00505     p.setPen(theme->color(Plasma::Theme::TextColor));
00506     p.setFont(font);
00507     p.drawText(QRectF(QPointF(0, 0), d->titleRect().size()),
00508                Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignLeft,
00509                d->title);
00510 
00511     // Create the alpha gradient for the fade out effect
00512     QLinearGradient alphaGradient(0, 0, 1, 0);
00513     alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
00514     //TODO: correct handling of right to left text.
00515     alphaGradient.setColorAt(0, QColor(0, 0, 0, 255));
00516     alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
00517 
00518     p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00519     p.fillRect(rect, alphaGradient);
00520 
00521     p.end();
00522 
00523     painter->drawPixmap(d->titleRect().topLeft(), pixmap);
00524 }
00525 
00526 void ExtenderItem::moveEvent(QGraphicsSceneMoveEvent *event)
00527 {
00528     Q_UNUSED(event)
00529     //not needed anymore, but here for binary compatibility
00530 }
00531 
00532 void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
00533 {
00534     Q_UNUSED(event)
00535     d->themeChanged();
00536 }
00537 
00538 void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
00539 {
00540     if (!(d->dragHandleRect().contains(event->pos())) ||
00541         d->extender->d->applet->immutability() != Plasma::Mutable) {
00542         event->ignore();
00543         return;
00544     }
00545 
00546     d->mousePos = event->pos().toPoint();
00547 }
00548 
00549 void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00550 {
00551     if ((event->pos().toPoint() - d->mousePos).manhattanLength()
00552         < QApplication::startDragDistance()) {
00553         return;
00554     }
00555 
00556     //Start the drag:
00557     d->dragStarted = true;
00558     QPointF curPos = pos();
00559 
00560     //remove item from the layout, and add it somewhere off screen so we can render it to a pixmap,
00561     //without other widgets interefing.
00562     d->extender->itemRemovedEvent(this);
00563     Corona *corona = qobject_cast<Corona*>(scene());
00564     corona->addOffscreenWidget(this);
00565 
00566     //update the borders, since while dragging, we want all of theme.
00567     d->themeChanged();
00568 
00569     //create a view to render the ExtenderItem and it's contents to a pixmap and set up a painter on
00570     //a pixmap.
00571     QGraphicsView view(scene());
00572     QSize screenSize(view.mapFromScene(sceneBoundingRect()).boundingRect().size());
00573     QPixmap pixmap(screenSize);
00574     pixmap.fill(Qt::transparent);
00575     QPainter p(&pixmap);
00576 
00577     //the following is necesarry to avoid having an offset when rendering the widget into the
00578     //pixmap.
00579     view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00580     view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00581     view.setFrameShape(QFrame::NoFrame);
00582 
00583     //aim the view and render.
00584     view.resize(screenSize);
00585     view.setSceneRect(sceneBoundingRect());
00586     view.render(&p, QRectF(QPointF(0, 0), pixmap.size()), QRect(QPoint(0, 0), screenSize));
00587 
00588     //create the necesarry mimedata.
00589     ExtenderItemMimeData *mimeData = new ExtenderItemMimeData();
00590     mimeData->setExtenderItem(this);
00591 
00592     //Hide empty internal extender containers when we drag the last item away. Avoids having
00593     //an ugly empty applet on the desktop temporarily.
00594     ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->extender->d->applet);
00595     if (extenderApplet && d->extender->attachedItems().count() < 2 &&
00596         extenderApplet->formFactor() != Plasma::Horizontal &&
00597         extenderApplet->formFactor() != Plasma::Vertical) {
00598         kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
00599         extenderApplet->hide();
00600         AppletHandle *handle = dynamic_cast<AppletHandle*>(extenderApplet->parentItem());
00601         if (handle) {
00602             handle->hide();
00603         }
00604     }
00605 
00606     ExtenderGroup *group = qobject_cast<ExtenderGroup*>(this);
00607     bool collapsedGroup = false;
00608     if (isGroup()) {
00609         collapsedGroup = group->d->collapsed;
00610         group->collapseGroup();
00611     }
00612 
00613     //and execute the drag.
00614     QWidget *dragParent = extender()->d->applet->view();
00615     QDrag *drag = new QDrag(dragParent);
00616     drag->setPixmap(pixmap);
00617     drag->setMimeData(mimeData);
00618     drag->setHotSpot(d->mousePos);
00619     Qt::DropAction action = drag->exec();
00620 
00621     corona->removeOffscreenWidget(this);
00622 
00623     if (!action || !drag->target()) {
00624         //we weren't moved, so reinsert the item in our current layout.
00625         d->extender->itemAddedEvent(this, curPos);
00626     }
00627 
00628     if (isGroup() && !collapsedGroup) {
00629         group->expandGroup();
00630     }
00631 
00632     d->dragStarted = false;
00633 }
00634 
00635 void ExtenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
00636 {
00637     if (d->titleRect().contains(event->pos())) {
00638         d->toggleCollapse();
00639     }
00640 }
00641 
00642 bool ExtenderItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
00643 {
00644     if (watched == d->widget && event->type() == QEvent::GraphicsSceneResize) {
00645         d->updateSizeHints();
00646     }
00647     return false;
00648 }
00649 
00650 void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00651 {
00652     Q_UNUSED(event)
00653     //not needed anymore, but here for binary compatibility
00654 }
00655 
00656 void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
00657 {
00658     if (d->titleRect().contains(event->pos()) &&
00659         d->extender->d->applet->immutability() == Plasma::Mutable) {
00660         if (!d->mouseOver) {
00661             QApplication::setOverrideCursor(Qt::OpenHandCursor);
00662             d->mouseOver = true;
00663         }
00664     } else {
00665         if (d->mouseOver) {
00666             QApplication::restoreOverrideCursor();
00667             d->mouseOver = false;
00668         }
00669     }
00670 }
00671 
00672 void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
00673 {
00674     Q_UNUSED(event);
00675 
00676     if (d->mouseOver) {
00677         QApplication::restoreOverrideCursor();
00678         d->mouseOver = false;
00679     }
00680 }
00681 
00682 ExtenderItemPrivate::ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
00683     : q(extenderItem),
00684       widget(0),
00685       toolbox(0),
00686       extender(hostExtender),
00687       sourceApplet(0),
00688       group(0),
00689       dragger(new FrameSvg(extenderItem)),
00690       background(new FrameSvg(extenderItem)),
00691       collapseIcon(0),
00692       title(QString()),
00693       mouseOver(false),
00694       dragStarted(false),
00695       destroyActionVisibility(false),
00696       collapsed(false),
00697       expirationTimer(0),
00698       iconSize(qreal(0))
00699 {
00700     dragLeft = dragTop = dragRight = dragBottom = 0;
00701     bgLeft = bgTop = bgRight = bgBottom = 0;
00702 }
00703 
00704 ExtenderItemPrivate::~ExtenderItemPrivate()
00705 {
00706     delete widget;
00707     widget = 0;
00708 }
00709 
00710 //returns a Rect containing the area of the detachable where the draghandle will be drawn.
00711 QRectF ExtenderItemPrivate::dragHandleRect()
00712 {
00713     QSizeF panelSize(QSizeF(q->size().width() - bgLeft - bgRight,
00714                      iconSize + dragTop + dragBottom));
00715     return QRectF(QPointF(bgLeft, bgTop), panelSize);
00716 }
00717 
00718 QRectF ExtenderItemPrivate::titleRect()
00719 {
00720     return dragHandleRect().adjusted(dragLeft + collapseIcon->size().width() + 1, dragTop,
00721                                      -toolbox->size().width() - dragRight, -dragBottom);
00722 }
00723 
00724 void ExtenderItemPrivate::toggleCollapse()
00725 {
00726     q->setCollapsed(!q->isCollapsed());
00727 }
00728 
00729 void ExtenderItemPrivate::updateToolBox()
00730 {
00731     Q_ASSERT(toolbox);
00732     Q_ASSERT(dragger);
00733     Q_ASSERT(toolboxLayout);
00734 
00735     //TODO: only delete items that actually have to be deleted, current performance is horrible.
00736     while (toolboxLayout->count()) {
00737         QGraphicsLayoutItem *icon = toolboxLayout->itemAt(0);
00738         QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(icon);
00739         widget->deleteLater();
00740         toolboxLayout->removeAt(0);
00741     }
00742 
00743     //add the actions that are actually set to visible.
00744     foreach (QAction *action, actionsInOrder) {
00745         if (action->isVisible()) {
00746             IconWidget *icon = new IconWidget(q);
00747             icon->setAction(action);
00748             QSizeF size = icon->sizeFromIconSize(iconSize);
00749             icon->setMinimumSize(size);
00750             icon->setMaximumSize(size);
00751             toolboxLayout->addItem(icon);
00752         }
00753     }
00754 
00755     //add the returntosource icon if we are detached, and have a source applet.
00756     if (q->isDetached() && sourceApplet) {
00757         IconWidget *returnToSource = new IconWidget(q);
00758         returnToSource->setSvg("widgets/configuration-icons", "return-to-source");
00759         QSizeF size = returnToSource->sizeFromIconSize(iconSize);
00760         returnToSource->setMinimumSize(size);
00761         returnToSource->setMaximumSize(size);
00762 
00763         toolboxLayout->addItem(returnToSource);
00764         QObject::connect(returnToSource, SIGNAL(clicked()), q, SLOT(returnToSource()));
00765     }
00766 
00767     //add the close icon if desired.
00768     if (destroyActionVisibility) {
00769         IconWidget *destroyAction = new IconWidget(q);
00770         destroyAction->setSvg("widgets/configuration-icons", "close");
00771         QSizeF size = destroyAction->sizeFromIconSize(iconSize);
00772         destroyAction->setMinimumSize(size);
00773         destroyAction->setMaximumSize(size);
00774 
00775         toolboxLayout->addItem(destroyAction);
00776         QObject::connect(destroyAction, SIGNAL(clicked()), q, SLOT(destroy()));
00777     }
00778 
00779     toolboxLayout->updateGeometry();
00780 
00781     //position the toolbox correctly.
00782     QSizeF minimum = toolboxLayout->minimumSize();
00783     toolbox->resize(minimum);
00784     repositionToolbox();
00785 }
00786 
00787 void ExtenderItemPrivate::repositionToolbox()
00788 {
00789     QSizeF minimum = toolboxLayout->minimumSize();
00790     toolbox->setPos(q->size().width() - minimum.width() - bgRight,
00791                     (dragHandleRect().height()/2) -
00792                     (minimum.height()/2) + bgTop);
00793 }
00794 
00795 Applet *ExtenderItemPrivate::hostApplet() const
00796 {
00797     if (extender) {
00798         return extender->d->applet;
00799     } else {
00800         return 0;
00801     }
00802 }
00803 
00804 void ExtenderItemPrivate::themeChanged()
00805 {
00806     if (dragStarted) {
00807         background->setImagePath("opaque/widgets/extender-background");
00808     } else {
00809         background->setImagePath("widgets/extender-background");
00810     }
00811     if (dragStarted) {
00812         background->setEnabledBorders(FrameSvg::AllBorders);
00813     } else {
00814         background->setEnabledBorders(extender->enabledBordersForItem(q));
00815     }
00816     background->getMargins(bgLeft, bgTop, bgRight, bgBottom);
00817 
00818     dragger->setImagePath("widgets/extender-dragger");
00819 
00820     //Read the preferred icon size hint, look at the font size, and calculate the desired title bar
00821     //icon height.
00822     dragger->resize();
00823     QSizeF size = dragger->elementSize("hint-preferred-icon-size");
00824     size = size.expandedTo(QSizeF(KIconLoader::SizeSmall,KIconLoader::SizeSmall));
00825 
00826     Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00827     QFont font = theme->font(Plasma::Theme::DefaultFont);
00828     QFontMetrics fm(font);
00829 
00830     iconSize = qMax(size.height(), (qreal) fm.height());
00831 
00832 
00833     dragger->getMargins(dragLeft, dragTop, dragRight, dragBottom);
00834 
00835     QSizeF panelSize(QSizeF(q->size().width() - bgLeft - bgRight,
00836                      iconSize + dragTop + dragBottom));
00837 
00838     //resize the collapse icon.
00839     collapseIcon->resize(collapseIcon->sizeFromIconSize(iconSize));
00840 
00841     //reposition the collapse icon based on the new margins and size.
00842     collapseIcon->setPos(bgLeft + dragLeft,
00843                          panelSize.height()/2 -
00844                          collapseIcon->size().height()/2 + bgTop);
00845 
00846     //reposition the widget based on the new margins.
00847     if (widget) {
00848         widget->setPos(QPointF(bgLeft + dragLeft, panelSize.height() +
00849                                                   bgTop));
00850     }
00851 
00852     //reposition the toolbox.
00853     repositionToolbox();
00854 
00855     updateSizeHints();
00856 
00857     if (!q->size().isEmpty())
00858         resizeContent(q->size());
00859 }
00860 
00861 void ExtenderItemPrivate::sourceAppletRemoved()
00862 {
00863     //the original source applet is removed, set the pointer to 0 and no longer show the return to
00864     //source icon.
00865     sourceApplet = 0;
00866     updateToolBox();
00867 }
00868 
00869 void ExtenderItemPrivate::resizeContent(const QSizeF &newSize)
00870 {
00871     qreal width = newSize.width();
00872     qreal height = newSize.height();
00873 
00874     //resize the dragger
00875     dragger->resizeFrame(QSizeF(width - bgLeft - bgRight,
00876                          iconSize + dragTop + dragBottom));
00877 
00878     //resize the applet background
00879     background->resizeFrame(newSize);
00880 
00881     //resize the widget
00882     if (widget && widget->isWidget()) {
00883         QSizeF newWidgetSize(width - bgLeft - bgRight - dragLeft - dragRight,
00884                              height - dragHandleRect().height() - bgTop - bgBottom -
00885                              dragTop - dragBottom);
00886 
00887         QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(widget);
00888         graphicsWidget->resize(newWidgetSize);
00889     }
00890 
00891     //reposition the toolbox.
00892     repositionToolbox();
00893 
00894     q->update();
00895 }
00896 
00897 void ExtenderItemPrivate::actionDestroyed(QObject *o)
00898 {
00899     QAction *action = static_cast<QAction *>(o);
00900     QMutableHashIterator<QString, QAction *> hit(actions);
00901     while (hit.hasNext()) {
00902         if (hit.next().value() == action) {
00903             hit.remove();
00904             break;
00905         }
00906     }
00907 
00908     QMutableListIterator<QAction *> lit(actionsInOrder);
00909     while (lit.hasNext()) {
00910         if (lit.next() == action) {
00911             lit.remove();
00912             break;
00913         }
00914     }
00915 }
00916 
00917 void ExtenderItemPrivate::updateSizeHints()
00918 {
00919     if (!widget) {
00920         return;
00921     }
00922 
00923     qreal marginWidth = bgLeft + bgRight + dragLeft + dragRight;
00924     qreal marginHeight = bgTop + bgBottom + dragTop + dragBottom;
00925 
00926     QSizeF min;
00927     QSizeF pref;
00928     QSizeF max;
00929 
00930     if (widget->isWidget()) {
00931         QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(widget);
00932         min = graphicsWidget->minimumSize();
00933         pref = graphicsWidget->preferredSize();
00934         max = graphicsWidget->maximumSize();
00935     } else {
00936         min = widget->boundingRect().size();
00937         pref = widget->boundingRect().size();
00938         max = widget->boundingRect().size();
00939     }
00940 
00941     if (collapsed) {
00942         q->setPreferredSize(QSizeF(pref.width() + marginWidth,
00943                                    dragHandleRect().height() + marginHeight));
00944         q->setMinimumSize(QSizeF(min.width() + marginWidth,
00945                                  dragHandleRect().height() + marginHeight));
00946         q->setMaximumSize(QSizeF(max.width() + marginWidth,
00947                                  dragHandleRect().height() + marginHeight));
00948         q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00949 
00950         if (collapseIcon) {
00951             collapseIcon->setToolTip(i18n("Expand this widget"));
00952         }
00953     } else {
00954         q->setPreferredSize(QSizeF(pref.width() + marginWidth,
00955                             pref.height() + dragHandleRect().height() + marginHeight));
00956         q->setMinimumSize(QSizeF(min.width() + marginWidth,
00957                           min.height() + dragHandleRect().height() + marginHeight));
00958         q->setMaximumSize(QSizeF(max.width() + marginWidth,
00959                           max.height() + dragHandleRect().height() + marginHeight));
00960 
00961         //set sane size policies depending on the appearence.
00962         if (extender->d->appearance == Extender::TopDownStacked ||
00963             extender->d->appearance == Extender::BottomUpStacked) {
00964             //used in popups, so fixed make sense.
00965             q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00966         } else if (extender->d->appearance == Extender::NoBorders) {
00967             //on the desktop or panel so take all the space we want.
00968             q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
00969         }
00970 
00971         if (collapseIcon) {
00972             collapseIcon->setToolTip(i18n("Collapse this widget"));
00973         }
00974     }
00975 
00976     q->updateGeometry();
00977 }
00978 
00979 uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
00980 
00981 } // namespace Plasma
00982 
00983 #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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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