• 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     if (extender()->d->destroying) {
00600         return;
00601     }
00602 
00603     config().writeEntry("isCollapsed", collapsed);
00604     d->collapsed = collapsed;
00605     d->collapseIcon->setToolTip(collapsed ? i18n("Expand this widget") : i18n("Collapse this widget"));
00606     if (d->widget.data()) {
00607         d->widget.data()->setVisible(!collapsed);
00608         if (collapsed) {
00609             d->layout->removeItem(d->widget.data());
00610         } else {
00611             d->layout->insertItem(1, d->widget.data());
00612         }
00613         updateGeometry();
00614 
00615         if (extender()) {
00616             extender()->d->adjustMinimumSize();
00617             static_cast<QGraphicsLayoutItem *>(extender()->d->mainWidget)->updateGeometry();
00618             if (group()) {
00619                 group()->layout()->invalidate();
00620                 static_cast<QGraphicsLayoutItem *>(group())->updateGeometry();
00621             }
00622 
00623             extender()->d->adjustSize();
00624         }
00625     }
00626 }
00627 
00628 void ExtenderItem::returnToSource()
00629 {
00630     if (!d || !d->sourceApplet) {
00631         return;
00632     }
00633 
00634     if (d->sourceApplet->d) {
00635         setExtender(d->sourceApplet->extender());
00636     }
00637 }
00638 
00639 void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
00640 {
00641     if (d->background->enabledBorders() != (FrameSvg::LeftBorder | FrameSvg::RightBorder) &&
00642         d->background->enabledBorders()) {
00643         //Don't paint if only the left and right borders are enabled, we only use the left and right
00644         //border in this situation to set the correct margins on this item.
00645         d->background->paintFrame(painter, option->exposedRect, option->exposedRect);
00646     }
00647 }
00648 
00649 void ExtenderItem::moveEvent(QGraphicsSceneMoveEvent *event)
00650 {
00651     Q_UNUSED(event)
00652     //not needed anymore, but here for binary compatibility
00653 }
00654 
00655 void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
00656 {
00657     Q_UNUSED(event)
00658     //resize the applet background
00659     d->background->resizeFrame(size());
00660     //d->resizeContent(size());
00661 }
00662 
00663 void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
00664 {
00665     if (!(d->dragHandleRect().contains(event->pos())) ||
00666         d->extender->d->applet.data()->immutability() != Plasma::Mutable) {
00667         event->ignore();
00668         return;
00669     }
00670 }
00671 
00672 void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00673 {
00674     QPoint mousePressPos = event->buttonDownPos(Qt::LeftButton).toPoint();
00675     if (!(event->buttons() & Qt::LeftButton) ||
00676         (event->pos().toPoint() - mousePressPos).manhattanLength()
00677         < QApplication::startDragDistance()) {
00678         return;
00679     }
00680 
00681     if (!d->extender->d->applet) {
00682         return;
00683     }
00684 
00685     //Start the drag:
00686     d->dragStarted = true;
00687     QPointF curPos = pos();
00688 
00689     //remove item from the layout, and add it somewhere off screen so we can render it to a pixmap,
00690     //without other widgets interefing.
00691     d->extender->itemRemovedEvent(this);
00692     Corona *corona = qobject_cast<Corona*>(scene());
00693     corona->addOffscreenWidget(this);
00694 
00695     //update the borders, since while dragging, we want all of theme.
00696     d->themeChanged();
00697 
00698     //create a view to render the ExtenderItem and it's contents to a pixmap and set up a painter on
00699     //a pixmap.
00700     QGraphicsView view(scene());
00701     QSize screenSize(view.mapFromScene(sceneBoundingRect()).boundingRect().size());
00702     QPixmap pixmap(screenSize);
00703     pixmap.fill(Qt::transparent);
00704     QPainter p(&pixmap);
00705 
00706     //the following is necesarry to avoid having an offset when rendering the widget into the
00707     //pixmap.
00708     view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00709     view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00710     view.setFrameShape(QFrame::NoFrame);
00711 
00712     //aim the view and render.
00713     view.resize(screenSize);
00714     view.setSceneRect(sceneBoundingRect());
00715     view.render(&p, QRectF(QPointF(0, 0), pixmap.size()), QRect(QPoint(0, 0), screenSize));
00716 
00717     //create the necesarry mimedata.
00718     ExtenderItemMimeData *mimeData = new ExtenderItemMimeData();
00719     mimeData->setExtenderItem(this);
00720     mimeData->setPointerOffset(mousePressPos);
00721 
00722     //Hide empty internal extender containers when we drag the last item away. Avoids having
00723     //an ugly empty applet on the desktop temporarily.
00724     ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->extender->d->applet.data());
00725     if (extenderApplet && d->extender->attachedItems().count() < 2 &&
00726         extenderApplet->formFactor() != Plasma::Horizontal &&
00727         extenderApplet->formFactor() != Plasma::Vertical) {
00728         kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
00729         extenderApplet->hide();
00730     }
00731 
00732     ExtenderGroup *group = qobject_cast<ExtenderGroup*>(this);
00733     bool collapsedGroup = false;
00734     if (isGroup()) {
00735         collapsedGroup = group->d->collapsed;
00736         group->collapseGroup();
00737     }
00738 
00739     if (!isGroup() && this->group()) {
00740         setGroup(0);
00741     }
00742 
00743     //and execute the drag.
00744     QWidget *dragParent = extender()->d->applet.data()->view();
00745     QDrag *drag = new QDrag(dragParent);
00746     drag->setPixmap(pixmap);
00747     drag->setMimeData(mimeData);
00748     drag->setHotSpot(mousePressPos);
00749 
00750     Qt::DropAction action = drag->exec();
00751 
00752     corona->removeOffscreenWidget(this);
00753     d->dragStarted = false;
00754 
00755     if (!action || !drag->target()) {
00756         //we weren't moved, so reinsert the item in our current layout.
00757         //TODO: make it into a stand-alone window?
00758         d->themeChanged();
00759         d->extender->itemAddedEvent(this, curPos);
00760         if (extenderApplet) {
00761             extenderApplet->show();
00762         }
00763     }
00764 
00765     if (isGroup() && !collapsedGroup) {
00766         group->expandGroup();
00767     }
00768 }
00769 
00770 void ExtenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
00771 {
00772     if (d->dragHandleRect().contains(event->pos())) {
00773         d->toggleCollapse();
00774     }
00775 }
00776 
00777 bool ExtenderItem::sceneEventFilter(QGraphicsItem *, QEvent *)
00778 {
00779     return false;
00780 }
00781 
00782 void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00783 {
00784     Q_UNUSED(event)
00785     //not needed anymore, but here for binary compatibility
00786 }
00787 
00788 void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
00789 {
00790 }
00791 
00792 void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
00793 {
00794 }
00795 
00796 QSizeF ExtenderItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
00797 {
00798     return QGraphicsWidget::sizeHint(which, constraint);
00799 }
00800 
00801 void ExtenderItem::setTransient(const bool transient)
00802 {
00803     d->transient = transient;
00804 }
00805 
00806 bool ExtenderItem::isTransient() const
00807 {
00808     return d->transient;
00809 }
00810 
00811 ExtenderItemPrivate::ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
00812     : q(extenderItem),
00813       toolbox(0),
00814       extender(hostExtender),
00815       sourceApplet(0),
00816       group(0),
00817       background(new FrameSvg(extenderItem)),
00818       collapseIcon(0),
00819       expirationTimer(0),
00820       dragStarted(false),
00821       destroyActionVisibility(false),
00822       collapsed(false),
00823       transient(false)
00824 {
00825 }
00826 
00827 ExtenderItemPrivate::~ExtenderItemPrivate()
00828 {
00829     delete widget.data();
00830 }
00831 
00832 //returns a Rect containing the area of the detachable where the draghandle will be drawn.
00833 QRectF ExtenderItemPrivate::dragHandleRect()
00834 {
00835     return toolbox->boundingRect();
00836 }
00837 
00838 void ExtenderItemPrivate::toggleCollapse()
00839 {
00840     q->setCollapsed(!q->isCollapsed());
00841 }
00842 
00843 void ExtenderItemPrivate::updateToolBox()
00844 {
00845     Q_ASSERT(toolbox);
00846     Q_ASSERT(toolboxLayout);
00847 
00848 
00849     QAction *closeAction = actions.value("close");
00850     QAction *returnToSourceAction = actions.value("extenderItemReturnToSource");
00851     bool returnToSourceVisibility = q->isDetached() && sourceApplet && (hostApplet()->immutability() == Plasma::Mutable);
00852     int closeIndex = -1;
00853     int returnToSourceIndex = -1;
00854     const int startingIndex = 2; // collapse item is index 0, title label is 1
00855     int lastIndex = 2;
00856     const QSizeF widgetSize = collapseIcon->sizeFromIconSize(toolbox->iconSize());
00857 
00858     QSet<QAction*> shownActions = actionsInOrder.toSet();
00859 
00860     QHash<QAction *, QGraphicsWidget *> actionWidgets;
00861     for (int index = startingIndex; index < toolboxLayout->count(); ++index) {
00862         QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(toolboxLayout->itemAt(index));
00863         QAction *widgetAction = 0;
00864 
00865         if (!widget) {
00866             continue;
00867         } else if (qobject_cast<IconWidget*>(widget)) {
00868             widgetAction = static_cast<IconWidget*>(widget)->action();
00869         } else if (qobject_cast<PushButton*>(widget)) {
00870             widgetAction = static_cast<PushButton*>(widget)->action();
00871         } else {
00872             continue;
00873         }
00874 
00875 
00876         if (closeIndex == -1 && destroyActionVisibility &&
00877             closeAction && widgetAction == closeAction) {
00878             closeIndex = index;
00879             continue;
00880         }
00881 
00882         if (returnToSourceIndex == -1 && returnToSourceVisibility &&
00883             returnToSourceAction && widgetAction == returnToSourceAction) {
00884             returnToSourceIndex = index;
00885             continue;
00886         }
00887 
00888         if (shownActions.contains(widgetAction)) {
00889             actionWidgets.insert(widgetAction, widget);
00890             continue;
00891         }
00892 
00893         toolboxLayout->removeAt(index);
00894         widget->deleteLater();
00895     }
00896 
00897 
00898     // ensure the collapseIcon is the correct size.
00899     collapseIcon->setMinimumSize(widgetSize);
00900     collapseIcon->setMaximumSize(widgetSize);
00901 
00902     //add the actions that are actually set to visible.
00903     foreach (QAction *action, actionsInOrder) {
00904         if (action->isVisible() && action != closeAction) {
00905             IconWidget *icon = qobject_cast<IconWidget*>(actionWidgets.value(action));
00906             PushButton *button = qobject_cast<PushButton*>(actionWidgets.value(action));
00907 
00908             if (action->icon().isNull() && !action->text().isNull()) {
00909                 if (!button) {
00910                     button = new PushButton(q);
00911                     button->setAction(action);
00912                 }
00913 
00914                 button->setMinimumHeight(widgetSize.height());
00915                 button->setMaximumHeight(widgetSize.height());
00916                 button->setCursor(Qt::ArrowCursor);
00917                 toolboxLayout->insertItem(startingIndex, button);
00918                 ++lastIndex;
00919             } else {
00920                 if (!icon) {
00921                     icon = new IconWidget(q);
00922                     icon->setAction(action);
00923                 }
00924 
00925                 if (action->icon().isNull()) {
00926                     icon->setText(action->text());
00927                 }
00928                 icon->setMinimumSize(widgetSize);
00929                 icon->setMaximumSize(widgetSize);
00930                 icon->setCursor(Qt::ArrowCursor);
00931                 toolboxLayout->insertItem(startingIndex, icon);
00932                 ++lastIndex;
00933             }
00934         }
00935     }
00936 
00937     //add the returntosource icon if we are detached, and have a source applet.
00938     if (returnToSourceVisibility && returnToSourceIndex == -1) {
00939         IconWidget *returnToSourceIcon = new IconWidget(q);
00940         if (!returnToSourceAction) {
00941             returnToSourceAction = new QAction(q);
00942             returnToSourceAction->setToolTip(i18n("Reattach"));
00943             actions.insert("extenderItemReturnToSource", returnToSourceAction);
00944             QObject::connect(returnToSourceAction, SIGNAL(triggered()), q, SLOT(returnToSource()));
00945         }
00946 
00947         returnToSourceIcon->setAction(returnToSourceAction);
00948         returnToSourceIcon->setSvg("widgets/configuration-icons", "return-to-source");
00949         returnToSourceIcon->setMinimumSize(widgetSize);
00950         returnToSourceIcon->setMaximumSize(widgetSize);
00951         returnToSourceIcon->setCursor(Qt::ArrowCursor);
00952 
00953         if (closeIndex == -1) {
00954             toolboxLayout->addItem(returnToSourceIcon);
00955         } else {
00956             toolboxLayout->insertItem(closeIndex - 1, returnToSourceIcon);
00957         }
00958         ++lastIndex;
00959     }
00960 
00961     //add the close icon if desired.
00962     if (destroyActionVisibility && closeIndex == -1) {
00963         IconWidget *destroyButton = new IconWidget(q);
00964         if (!closeAction) {
00965             closeAction = new QAction(q);
00966             actions.insert("close", closeAction);
00967             if (returnToSourceAction) {
00968                 returnToSourceAction->setToolTip(i18n("Close"));
00969             }
00970             QObject::connect(closeAction, SIGNAL(triggered()), q, SLOT(destroy()));
00971         }
00972 
00973         destroyButton->setAction(closeAction);
00974         destroyButton->setSvg("widgets/configuration-icons", "close");
00975         destroyButton->setMinimumSize(widgetSize);
00976         destroyButton->setMaximumSize(widgetSize);
00977         destroyButton->setCursor(Qt::ArrowCursor);
00978         toolboxLayout->addItem(destroyButton);
00979         ++lastIndex;
00980     }
00981 
00982     //to keep the text really centered
00983     toolboxLayout->setItemSpacing(0, KIconLoader::SizeSmall * (lastIndex - 2));
00984     if (lastIndex == 2) {
00985         if (QApplication::layoutDirection() == Qt::RightToLeft) {
00986             toolboxLayout->setContentsMargins(KIconLoader::SizeSmall, 0, 0, 0);
00987         } else {
00988             toolboxLayout->setContentsMargins(0, 0, KIconLoader::SizeSmall, 0);
00989         }
00990     } else {
00991         toolboxLayout->setContentsMargins(0, 0, 0, 0);
00992     }
00993 }
00994 
00995 Applet *ExtenderItemPrivate::hostApplet() const
00996 {
00997     if (extender) {
00998         return extender->d->applet.data();
00999     } else {
01000         return 0;
01001     }
01002 }
01003 
01004 void ExtenderItemPrivate::themeChanged()
01005 {
01006     kDebug();
01007     if (dragStarted) {
01008         background->setImagePath("opaque/dialogs/background");
01009         background->setEnabledBorders(FrameSvg::AllBorders);
01010     } else {
01011         background->setImagePath("widgets/extender-background");
01012         background->setEnabledBorders(extender->enabledBordersForItem(q));
01013     }
01014 
01015     qreal left, top, right, bottom;
01016     background->getMargins(left, top, right, bottom);
01017     layout->setContentsMargins(left, top, right, bottom);
01018 
01019     if (group) {
01020         toolbox->setBackgroundPrefix("grouped");
01021     } else {
01022         if (extender->items().count() <= 1 || extender->items().first() == q || extender->appearance() != Extender::NoBorders) {
01023             toolbox->setBackgroundPrefix("root");
01024         } else {
01025             toolbox->setBackgroundPrefix(QString());
01026         }
01027     }
01028 
01029     toolbox->updateTheme();
01030 }
01031 
01032 void ExtenderItemPrivate::sourceAppletRemoved()
01033 {
01034     //the original source applet is removed, set the pointer to 0 and no longer show the return to
01035     //source icon.
01036     sourceApplet = 0;
01037     updateToolBox();
01038 }
01039 
01040 void ExtenderItemPrivate::actionDestroyed(QObject *o)
01041 {
01042     QAction *action = static_cast<QAction *>(o);
01043     QMutableHashIterator<QString, QAction *> hit(actions);
01044     while (hit.hasNext()) {
01045         if (hit.next().value() == action) {
01046             hit.remove();
01047             break;
01048         }
01049     }
01050 
01051     QMutableListIterator<QAction *> lit(actionsInOrder);
01052     while (lit.hasNext()) {
01053         if (lit.next() == action) {
01054             lit.remove();
01055             break;
01056         }
01057     }
01058 }
01059 
01060 void ExtenderItemPrivate::setMovable(bool movable)
01061 {
01062     if (movable) {
01063         titleLabel->setCursor(Qt::OpenHandCursor);
01064         toolbox->setCursor(Qt::OpenHandCursor);
01065     } else {
01066         titleLabel->unsetCursor();
01067         toolbox->unsetCursor();
01068     }
01069 }
01070 
01071 uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
01072 
01073 } // namespace Plasma
01074 
01075 #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