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