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"
KDE 4.6 API Reference