• Skip to content
  • Skip to link menu
KDE 4.7 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 
00402     if (!corona) {
00403       return;
00404     }
00405 
00406     KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
00407 
00408     if (isDetached()) {
00409         kDebug() << "detached, adding entry to the global group";
00410         KConfigGroup itemConfig = extenderItemGroup.group(QString::number(d->extenderItemId));
00411         itemConfig.writeEntry("sourceAppletPluginName",
00412                 config().readEntry("sourceAppletPluginName", ""));
00413         itemConfig.writeEntry("sourceAppletId",
00414                 config().readEntry("sourceAppletId", 0));
00415         itemConfig.writeEntry("extenderItemName",
00416                 config().readEntry("extenderItemName", ""));
00417     } else if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
00418         kDebug() << "no longer detached, removing entry from the global group";
00419         extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
00420     }
00421 
00422     d->themeChanged();
00423 
00424     //we might have to enable or disable the returnToSource button.
00425     d->updateToolBox();
00426 
00427     //invoke setGroup on all items belonging to this group, to make sure all children move to the
00428     //new extender together with the group.
00429     if (group) {
00430         foreach (ExtenderItem *item, childItems) {
00431             item->setGroup(group);
00432         }
00433     }
00434 }
00435 
00436 Extender *ExtenderItem::extender() const
00437 {
00438     return d->extender;
00439 }
00440 
00441 //TODO KDE5: only one setGroup()
00442 void ExtenderItem::setGroup(ExtenderGroup *group)
00443 {
00444     setGroup(group, QPointF(-1, -1));
00445 }
00446 
00447 void ExtenderItem::setGroup(ExtenderGroup *group, const QPointF &pos)
00448 {
00449     if (isGroup()) {
00450         //nesting extender groups is just insane. I don't think we'd even want to support that.
00451         kWarning() << "Nesting ExtenderGroups is not supported";
00452         return;
00453     }
00454 
00455     ExtenderGroup *oldGroup = d->group;
00456     d->group = group;
00457 
00458     if (group) {
00459         d->toolbox->setBackgroundPrefix("grouped");
00460         config().writeEntry("group", group->name());
00461         //TODO: move to another extender if the group we set is actually detached.
00462         if (group->extender() != extender()) {
00463             kDebug() << "moving to another extender because we're joining a detached group.";
00464             setExtender(group->extender());
00465         }
00466         group->d->addItemToGroup(this, pos);
00467     } else {
00468         if (d->extender->appearance() != Extender::NoBorders) {
00469             d->toolbox->setBackgroundPrefix("root");
00470         } else {
00471             d->toolbox->setBackgroundPrefix(QString());
00472         }
00473         d->toolbox->setBackgroundPrefix(QString());
00474         if (oldGroup) {
00475             oldGroup->d->removeItemFromGroup(this);
00476         }
00477         config().deleteEntry("group");
00478     }
00479     d->dragStarted = false;
00480     d->themeChanged();
00481 }
00482 
00483 ExtenderGroup *ExtenderItem::group() const
00484 {
00485     return d->group;
00486 }
00487 
00488 bool ExtenderItem::isGroup() const
00489 {
00490     return (config().readEntry("isGroup", false) && qobject_cast<const Plasma::ExtenderGroup *>(this));
00491 }
00492 
00493 bool ExtenderItem::isCollapsed() const
00494 {
00495     return d->collapsed;
00496 }
00497 
00498 void ExtenderItem::setAutoExpireDelay(uint time)
00499 {
00500     if (!time) {
00501         if (d->expirationTimer) {
00502             d->expirationTimer->stop();
00503             delete d->expirationTimer;
00504             d->expirationTimer = 0;
00505         }
00506         return;
00507     }
00508 
00509     if (!isDetached()) {
00510         if (!d->expirationTimer) {
00511             d->expirationTimer = new QTimer(this);
00512             connect(d->expirationTimer, SIGNAL(timeout()), this, SLOT(destroy()));
00513         }
00514 
00515         d->expirationTimer->stop();
00516         d->expirationTimer->setSingleShot(true);
00517         d->expirationTimer->setInterval(time);
00518         d->expirationTimer->start();
00519     }
00520 }
00521 
00522 uint ExtenderItem::autoExpireDelay() const
00523 {
00524     if (d->expirationTimer) {
00525         return d->expirationTimer->interval();
00526     } else {
00527         return 0;
00528     }
00529 }
00530 
00531 bool ExtenderItem::isDetached() const
00532 {
00533     if (d->hostApplet()) {
00534         return (d->sourceApplet != d->hostApplet());
00535     } else {
00536         return false;
00537     }
00538 }
00539 
00540 void ExtenderItem::addAction(const QString &name, QAction *action)
00541 {
00542     Q_ASSERT(action);
00543     if (d->actionsInOrder.contains(action)) {
00544         return;
00545     }
00546 
00547     d->actions.insert(name, action);
00548     d->actionsInOrder.append(action);
00549     connect(action, SIGNAL(changed()), this, SLOT(updateToolBox()));
00550     connect(action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
00551     d->updateToolBox();
00552 }
00553 
00554 QAction *ExtenderItem::action(const QString &name) const
00555 {
00556     return d->actions.value(name, 0);
00557 }
00558 
00559 void ExtenderItem::showCloseButton()
00560 {
00561     if (d->destroyActionVisibility) {
00562         return;
00563     }
00564 
00565     d->destroyActionVisibility = true;
00566     d->updateToolBox();
00567 }
00568 
00569 void ExtenderItem::hideCloseButton()
00570 {
00571     if (!d->destroyActionVisibility) {
00572         return;
00573     }
00574 
00575     d->destroyActionVisibility = false;
00576     d->updateToolBox();
00577 }
00578 
00579 void ExtenderItem::destroy()
00580 {
00581     if (d->dragStarted) {
00582         //avoid being destroyed while we're being dragged.
00583         return;
00584     }
00585 
00586     //remove global entry if needed.
00587     Corona *corona = qobject_cast<Corona*>(scene());
00588     if (corona) {
00589         KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
00590         if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
00591             extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
00592         }
00593     }
00594 
00595     d->hostApplet()->config("ExtenderItems").deleteGroup(QString::number(d->extenderItemId));
00596     d->extender->d->removeExtenderItem(this);
00597     emit d->extender->itemDetached(this);
00598 
00599     deleteLater();
00600 }
00601 
00602 void ExtenderItem::setCollapsed(bool collapsed)
00603 {
00604     if (extender()->d->destroying) {
00605         return;
00606     }
00607 
00608     config().writeEntry("isCollapsed", collapsed);
00609     d->collapsed = collapsed;
00610     d->collapseIcon->setToolTip(collapsed ? i18n("Expand this widget") : i18n("Collapse this widget"));
00611     if (d->widget.data()) {
00612         d->widget.data()->setVisible(!collapsed);
00613         if (collapsed) {
00614             d->layout->removeItem(d->widget.data());
00615         } else {
00616             d->layout->insertItem(1, d->widget.data());
00617         }
00618         updateGeometry();
00619 
00620         if (extender()) {
00621             extender()->d->adjustMinimumSize();
00622             static_cast<QGraphicsLayoutItem *>(extender()->d->mainWidget)->updateGeometry();
00623             if (group()) {
00624                 group()->layout()->invalidate();
00625                 static_cast<QGraphicsLayoutItem *>(group())->updateGeometry();
00626             }
00627 
00628             extender()->d->adjustSize();
00629         }
00630     }
00631 }
00632 
00633 void ExtenderItem::returnToSource()
00634 {
00635     if (!d || !d->sourceApplet) {
00636         return;
00637     }
00638 
00639     if (d->sourceApplet->d) {
00640         setExtender(d->sourceApplet->extender());
00641     }
00642 }
00643 
00644 void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
00645 {
00646     if (d->background->enabledBorders() != (FrameSvg::LeftBorder | FrameSvg::RightBorder) &&
00647         d->background->enabledBorders()) {
00648         //Don't paint if only the left and right borders are enabled, we only use the left and right
00649         //border in this situation to set the correct margins on this item.
00650         d->background->paintFrame(painter, option->exposedRect, option->exposedRect);
00651     }
00652 }
00653 
00654 void ExtenderItem::moveEvent(QGraphicsSceneMoveEvent *event)
00655 {
00656     Q_UNUSED(event)
00657     //not needed anymore, but here for binary compatibility
00658 }
00659 
00660 void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
00661 {
00662     Q_UNUSED(event)
00663     //resize the applet background
00664     d->background->resizeFrame(size());
00665     //d->resizeContent(size());
00666 }
00667 
00668 void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
00669 {
00670     if (!(d->dragHandleRect().contains(event->pos())) ||
00671         d->extender->d->applet.data()->immutability() != Plasma::Mutable) {
00672         event->ignore();
00673         return;
00674     }
00675 }
00676 
00677 void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00678 {
00679     QPoint mousePressPos = event->buttonDownPos(Qt::LeftButton).toPoint();
00680     if (!(event->buttons() & Qt::LeftButton) ||
00681         (event->pos().toPoint() - mousePressPos).manhattanLength()
00682         < QApplication::startDragDistance()) {
00683         return;
00684     }
00685 
00686     if (!d->extender->d->applet) {
00687         return;
00688     }
00689 
00690     //Start the drag:
00691     d->dragStarted = true;
00692     QPointF curPos = pos();
00693 
00694     //remove item from the layout, and add it somewhere off screen so we can render it to a pixmap,
00695     //without other widgets interefing.
00696     d->extender->itemRemovedEvent(this);
00697     Corona *corona = qobject_cast<Corona*>(scene());
00698     corona->addOffscreenWidget(this);
00699 
00700     //update the borders, since while dragging, we want all of theme.
00701     d->themeChanged();
00702 
00703     //create a view to render the ExtenderItem and it's contents to a pixmap and set up a painter on
00704     //a pixmap.
00705     QGraphicsView view(scene());
00706     QSize screenSize(view.mapFromScene(sceneBoundingRect()).boundingRect().size());
00707     QPixmap pixmap(screenSize);
00708     pixmap.fill(Qt::transparent);
00709     QPainter p(&pixmap);
00710 
00711     //the following is necesarry to avoid having an offset when rendering the widget into the
00712     //pixmap.
00713     view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00714     view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00715     view.setFrameShape(QFrame::NoFrame);
00716 
00717     //aim the view and render.
00718     view.resize(screenSize);
00719     view.setSceneRect(sceneBoundingRect());
00720     view.render(&p, QRectF(QPointF(0, 0), pixmap.size()), QRect(QPoint(0, 0), screenSize));
00721 
00722     //create the necesarry mimedata.
00723     ExtenderItemMimeData *mimeData = new ExtenderItemMimeData();
00724     mimeData->setExtenderItem(this);
00725     mimeData->setPointerOffset(mousePressPos);
00726 
00727     //Hide empty internal extender containers when we drag the last item away. Avoids having
00728     //an ugly empty applet on the desktop temporarily.
00729     ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->extender->d->applet.data());
00730     if (extenderApplet && d->extender->attachedItems().count() < 2 &&
00731         extenderApplet->formFactor() != Plasma::Horizontal &&
00732         extenderApplet->formFactor() != Plasma::Vertical) {
00733         kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
00734         extenderApplet->hide();
00735     }
00736 
00737     ExtenderGroup *group = qobject_cast<ExtenderGroup*>(this);
00738     bool collapsedGroup = false;
00739     if (isGroup()) {
00740         collapsedGroup = group->d->collapsed;
00741         group->collapseGroup();
00742     }
00743 
00744     if (!isGroup() && this->group()) {
00745         setGroup(0);
00746     }
00747 
00748     //and execute the drag.
00749     QWidget *dragParent = extender()->d->applet.data()->view();
00750     QDrag *drag = new QDrag(dragParent);
00751     drag->setPixmap(pixmap);
00752     drag->setMimeData(mimeData);
00753     drag->setHotSpot(mousePressPos);
00754 
00755     Qt::DropAction action = drag->exec();
00756 
00757     corona->removeOffscreenWidget(this);
00758     d->dragStarted = false;
00759 
00760     if (!action || !drag->target()) {
00761         //we weren't moved, so reinsert the item in our current layout.
00762         //TODO: make it into a stand-alone window?
00763         d->themeChanged();
00764         d->extender->itemAddedEvent(this, curPos);
00765         if (extenderApplet) {
00766             extenderApplet->show();
00767         }
00768     }
00769 
00770     if (isGroup() && !collapsedGroup) {
00771         group->expandGroup();
00772     }
00773 }
00774 
00775 void ExtenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
00776 {
00777     if (d->dragHandleRect().contains(event->pos())) {
00778         d->toggleCollapse();
00779     }
00780 }
00781 
00782 bool ExtenderItem::sceneEventFilter(QGraphicsItem *, QEvent *)
00783 {
00784     return false;
00785 }
00786 
00787 void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00788 {
00789     Q_UNUSED(event)
00790     //not needed anymore, but here for binary compatibility
00791 }
00792 
00793 void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
00794 {
00795 }
00796 
00797 void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
00798 {
00799 }
00800 
00801 QSizeF ExtenderItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
00802 {
00803     return QGraphicsWidget::sizeHint(which, constraint);
00804 }
00805 
00806 void ExtenderItem::setTransient(const bool transient)
00807 {
00808     d->transient = transient;
00809 }
00810 
00811 bool ExtenderItem::isTransient() const
00812 {
00813     return d->transient;
00814 }
00815 
00816 ExtenderItemPrivate::ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
00817     : q(extenderItem),
00818       toolbox(0),
00819       extender(hostExtender),
00820       sourceApplet(0),
00821       group(0),
00822       background(new FrameSvg(extenderItem)),
00823       collapseIcon(0),
00824       expirationTimer(0),
00825       dragStarted(false),
00826       destroyActionVisibility(false),
00827       collapsed(false),
00828       transient(false)
00829 {
00830 }
00831 
00832 ExtenderItemPrivate::~ExtenderItemPrivate()
00833 {
00834     delete widget.data();
00835 }
00836 
00837 //returns a Rect containing the area of the detachable where the draghandle will be drawn.
00838 QRectF ExtenderItemPrivate::dragHandleRect()
00839 {
00840     return toolbox->boundingRect();
00841 }
00842 
00843 void ExtenderItemPrivate::toggleCollapse()
00844 {
00845     q->setCollapsed(!q->isCollapsed());
00846 }
00847 
00848 void ExtenderItemPrivate::updateToolBox()
00849 {
00850     Q_ASSERT(toolbox);
00851     Q_ASSERT(toolboxLayout);
00852 
00853 
00854     QAction *closeAction = actions.value("close");
00855     QAction *returnToSourceAction = actions.value("extenderItemReturnToSource");
00856     bool returnToSourceVisibility = q->isDetached() && sourceApplet && (hostApplet()->immutability() == Plasma::Mutable);
00857     int closeIndex = -1;
00858     int returnToSourceIndex = -1;
00859     const int startingIndex = 2; // collapse item is index 0, title label is 1
00860     int lastIndex = 2;
00861     const QSizeF widgetSize = collapseIcon->sizeFromIconSize(toolbox->iconSize());
00862 
00863     QSet<QAction*> shownActions = actionsInOrder.toSet();
00864 
00865     QHash<QAction *, QGraphicsWidget *> actionWidgets;
00866     for (int index = startingIndex; index < toolboxLayout->count(); ++index) {
00867         QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(toolboxLayout->itemAt(index));
00868         QAction *widgetAction = 0;
00869 
00870         if (!widget) {
00871             continue;
00872         } else if (qobject_cast<IconWidget*>(widget)) {
00873             widgetAction = static_cast<IconWidget*>(widget)->action();
00874         } else if (qobject_cast<PushButton*>(widget)) {
00875             widgetAction = static_cast<PushButton*>(widget)->action();
00876         } else {
00877             continue;
00878         }
00879 
00880 
00881         if (closeIndex == -1 && destroyActionVisibility &&
00882             closeAction && widgetAction == closeAction) {
00883             closeIndex = index;
00884             continue;
00885         }
00886 
00887         if (returnToSourceIndex == -1 && returnToSourceVisibility &&
00888             returnToSourceAction && widgetAction == returnToSourceAction) {
00889             returnToSourceIndex = index;
00890             continue;
00891         }
00892 
00893         if (shownActions.contains(widgetAction)) {
00894             actionWidgets.insert(widgetAction, widget);
00895             continue;
00896         }
00897 
00898         toolboxLayout->removeAt(index);
00899         widget->deleteLater();
00900     }
00901 
00902 
00903     // ensure the collapseIcon is the correct size.
00904     collapseIcon->setMinimumSize(widgetSize);
00905     collapseIcon->setMaximumSize(widgetSize);
00906 
00907     //add the actions that are actually set to visible.
00908     foreach (QAction *action, actionsInOrder) {
00909         if (action->isVisible() && action != closeAction) {
00910             IconWidget *icon = qobject_cast<IconWidget*>(actionWidgets.value(action));
00911             PushButton *button = qobject_cast<PushButton*>(actionWidgets.value(action));
00912 
00913             if (action->icon().isNull() && !action->text().isNull()) {
00914                 if (!button) {
00915                     button = new PushButton(q);
00916                     button->setAction(action);
00917                 }
00918 
00919                 button->setMinimumHeight(widgetSize.height());
00920                 button->setMaximumHeight(widgetSize.height());
00921                 button->setCursor(Qt::ArrowCursor);
00922                 toolboxLayout->insertItem(startingIndex, button);
00923                 ++lastIndex;
00924             } else {
00925                 if (!icon) {
00926                     icon = new IconWidget(q);
00927                     icon->setAction(action);
00928                 }
00929 
00930                 if (action->icon().isNull()) {
00931                     icon->setText(action->text());
00932                 }
00933                 icon->setMinimumSize(widgetSize);
00934                 icon->setMaximumSize(widgetSize);
00935                 icon->setCursor(Qt::ArrowCursor);
00936                 toolboxLayout->insertItem(startingIndex, icon);
00937                 ++lastIndex;
00938             }
00939         }
00940     }
00941 
00942     //add the returntosource icon if we are detached, and have a source applet.
00943     if (returnToSourceVisibility && returnToSourceIndex == -1) {
00944         IconWidget *returnToSourceIcon = new IconWidget(q);
00945         if (!returnToSourceAction) {
00946             returnToSourceAction = new QAction(q);
00947             returnToSourceAction->setToolTip(i18n("Reattach"));
00948             actions.insert("extenderItemReturnToSource", returnToSourceAction);
00949             QObject::connect(returnToSourceAction, SIGNAL(triggered()), q, SLOT(returnToSource()));
00950         }
00951 
00952         returnToSourceIcon->setAction(returnToSourceAction);
00953         returnToSourceIcon->setSvg("widgets/configuration-icons", "return-to-source");
00954         returnToSourceIcon->setMinimumSize(widgetSize);
00955         returnToSourceIcon->setMaximumSize(widgetSize);
00956         returnToSourceIcon->setCursor(Qt::ArrowCursor);
00957 
00958         if (closeIndex == -1) {
00959             toolboxLayout->addItem(returnToSourceIcon);
00960         } else {
00961             toolboxLayout->insertItem(closeIndex - 1, returnToSourceIcon);
00962         }
00963         ++lastIndex;
00964     }
00965 
00966     //add the close icon if desired.
00967     if (destroyActionVisibility && closeIndex == -1) {
00968         IconWidget *destroyButton = new IconWidget(q);
00969         if (!closeAction) {
00970             closeAction = new QAction(q);
00971             actions.insert("close", closeAction);
00972             if (returnToSourceAction) {
00973                 returnToSourceAction->setToolTip(i18n("Close"));
00974             }
00975             QObject::connect(closeAction, SIGNAL(triggered()), q, SLOT(destroy()));
00976         }
00977 
00978         destroyButton->setAction(closeAction);
00979         destroyButton->setSvg("widgets/configuration-icons", "close");
00980         destroyButton->setMinimumSize(widgetSize);
00981         destroyButton->setMaximumSize(widgetSize);
00982         destroyButton->setCursor(Qt::ArrowCursor);
00983         toolboxLayout->addItem(destroyButton);
00984         ++lastIndex;
00985     }
00986 
00987     //to keep the text really centered
00988     toolboxLayout->setItemSpacing(0, KIconLoader::SizeSmall * (lastIndex - 2));
00989     if (lastIndex == 2) {
00990         if (QApplication::layoutDirection() == Qt::RightToLeft) {
00991             toolboxLayout->setContentsMargins(KIconLoader::SizeSmall, 0, 0, 0);
00992         } else {
00993             toolboxLayout->setContentsMargins(0, 0, KIconLoader::SizeSmall, 0);
00994         }
00995     } else {
00996         toolboxLayout->setContentsMargins(0, 0, 0, 0);
00997     }
00998 }
00999 
01000 Applet *ExtenderItemPrivate::hostApplet() const
01001 {
01002     if (extender) {
01003         return extender->d->applet.data();
01004     } else {
01005         return 0;
01006     }
01007 }
01008 
01009 void ExtenderItemPrivate::themeChanged()
01010 {
01011     kDebug();
01012     if (dragStarted) {
01013         background->setImagePath("opaque/dialogs/background");
01014         background->setEnabledBorders(FrameSvg::AllBorders);
01015     } else {
01016         background->setImagePath("widgets/extender-background");
01017         background->setEnabledBorders(extender->enabledBordersForItem(q));
01018     }
01019 
01020     qreal left, top, right, bottom;
01021     background->getMargins(left, top, right, bottom);
01022     layout->setContentsMargins(left, top, right, bottom);
01023 
01024     if (group) {
01025         toolbox->setBackgroundPrefix("grouped");
01026     } else {
01027         if (extender->items().count() <= 1 || extender->items().first() == q || extender->appearance() != Extender::NoBorders) {
01028             toolbox->setBackgroundPrefix("root");
01029         } else {
01030             toolbox->setBackgroundPrefix(QString());
01031         }
01032     }
01033 
01034     toolbox->updateTheme();
01035 }
01036 
01037 void ExtenderItemPrivate::sourceAppletRemoved()
01038 {
01039     //the original source applet is removed, set the pointer to 0 and no longer show the return to
01040     //source icon.
01041     sourceApplet = 0;
01042     updateToolBox();
01043 }
01044 
01045 void ExtenderItemPrivate::actionDestroyed(QObject *o)
01046 {
01047     QAction *action = static_cast<QAction *>(o);
01048     QMutableHashIterator<QString, QAction *> hit(actions);
01049     while (hit.hasNext()) {
01050         if (hit.next().value() == action) {
01051             hit.remove();
01052             break;
01053         }
01054     }
01055 
01056     QMutableListIterator<QAction *> lit(actionsInOrder);
01057     while (lit.hasNext()) {
01058         if (lit.next() == action) {
01059             lit.remove();
01060             break;
01061         }
01062     }
01063 }
01064 
01065 void ExtenderItemPrivate::setMovable(bool movable)
01066 {
01067     if (movable) {
01068         titleLabel->setCursor(Qt::OpenHandCursor);
01069         toolbox->setCursor(Qt::OpenHandCursor);
01070     } else {
01071         titleLabel->unsetCursor();
01072         toolbox->unsetCursor();
01073     }
01074 }
01075 
01076 uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
01077 
01078 } // namespace Plasma
01079 
01080 #include "extenderitem.moc"

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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