• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • sources
  • kde-4.12
  • kdelibs
  • plasma
  • extenders
extenderitem.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2008, 2009 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301 USA
18  */
19 
20 #include "extenderitem.h"
21 
22 #include <QAction>
23 #include <QApplication>
24 #include <QBitmap>
25 #include <QDrag>
26 #include <QGraphicsSceneResizeEvent>
27 #include <QGraphicsSceneMouseEvent>
28 #include <QGraphicsLinearLayout>
29 #include <QLayout>
30 #include <QMimeData>
31 #include <QPainter>
32 #include <QTimer>
33 
34 #include <kdebug.h>
35 #include <kicon.h>
36 #include <kiconloader.h>
37 #include <ksharedconfig.h>
38 
39 #include "applet.h"
40 #include "containment.h"
41 #include "corona.h"
42 #include "dialog.h"
43 #include "extender.h"
44 #include "extendergroup.h"
45 #include "framesvg.h"
46 #include "popupapplet.h"
47 #include "theme.h"
48 #include "view.h"
49 
50 #include "widgets/iconwidget.h"
51 #include "widgets/pushbutton.h"
52 
53 #include "private/applethandle_p.h"
54 #include "private/extender_p.h"
55 #include "private/extenderapplet_p.h"
56 #include "private/extendergroup_p.h"
57 #include "private/extenderitem_p.h"
58 #include "private/extenderitemmimedata_p.h"
59 #include "widgets/label.h"
60 
61 namespace Plasma
62 {
63 
64 class ExtenderItemToolbox : public QGraphicsWidget
65 {
66 public:
67  ExtenderItemToolbox(QGraphicsWidget *parent)
68  : QGraphicsWidget(parent),
69  m_background(new FrameSvg(this))
70  {
71  m_background->setImagePath("widgets/extender-dragger");
72  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
73  updateTheme();
74  }
75 
76  qreal iconSize()
77  {
78  return m_iconSize;
79  }
80 
81  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
82  {
83  m_background->paintFrame(painter, option->exposedRect, option->exposedRect);
84  }
85 
86  void updateTheme()
87  {
88  //Read the preferred icon size hint, look at the font size, and calculate the desired title bar
89  //icon height.
90  m_background->resize();
91  QSizeF size = m_background->elementSize("hint-preferred-icon-size");
92  size = size.expandedTo(QSizeF(KIconLoader::SizeSmall,KIconLoader::SizeSmall));
93 
94  Plasma::Theme *theme = Plasma::Theme::defaultTheme();
95  QFont font = theme->font(Plasma::Theme::DefaultFont);
96  QFontMetrics fm(font);
97  m_iconSize = qMax(size.height(), (qreal) fm.height());
98  }
99 
100  void setBackgroundPrefix(const QString &string)
101  {
102  if (string.isEmpty() || m_background->hasElementPrefix(string)) {
103  m_background->setElementPrefix(string);
104  update();
105  }
106  }
107 
108  const QString backgroundPrefix() const
109  {
110  return m_background->prefix();
111  }
112 
113 protected:
114  void resizeEvent(QGraphicsSceneResizeEvent *)
115  {
116  m_background->resizeFrame(size());
117  qreal left, top, right, bottom;
118  m_background->getMargins(left, top, right, bottom);
119  setContentsMargins(0, top, 0, bottom);
120  }
121 
122 private:
123  FrameSvg *m_background;
124  QString m_prefix;
125  qreal m_iconSize;
126 };
127 
128 ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
129  : QGraphicsWidget(hostExtender),
130  d(new ExtenderItemPrivate(this, hostExtender))
131 {
132  Q_ASSERT(hostExtender);
133  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
134 
135  //set the extenderId
136  if (extenderItemId) {
137  d->extenderItemId = extenderItemId;
138  ExtenderItemPrivate::s_maxExtenderItemId =
139  qMax(ExtenderItemPrivate::s_maxExtenderItemId, extenderItemId);
140  } else {
141  d->extenderItemId = ++ExtenderItemPrivate::s_maxExtenderItemId;
142  }
143 
144  //create the toolbox.
145  d->toolbox = new ExtenderItemToolbox(this);
146  d->toolboxLayout = new QGraphicsLinearLayout(d->toolbox);
147 
148  //create items's configgroup
149  KConfigGroup cg = hostExtender->d->applet.data()->config("ExtenderItems");
150  KConfigGroup dg = KConfigGroup(&cg, QString::number(d->extenderItemId));
151 
152  //create own layout
153  d->layout = new QGraphicsLinearLayout(Qt::Vertical, this);
154  d->layout->addItem(d->toolbox);
155 
156  uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
157 
158  //check if we're creating a new item or reinstantiating an existing one.
159  d->collapseIcon = new IconWidget(d->toolbox);
160  d->collapseIcon->setCursor(Qt::ArrowCursor);
161  d->titleLabel = new Label(d->toolbox);
162  d->titleLabel->setWordWrap(false);
163  d->titleLabel->setAlignment(Qt::AlignCenter);
164 
165  d->toolboxLayout->addItem(d->collapseIcon);
166  d->toolboxLayout->addItem(d->titleLabel);
167  d->toolboxLayout->setStretchFactor(d->titleLabel, 10);
168 
169  if (!sourceAppletId) {
170  //The item is new
171  dg.writeEntry("sourceAppletPluginName", hostExtender->d->applet.data()->pluginName());
172  dg.writeEntry("sourceAppletId", hostExtender->d->applet.data()->id());
173  dg.writeEntry("extenderIconName", hostExtender->d->applet.data()->icon());
174  d->sourceApplet = hostExtender->d->applet.data();
175  d->collapseIcon->setIcon(KIcon(hostExtender->d->applet.data()->icon()));
176  } else {
177  //The item already exists.
178  d->name = dg.readEntry("extenderItemName", "");
179  d->titleLabel->setText(dg.readEntry("extenderTitle", ""));
180  setCollapsed(dg.readEntry("isCollapsed", false));
181 
182  QString iconName = dg.readEntry("extenderIconName", "utilities-desktop-extra");
183  if (iconName.isEmpty()) {
184  iconName = "utilities-desktop-extra";
185  }
186  d->collapseIcon->setIcon(iconName);
187 
188  //Find the group if it's already there.
189  QString groupName = dg.readEntry("group", "");
190  d->group = hostExtender->d->findGroup(groupName);
191 
192  //Find the sourceapplet.
193  Corona *corona = 0;
194  if (hostExtender && hostExtender->d->applet && hostExtender->d->applet.data()->containment()) {
195  corona = hostExtender->d->applet.data()->containment()->corona();
196  }
197  if (sourceAppletId == hostExtender->applet()->id()) {
198  d->sourceApplet = hostExtender->applet();
199  } else if (corona) {
200  foreach (Containment *containment, corona->containments()) {
201  foreach (Applet *applet, containment->applets()) {
202  if (applet->id() == sourceAppletId &&
203  applet->pluginName() == dg.readEntry("sourceAppletPluginName", "")) {
204  d->sourceApplet = applet;
205  }
206  }
207  }
208  }
209  }
210 
211  //make sure we keep monitoring if the source applet still exists, so the return to source icon
212  //can be hidden if it is removed.
213  if (d->sourceApplet) {
214  connect(d->sourceApplet, SIGNAL(destroyed()), this, SLOT(sourceAppletRemoved()));
215  }
216 
217  connect(d->collapseIcon, SIGNAL(clicked()), this, SLOT(toggleCollapse()));
218 
219  //set the extender we want to move to.
220  setExtender(hostExtender);
221 
222  //set the image paths, image sizes
223  d->themeChanged();
224 
225  //show or hide the toolbox interface itmems
226  d->updateToolBox();
227 
228  setAcceptsHoverEvents(true);
229 
230  connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
231  d->setMovable(d->extender->d->applet.data()->immutability() == Plasma::Mutable);
232 }
233 
234 ExtenderItem::~ExtenderItem()
235 {
236  emit destroyed(this);
237  delete d;
238 }
239 
240 KConfigGroup ExtenderItem::config() const
241 {
242  if (!d->extender->d->applet) {
243  return KConfigGroup();
244  }
245 
246  KConfigGroup cg = d->extender->d->applet.data()->config("ExtenderItems");
247  KConfigGroup itemCg = KConfigGroup(&cg, QString::number(d->extenderItemId));
248 
249  //we try to figure out if we are a transient ExtenderItem
250  //if we are, return an in memory config group (nothing will be saved on disk)
251  //if we aren't, return the ExtenderItems subgroup of our applet, as usual
252  if (d->transient) {
253  //create the dummy config group pointer if doesn't exists
254  if (!d->transientConfig) {
255  d->transientConfig = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
256  KConfigGroup dummyGroup = KConfigGroup(d->transientConfig, "ExtenderItems");
257  itemCg.reparent(&dummyGroup);
258  return itemCg;
259  }
260  KConfigGroup dummyGroup = KConfigGroup(d->transientConfig, "ExtenderItems");
261  dummyGroup = KConfigGroup(&dummyGroup, QString::number(d->extenderItemId));
262  return dummyGroup;
263  } else {
264  //if the dummy config pointer still exists, get rid of it
265  if (d->transientConfig) {
266  KConfigGroup dummyGroup = KConfigGroup(d->transientConfig, "ExtenderItems");
267  dummyGroup = KConfigGroup(&dummyGroup, QString::number(d->extenderItemId));
268  dummyGroup.reparent(&cg);
269  delete d->transientConfig.data();
270  d->transientConfig.clear();
271  return dummyGroup;
272  }
273  return itemCg;
274  }
275 }
276 
277 void ExtenderItem::setTitle(const QString &title)
278 {
279  if (d->titleLabel->text() != title) {
280  d->titleLabel->setText(title);
281  config().writeEntry("extenderTitle", title);
282  update();
283  }
284 }
285 
286 QString ExtenderItem::title() const
287 {
288  return d->titleLabel->text();
289 }
290 
291 void ExtenderItem::setName(const QString &name)
292 {
293  d->name = name;
294  config().writeEntry("extenderItemName", name);
295 }
296 
297 QString ExtenderItem::name() const
298 {
299  return d->name;
300 }
301 
302 void ExtenderItem::setWidget(QGraphicsItem *widget)
303 {
304  if (d->widget.data()) {
305  d->widget.data()->removeSceneEventFilter(this);
306  d->layout->removeItem(d->widget.data());
307  d->widget.data()->deleteLater();
308  }
309 
310  if (!widget || !widget->isWidget()) {
311  return;
312  }
313 
314  widget->setParentItem(this);
315  d->widget = static_cast<QGraphicsWidget *>(widget);
316  d->layout->insertItem(1, d->widget.data());
317  d->widget.data()->setVisible(!d->collapsed);
318 }
319 
320 QGraphicsItem *ExtenderItem::widget() const
321 {
322  return d->widget.data();
323 }
324 
325 void ExtenderItem::setIcon(const QIcon &icon)
326 {
327  if (d->collapseIcon->icon().isNull() || icon.cacheKey() != d->collapseIcon->icon().cacheKey()) {
328  d->iconName.clear();
329  d->collapseIcon->setIcon(icon);
330  d->collapseIcon->setVisible(!icon.isNull());
331  }
332 }
333 
334 void ExtenderItem::setIcon(const QString &icon)
335 {
336  if (icon != d->iconName) {
337  d->collapseIcon->setIcon(icon);
338  d->iconName = icon;
339  config().writeEntry("extenderIconName", icon);
340  }
341 }
342 
343 QIcon ExtenderItem::icon() const
344 {
345  return d->collapseIcon->icon();
346 }
347 
348 void ExtenderItem::setExtender(Extender *extender, const QPointF &pos)
349 {
350  Q_ASSERT(extender);
351 
352  //themeChanged() has to now that by now, we're no longer dragging, even though the QDrag has not
353  //been entirely finished.
354  d->dragStarted = false;
355 
356  ExtenderGroup *group = qobject_cast<ExtenderGroup*>(this);
357  QList<ExtenderItem*> childItems;
358  if (group) {
359  childItems = group->items();
360  }
361 
362  if (extender == d->extender) {
363  //We're not moving between extenders, so just insert this item back into the layout.
364  setParentItem(extender);
365  extender->d->addExtenderItem(this, pos);
366  return;
367  }
368 
369  //We are switching extender...
370  //first remove this item from the old extender.
371  d->extender->d->removeExtenderItem(this);
372 
373  //move the configuration.
374  if (!d->transient && d->hostApplet() && (extender != d->extender)) {
375  KConfigGroup c = extender->d->applet.data()->config("ExtenderItems");
376  config().reparent(&c);
377  }
378 
379  //and notify the applet of the item being detached, after the config has been moved.
380  emit d->extender->itemDetached(this);
381 
382  setParentItem(extender);
383  setParent(extender);
384  if (d->extender) {
385  disconnect(d->extender->applet(), SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)), this, SLOT(updateToolBox()));
386  }
387  d->extender = extender;
388  connect(d->extender->applet(), SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)), this, SLOT(updateToolBox()));
389 
390  //change parent.
391  extender->d->addExtenderItem(this, pos);
392 
393  //cancel the timer.
394  if (d->expirationTimer && isDetached()) {
395  d->expirationTimer->stop();
396  delete d->expirationTimer;
397  d->expirationTimer = 0;
398  }
399 
400  Corona *corona = qobject_cast<Corona*>(scene());
401 
402  if (!corona) {
403  return;
404  }
405 
406  KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
407 
408  if (isDetached()) {
409  kDebug() << "detached, adding entry to the global group";
410  KConfigGroup itemConfig = extenderItemGroup.group(QString::number(d->extenderItemId));
411  itemConfig.writeEntry("sourceAppletPluginName",
412  config().readEntry("sourceAppletPluginName", ""));
413  itemConfig.writeEntry("sourceAppletId",
414  config().readEntry("sourceAppletId", 0));
415  itemConfig.writeEntry("extenderItemName",
416  config().readEntry("extenderItemName", ""));
417  } else if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
418  kDebug() << "no longer detached, removing entry from the global group";
419  extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
420  }
421 
422  d->themeChanged();
423 
424  //we might have to enable or disable the returnToSource button.
425  d->updateToolBox();
426 
427  //invoke setGroup on all items belonging to this group, to make sure all children move to the
428  //new extender together with the group.
429  if (group) {
430  foreach (ExtenderItem *item, childItems) {
431  item->setGroup(group);
432  }
433  }
434 }
435 
436 Extender *ExtenderItem::extender() const
437 {
438  return d->extender;
439 }
440 
441 //TODO KDE5: only one setGroup()
442 void ExtenderItem::setGroup(ExtenderGroup *group)
443 {
444  setGroup(group, QPointF(-1, -1));
445 }
446 
447 void ExtenderItem::setGroup(ExtenderGroup *group, const QPointF &pos)
448 {
449  if (isGroup()) {
450  //nesting extender groups is just insane. I don't think we'd even want to support that.
451  kWarning() << "Nesting ExtenderGroups is not supported";
452  return;
453  }
454 
455  ExtenderGroup *oldGroup = d->group;
456  d->group = group;
457 
458  if (group) {
459  d->toolbox->setBackgroundPrefix("grouped");
460  config().writeEntry("group", group->name());
461  //TODO: move to another extender if the group we set is actually detached.
462  if (group->extender() != extender()) {
463  kDebug() << "moving to another extender because we're joining a detached group.";
464  setExtender(group->extender());
465  }
466  group->d->addItemToGroup(this, pos);
467  } else {
468  if (d->extender->appearance() != Extender::NoBorders) {
469  d->toolbox->setBackgroundPrefix("root");
470  } else {
471  d->toolbox->setBackgroundPrefix(QString());
472  }
473  d->toolbox->setBackgroundPrefix(QString());
474  if (oldGroup) {
475  oldGroup->d->removeItemFromGroup(this);
476  }
477  config().deleteEntry("group");
478  }
479  d->dragStarted = false;
480  d->themeChanged();
481 }
482 
483 ExtenderGroup *ExtenderItem::group() const
484 {
485  return d->group;
486 }
487 
488 bool ExtenderItem::isGroup() const
489 {
490  return (config().readEntry("isGroup", false) && qobject_cast<const Plasma::ExtenderGroup *>(this));
491 }
492 
493 bool ExtenderItem::isCollapsed() const
494 {
495  return d->collapsed;
496 }
497 
498 void ExtenderItem::setAutoExpireDelay(uint time)
499 {
500  if (!time) {
501  if (d->expirationTimer) {
502  d->expirationTimer->stop();
503  delete d->expirationTimer;
504  d->expirationTimer = 0;
505  }
506  return;
507  }
508 
509  if (!isDetached()) {
510  if (!d->expirationTimer) {
511  d->expirationTimer = new QTimer(this);
512  connect(d->expirationTimer, SIGNAL(timeout()), this, SLOT(destroy()));
513  }
514 
515  d->expirationTimer->stop();
516  d->expirationTimer->setSingleShot(true);
517  d->expirationTimer->setInterval(time);
518  d->expirationTimer->start();
519  }
520 }
521 
522 uint ExtenderItem::autoExpireDelay() const
523 {
524  if (d->expirationTimer) {
525  return d->expirationTimer->interval();
526  } else {
527  return 0;
528  }
529 }
530 
531 bool ExtenderItem::isDetached() const
532 {
533  if (d->hostApplet()) {
534  return (d->sourceApplet != d->hostApplet());
535  } else {
536  return false;
537  }
538 }
539 
540 void ExtenderItem::addAction(const QString &name, QAction *action)
541 {
542  Q_ASSERT(action);
543  if (d->actionsInOrder.contains(action)) {
544  return;
545  }
546 
547  d->actions.insert(name, action);
548  d->actionsInOrder.append(action);
549  connect(action, SIGNAL(changed()), this, SLOT(updateToolBox()));
550  connect(action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
551  d->updateToolBox();
552 }
553 
554 QAction *ExtenderItem::action(const QString &name) const
555 {
556  return d->actions.value(name, 0);
557 }
558 
559 void ExtenderItem::showCloseButton()
560 {
561  if (d->destroyActionVisibility) {
562  return;
563  }
564 
565  d->destroyActionVisibility = true;
566  d->updateToolBox();
567 }
568 
569 void ExtenderItem::hideCloseButton()
570 {
571  if (!d->destroyActionVisibility) {
572  return;
573  }
574 
575  d->destroyActionVisibility = false;
576  d->updateToolBox();
577 }
578 
579 void ExtenderItem::destroy()
580 {
581  if (d->dragStarted) {
582  //avoid being destroyed while we're being dragged.
583  return;
584  }
585 
586  //remove global entry if needed.
587  Corona *corona = qobject_cast<Corona*>(scene());
588  if (corona) {
589  KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
590  if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
591  extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
592  }
593  }
594 
595  d->hostApplet()->config("ExtenderItems").deleteGroup(QString::number(d->extenderItemId));
596  d->extender->d->removeExtenderItem(this);
597  emit d->extender->itemDetached(this);
598 
599  deleteLater();
600 }
601 
602 void ExtenderItem::setCollapsed(bool collapsed)
603 {
604  if (extender()->d->destroying) {
605  return;
606  }
607 
608  config().writeEntry("isCollapsed", collapsed);
609  d->collapsed = collapsed;
610  d->collapseIcon->setToolTip(collapsed ? i18n("Expand this widget") : i18n("Collapse this widget"));
611  if (d->widget.data()) {
612  d->widget.data()->setVisible(!collapsed);
613  if (collapsed) {
614  d->layout->removeItem(d->widget.data());
615  } else {
616  d->layout->insertItem(1, d->widget.data());
617  }
618  updateGeometry();
619 
620  if (extender()) {
621  extender()->d->adjustMinimumSize();
622  static_cast<QGraphicsLayoutItem *>(extender()->d->mainWidget)->updateGeometry();
623  if (group()) {
624  group()->layout()->invalidate();
625  static_cast<QGraphicsLayoutItem *>(group())->updateGeometry();
626  }
627 
628  extender()->d->adjustSize();
629  }
630  }
631 }
632 
633 void ExtenderItem::returnToSource()
634 {
635  if (!d || !d->sourceApplet) {
636  return;
637  }
638 
639  if (d->sourceApplet->d) {
640  setExtender(d->sourceApplet->extender());
641  }
642 }
643 
644 void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
645 {
646  if (d->background->enabledBorders() != (FrameSvg::LeftBorder | FrameSvg::RightBorder) &&
647  d->background->enabledBorders()) {
648  //Don't paint if only the left and right borders are enabled, we only use the left and right
649  //border in this situation to set the correct margins on this item.
650  d->background->paintFrame(painter, option->exposedRect, option->exposedRect);
651  }
652 }
653 
654 void ExtenderItem::moveEvent(QGraphicsSceneMoveEvent *event)
655 {
656  Q_UNUSED(event)
657  //not needed anymore, but here for binary compatibility
658 }
659 
660 void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
661 {
662  Q_UNUSED(event)
663  //resize the applet background
664  d->background->resizeFrame(size());
665  //d->resizeContent(size());
666 }
667 
668 void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
669 {
670  if (!(d->dragHandleRect().contains(event->pos())) ||
671  d->extender->d->applet.data()->immutability() != Plasma::Mutable) {
672  event->ignore();
673  return;
674  }
675 }
676 
677 void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
678 {
679  QPoint mousePressPos = event->buttonDownPos(Qt::LeftButton).toPoint();
680  if (!(event->buttons() & Qt::LeftButton) ||
681  (event->pos().toPoint() - mousePressPos).manhattanLength()
682  < QApplication::startDragDistance()) {
683  return;
684  }
685 
686  if (!d->extender->d->applet) {
687  return;
688  }
689 
690  //Start the drag:
691  d->dragStarted = true;
692  QPointF curPos = pos();
693 
694  //remove item from the layout, and add it somewhere off screen so we can render it to a pixmap,
695  //without other widgets interefing.
696  d->extender->itemRemovedEvent(this);
697  Corona *corona = qobject_cast<Corona*>(scene());
698  corona->addOffscreenWidget(this);
699 
700  //update the borders, since while dragging, we want all of theme.
701  d->themeChanged();
702 
703  //create a view to render the ExtenderItem and it's contents to a pixmap and set up a painter on
704  //a pixmap.
705  QGraphicsView view(scene());
706  QSize screenSize(view.mapFromScene(sceneBoundingRect()).boundingRect().size());
707  QPixmap pixmap(screenSize);
708  pixmap.fill(Qt::transparent);
709  QPainter p(&pixmap);
710 
711  //the following is necesarry to avoid having an offset when rendering the widget into the
712  //pixmap.
713  view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
714  view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
715  view.setFrameShape(QFrame::NoFrame);
716 
717  //aim the view and render.
718  view.resize(screenSize);
719  view.setSceneRect(sceneBoundingRect());
720  view.render(&p, QRectF(QPointF(0, 0), pixmap.size()), QRect(QPoint(0, 0), screenSize));
721 
722  //create the necesarry mimedata.
723  ExtenderItemMimeData *mimeData = new ExtenderItemMimeData();
724  mimeData->setExtenderItem(this);
725  mimeData->setPointerOffset(mousePressPos);
726 
727  //Hide empty internal extender containers when we drag the last item away. Avoids having
728  //an ugly empty applet on the desktop temporarily.
729  ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->extender->d->applet.data());
730  if (extenderApplet && d->extender->attachedItems().count() < 2 &&
731  extenderApplet->formFactor() != Plasma::Horizontal &&
732  extenderApplet->formFactor() != Plasma::Vertical) {
733  kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
734  extenderApplet->hide();
735  }
736 
737  ExtenderGroup *group = qobject_cast<ExtenderGroup*>(this);
738  bool collapsedGroup = false;
739  if (isGroup()) {
740  collapsedGroup = group->d->collapsed;
741  group->collapseGroup();
742  }
743 
744  if (!isGroup() && this->group()) {
745  setGroup(0);
746  }
747 
748  //and execute the drag.
749  QWidget *dragParent = extender()->d->applet.data()->view();
750  QDrag *drag = new QDrag(dragParent);
751  drag->setPixmap(pixmap);
752  drag->setMimeData(mimeData);
753  drag->setHotSpot(mousePressPos);
754 
755  Qt::DropAction action = drag->exec();
756 
757  corona->removeOffscreenWidget(this);
758  d->dragStarted = false;
759 
760  if (!action || !drag->target()) {
761  //we weren't moved, so reinsert the item in our current layout.
762  //TODO: make it into a stand-alone window?
763  d->themeChanged();
764  d->extender->itemAddedEvent(this, curPos);
765  if (extenderApplet) {
766  extenderApplet->show();
767  }
768  }
769 
770  if (isGroup() && !collapsedGroup) {
771  group->expandGroup();
772  }
773 }
774 
775 void ExtenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
776 {
777  if (d->dragHandleRect().contains(event->pos())) {
778  d->toggleCollapse();
779  }
780 }
781 
782 bool ExtenderItem::sceneEventFilter(QGraphicsItem *, QEvent *)
783 {
784  return false;
785 }
786 
787 void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
788 {
789  Q_UNUSED(event)
790  //not needed anymore, but here for binary compatibility
791 }
792 
793 void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
794 {
795 }
796 
797 void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
798 {
799 }
800 
801 QSizeF ExtenderItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
802 {
803  return QGraphicsWidget::sizeHint(which, constraint);
804 }
805 
806 void ExtenderItem::setTransient(const bool transient)
807 {
808  d->transient = transient;
809 }
810 
811 bool ExtenderItem::isTransient() const
812 {
813  return d->transient;
814 }
815 
816 ExtenderItemPrivate::ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
817  : q(extenderItem),
818  toolbox(0),
819  extender(hostExtender),
820  sourceApplet(0),
821  group(0),
822  background(new FrameSvg(extenderItem)),
823  collapseIcon(0),
824  expirationTimer(0),
825  dragStarted(false),
826  destroyActionVisibility(false),
827  collapsed(false),
828  transient(false)
829 {
830 }
831 
832 ExtenderItemPrivate::~ExtenderItemPrivate()
833 {
834  delete widget.data();
835 }
836 
837 //returns a Rect containing the area of the detachable where the draghandle will be drawn.
838 QRectF ExtenderItemPrivate::dragHandleRect()
839 {
840  return toolbox->boundingRect();
841 }
842 
843 void ExtenderItemPrivate::toggleCollapse()
844 {
845  q->setCollapsed(!q->isCollapsed());
846 }
847 
848 void ExtenderItemPrivate::updateToolBox()
849 {
850  Q_ASSERT(toolbox);
851  Q_ASSERT(toolboxLayout);
852 
853 
854  QAction *closeAction = actions.value("close");
855  QAction *returnToSourceAction = actions.value("extenderItemReturnToSource");
856  bool returnToSourceVisibility = q->isDetached() && sourceApplet && (hostApplet()->immutability() == Plasma::Mutable);
857  int closeIndex = -1;
858  int returnToSourceIndex = -1;
859  const int startingIndex = 2; // collapse item is index 0, title label is 1
860  int lastIndex = 2;
861  const QSizeF widgetSize = collapseIcon->sizeFromIconSize(toolbox->iconSize());
862 
863  QSet<QAction*> shownActions = actionsInOrder.toSet();
864 
865  QHash<QAction *, QGraphicsWidget *> actionWidgets;
866  for (int index = startingIndex; index < toolboxLayout->count(); ++index) {
867  QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(toolboxLayout->itemAt(index));
868  QAction *widgetAction = 0;
869 
870  if (!widget) {
871  continue;
872  } else if (qobject_cast<IconWidget*>(widget)) {
873  widgetAction = static_cast<IconWidget*>(widget)->action();
874  } else if (qobject_cast<PushButton*>(widget)) {
875  widgetAction = static_cast<PushButton*>(widget)->action();
876  } else {
877  continue;
878  }
879 
880 
881  if (closeIndex == -1 && destroyActionVisibility &&
882  closeAction && widgetAction == closeAction) {
883  closeIndex = index;
884  continue;
885  }
886 
887  if (returnToSourceIndex == -1 && returnToSourceVisibility &&
888  returnToSourceAction && widgetAction == returnToSourceAction) {
889  returnToSourceIndex = index;
890  continue;
891  }
892 
893  if (shownActions.contains(widgetAction)) {
894  actionWidgets.insert(widgetAction, widget);
895  continue;
896  }
897 
898  toolboxLayout->removeAt(index);
899  widget->deleteLater();
900  }
901 
902 
903  // ensure the collapseIcon is the correct size.
904  collapseIcon->setMinimumSize(widgetSize);
905  collapseIcon->setMaximumSize(widgetSize);
906 
907  //add the actions that are actually set to visible.
908  foreach (QAction *action, actionsInOrder) {
909  if (action->isVisible() && action != closeAction) {
910  IconWidget *icon = qobject_cast<IconWidget*>(actionWidgets.value(action));
911  PushButton *button = qobject_cast<PushButton*>(actionWidgets.value(action));
912 
913  if (action->icon().isNull() && !action->text().isNull()) {
914  if (!button) {
915  button = new PushButton(q);
916  button->setAction(action);
917  }
918 
919  button->setMinimumHeight(widgetSize.height());
920  button->setMaximumHeight(widgetSize.height());
921  button->setCursor(Qt::ArrowCursor);
922  toolboxLayout->insertItem(startingIndex, button);
923  ++lastIndex;
924  } else {
925  if (!icon) {
926  icon = new IconWidget(q);
927  icon->setAction(action);
928  }
929 
930  if (action->icon().isNull()) {
931  icon->setText(action->text());
932  }
933  icon->setMinimumSize(widgetSize);
934  icon->setMaximumSize(widgetSize);
935  icon->setCursor(Qt::ArrowCursor);
936  toolboxLayout->insertItem(startingIndex, icon);
937  ++lastIndex;
938  }
939  }
940  }
941 
942  //add the returntosource icon if we are detached, and have a source applet.
943  if (returnToSourceVisibility && returnToSourceIndex == -1) {
944  IconWidget *returnToSourceIcon = new IconWidget(q);
945  if (!returnToSourceAction) {
946  returnToSourceAction = new QAction(q);
947  returnToSourceAction->setToolTip(i18n("Reattach"));
948  actions.insert("extenderItemReturnToSource", returnToSourceAction);
949  QObject::connect(returnToSourceAction, SIGNAL(triggered()), q, SLOT(returnToSource()));
950  }
951 
952  returnToSourceIcon->setAction(returnToSourceAction);
953  returnToSourceIcon->setSvg("widgets/configuration-icons", "return-to-source");
954  returnToSourceIcon->setMinimumSize(widgetSize);
955  returnToSourceIcon->setMaximumSize(widgetSize);
956  returnToSourceIcon->setCursor(Qt::ArrowCursor);
957 
958  if (closeIndex == -1) {
959  toolboxLayout->addItem(returnToSourceIcon);
960  } else {
961  toolboxLayout->insertItem(closeIndex - 1, returnToSourceIcon);
962  }
963  ++lastIndex;
964  }
965 
966  //add the close icon if desired.
967  if (destroyActionVisibility && closeIndex == -1) {
968  IconWidget *destroyButton = new IconWidget(q);
969  if (!closeAction) {
970  closeAction = new QAction(q);
971  actions.insert("close", closeAction);
972  if (returnToSourceAction) {
973  returnToSourceAction->setToolTip(i18n("Close"));
974  }
975  QObject::connect(closeAction, SIGNAL(triggered()), q, SLOT(destroy()));
976  }
977 
978  destroyButton->setAction(closeAction);
979  destroyButton->setSvg("widgets/configuration-icons", "close");
980  destroyButton->setMinimumSize(widgetSize);
981  destroyButton->setMaximumSize(widgetSize);
982  destroyButton->setCursor(Qt::ArrowCursor);
983  toolboxLayout->addItem(destroyButton);
984  ++lastIndex;
985  }
986 
987  //to keep the text really centered
988  toolboxLayout->setItemSpacing(0, KIconLoader::SizeSmall * (lastIndex - 2));
989  if (lastIndex == 2) {
990  if (QApplication::layoutDirection() == Qt::RightToLeft) {
991  toolboxLayout->setContentsMargins(KIconLoader::SizeSmall, 0, 0, 0);
992  } else {
993  toolboxLayout->setContentsMargins(0, 0, KIconLoader::SizeSmall, 0);
994  }
995  } else {
996  toolboxLayout->setContentsMargins(0, 0, 0, 0);
997  }
998 }
999 
1000 Applet *ExtenderItemPrivate::hostApplet() const
1001 {
1002  if (extender) {
1003  return extender->d->applet.data();
1004  } else {
1005  return 0;
1006  }
1007 }
1008 
1009 void ExtenderItemPrivate::themeChanged()
1010 {
1011  kDebug();
1012  if (dragStarted) {
1013  background->setImagePath("opaque/dialogs/background");
1014  background->setEnabledBorders(FrameSvg::AllBorders);
1015  } else {
1016  background->setImagePath("widgets/extender-background");
1017  background->setEnabledBorders(extender->enabledBordersForItem(q));
1018  }
1019 
1020  qreal left, top, right, bottom;
1021  background->getMargins(left, top, right, bottom);
1022  layout->setContentsMargins(left, top, right, bottom);
1023 
1024  if (group) {
1025  toolbox->setBackgroundPrefix("grouped");
1026  } else {
1027  if (extender->items().count() <= 1 || extender->items().first() == q || extender->appearance() != Extender::NoBorders) {
1028  toolbox->setBackgroundPrefix("root");
1029  } else {
1030  toolbox->setBackgroundPrefix(QString());
1031  }
1032  }
1033 
1034  toolbox->updateTheme();
1035 }
1036 
1037 void ExtenderItemPrivate::sourceAppletRemoved()
1038 {
1039  //the original source applet is removed, set the pointer to 0 and no longer show the return to
1040  //source icon.
1041  sourceApplet = 0;
1042  updateToolBox();
1043 }
1044 
1045 void ExtenderItemPrivate::actionDestroyed(QObject *o)
1046 {
1047  QAction *action = static_cast<QAction *>(o);
1048  QMutableHashIterator<QString, QAction *> hit(actions);
1049  while (hit.hasNext()) {
1050  if (hit.next().value() == action) {
1051  hit.remove();
1052  break;
1053  }
1054  }
1055 
1056  QMutableListIterator<QAction *> lit(actionsInOrder);
1057  while (lit.hasNext()) {
1058  if (lit.next() == action) {
1059  lit.remove();
1060  break;
1061  }
1062  }
1063 }
1064 
1065 void ExtenderItemPrivate::setMovable(bool movable)
1066 {
1067  if (movable) {
1068  titleLabel->setCursor(Qt::OpenHandCursor);
1069  toolbox->setCursor(Qt::OpenHandCursor);
1070  } else {
1071  titleLabel->unsetCursor();
1072  toolbox->unsetCursor();
1073  }
1074 }
1075 
1076 uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
1077 
1078 } // namespace Plasma
1079 
1080 #include "extenderitem.moc"
Plasma::ExtenderItem::setTitle
void setTitle(const QString &title)
Definition: extenderitem.cpp:277
Plasma::ExtenderItem::extender
Extender extender
Definition: extenderitem.h:87
Plasma::ExtenderItem::isDetached
bool isDetached() const
Definition: extenderitem.cpp:531
Plasma::ExtenderItem::returnToSource
void returnToSource()
Returns the extender item to its source applet.
Definition: extenderitem.cpp:633
Plasma::Vertical
The applet is constrained horizontally, but can expand vertically.
Definition: plasma.h:77
Plasma::FrameSvg::LeftBorder
Definition: framesvg.h:91
Plasma::ExtenderItem::sceneEventFilter
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event)
Definition: extenderitem.cpp:782
Plasma::ExtenderItem::isCollapsed
bool isCollapsed() const
Definition: extenderitem.cpp:493
Plasma::ImmutabilityType
ImmutabilityType
Defines the immutability of items like applets, corona and containments they can be free to modify...
Definition: plasma.h:197
Plasma::ExtenderGroup::expandGroup
void expandGroup()
Expands this group to show all ExtenderItems that are contained in this group.
Definition: extendergroup.cpp:162
Plasma::Extender
Extends applets to allow detachable parts.
Definition: extender.h:65
Plasma::ExtenderItem::setGroup
void setGroup(ExtenderGroup *group)
Definition: extenderitem.cpp:442
Plasma::ExtenderItem::setCollapsed
void setCollapsed(bool collapsed)
Collapse or expand the extender item.
Definition: extenderitem.cpp:602
Plasma::Extender::NoBorders
Draws no borders on the extender's items.
Definition: extender.h:80
Plasma::ExtenderGroup::items
QList< ExtenderItem * > items() const
Definition: extendergroup.cpp:109
Plasma::ExtenderItem::name
QString name() const
Plasma::ExtenderItem::config
KConfigGroup config() const
fetch the configuration of this widget.
Definition: extenderitem.cpp:240
Plasma::FrameSvg::AllBorders
Definition: framesvg.h:93
Plasma::ExtenderItem::icon
QIcon icon() const
Plasma::ExtenderItem::setName
void setName(const QString &name)
You can assign names to extender items to look them up through the item() function.
Definition: extenderitem.cpp:291
Plasma::Corona::containments
QList< Containment * > containments() const
Definition: corona.cpp:328
extender.h
Plasma::ExtenderItem::addAction
void addAction(const QString &name, QAction *action)
Definition: extenderitem.cpp:540
Plasma::Theme::DefaultFont
The standard text font.
Definition: theme.h:80
Plasma::ExtenderItem::resizeEvent
void resizeEvent(QGraphicsSceneResizeEvent *event)
Definition: extenderitem.cpp:660
QWidget
Plasma::Theme::font
Q_INVOKABLE QFont font(FontRole role) const
Returns the font to be used by themed items.
Definition: theme.cpp:970
Plasma::Extender::applet
Applet * applet() const
Definition: extender.cpp:358
Plasma::Containment::applets
Applet::List applets() const
Definition: containment.cpp:950
theme.h
iconwidget.h
containment.h
Plasma::ExtenderItem::sizeHint
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
Definition: extenderitem.cpp:801
Plasma::FrameSvg
Provides an SVG with borders.
Definition: framesvg.h:76
Plasma::ExtenderItem::moveEvent
void moveEvent(QGraphicsSceneMoveEvent *event)
Definition: extenderitem.cpp:654
QObject
Plasma::ExtenderItem::widget
QGraphicsItem * widget() const
Plasma::Horizontal
The applet is constrained vertically, but can expand horizontally.
Definition: plasma.h:75
Plasma::ExtenderItem::action
QAction * action(const QString &name) const
Definition: extenderitem.cpp:554
Plasma::Mutable
The item can be modified in any way.
Definition: plasma.h:198
Plasma::ExtenderItem::isGroup
bool isGroup() const
Definition: extenderitem.cpp:488
Plasma::ExtenderItem::destroy
void destroy()
Destroys the extender item.
Definition: extenderitem.cpp:579
pushbutton.h
Plasma::Applet
The base Applet class.
Definition: applet.h:77
Plasma::Corona::config
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
Definition: corona.cpp:340
Plasma::ExtenderItem::mouseDoubleClickEvent
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
Definition: extenderitem.cpp:775
Plasma::Applet::id
uint id
Definition: applet.h:91
view.h
Plasma::ExtenderItem::setExtender
void setExtender(Extender *extender, const QPointF &pos=QPointF(-1,-1))
Definition: extenderitem.cpp:348
Plasma::ExtenderItem::destroyed
void destroyed(Plasma::ExtenderItem *item)
Emitted when the extender item is destroyed.
Plasma::Label
Provides a plasma-themed QLabel.
Definition: label.h:40
Plasma::Applet::pluginName
QString pluginName
Definition: applet.h:82
Plasma::ExtenderItem::group
ExtenderGroup * group() const
Definition: extenderitem.cpp:483
applet.h
Plasma::ExtenderItem::hideCloseButton
void hideCloseButton()
Hides the close button in this item's drag handle.
Definition: extenderitem.cpp:569
Plasma::ExtenderItem::name
QString name
Definition: extenderitem.h:85
Plasma::ExtenderItem::showCloseButton
void showCloseButton()
Shows a close button in this item's drag handle.
Definition: extenderitem.cpp:559
Plasma::ExtenderItem::isTransient
bool isTransient() const
Definition: extenderitem.cpp:811
dialog.h
Plasma::Theme
Interface to the Plasma theme.
Definition: theme.h:56
Plasma::ExtenderItem::~ExtenderItem
~ExtenderItem()
Definition: extenderitem.cpp:234
Plasma::ExtenderItem::ExtenderItem
ExtenderItem(Extender *hostExtender, uint extenderItemId=0)
The constructor takes care of adding this item to an extender.
Definition: extenderitem.cpp:128
Plasma::Theme::defaultTheme
static Theme * defaultTheme()
Singleton pattern accessor.
Definition: theme.cpp:544
Plasma::FrameSvg::RightBorder
Definition: framesvg.h:92
Plasma::ExtenderItem::setTransient
void setTransient(const bool transient)
Set the ExtenderItem as transient: won't be saved in the Plasma config and won't be restored...
Definition: extenderitem.cpp:806
corona.h
IconWidget
Provides a generic icon.
Definition: iconwidget.h:37
framesvg.h
Plasma::IconWidget
Definition: iconwidget.h:56
Plasma::ExtenderItem::mouseMoveEvent
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
Definition: extenderitem.cpp:677
QGraphicsView
Plasma::ExtenderGroup
Allows for grouping of extender items.
Definition: extendergroup.h:50
label.h
Plasma::ExtenderItem::mousePressEvent
void mousePressEvent(QGraphicsSceneMouseEvent *event)
Definition: extenderitem.cpp:668
Plasma::ExtenderGroup::collapseGroup
void collapseGroup()
Collapses this group to hide all ExtenderItems that are contained in this group, and shows the summar...
Definition: extendergroup.cpp:190
Plasma::Containment
The base class for plugins that provide backgrounds and applet grouping containers.
Definition: containment.h:72
extendergroup.h
Plasma::ExtenderItem::setIcon
void setIcon(const QString &icon)
Definition: extenderitem.cpp:334
Plasma::Corona::addOffscreenWidget
void addOffscreenWidget(QGraphicsWidget *widget)
Adds a widget in the topleft quadrant in the scene.
Definition: corona.cpp:377
popupapplet.h
Plasma::ExtenderItem::title
QString title() const
Plasma::Corona::removeOffscreenWidget
void removeOffscreenWidget(QGraphicsWidget *widget)
Removes a widget from the topleft quadrant in the scene.
Definition: corona.cpp:410
Plasma::ExtenderItem
Provides detachable items for an Extender.
Definition: extenderitem.h:80
QStyleOptionGraphicsItem
Plasma::ExtenderItem::paint
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
Definition: extenderitem.cpp:644
Plasma::ExtenderItem::collapsed
bool collapsed
Definition: extenderitem.h:88
Plasma::ExtenderItem::extender
Extender * extender() const
extenderitem.h
Plasma::ExtenderItem::setWidget
void setWidget(QGraphicsItem *widget)
Definition: extenderitem.cpp:302
Plasma::ExtenderItem::autoExpireDelay
uint autoExpireDelay() const
Plasma::ExtenderItem::setAutoExpireDelay
void setAutoExpireDelay(uint time)
Definition: extenderitem.cpp:498
Plasma::ExtenderItem::mouseReleaseEvent
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
Definition: extenderitem.cpp:787
Plasma::Corona
A QGraphicsScene for Plasma::Applets.
Definition: corona.h:48
QGraphicsWidget
Plasma::ExtenderItem::hoverLeaveEvent
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
Definition: extenderitem.cpp:797
Plasma::ExtenderItem::hoverMoveEvent
void hoverMoveEvent(QGraphicsSceneHoverEvent *event)
Definition: extenderitem.cpp:793
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:33 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal