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