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