• 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
extender.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 "extender.h"
21 
22 #include <QAction>
23 #include <QDesktopWidget>
24 #include <QLabel>
25 #include <QGraphicsSceneDragDropEvent>
26 #include <QGraphicsGridLayout>
27 #include <QGraphicsLinearLayout>
28 #include <QPainter>
29 
30 #include "applet.h"
31 #include "containment.h"
32 #include "corona.h"
33 #include "dialog.h"
34 #include "extendergroup.h"
35 #include "extenderitem.h"
36 #include "framesvg.h"
37 #include "paintutils.h"
38 #include "popupapplet.h"
39 #include "svg.h"
40 #include "theme.h"
41 #include "widgets/label.h"
42 #include "widgets/scrollwidget.h"
43 
44 #include "private/applet_p.h"
45 #include "private/applethandle_p.h"
46 #include "private/extender_p.h"
47 #include "private/extenderapplet_p.h"
48 #include "private/extenderitem_p.h"
49 #include "private/extenderitemmimedata_p.h"
50 #include "private/popupapplet_p.h"
51 
52 namespace Plasma
53 {
54 
55 Spacer::Spacer(QGraphicsItem *parent)
56  : QGraphicsWidget(parent)
57 {
58 }
59 
60 Spacer::~Spacer()
61 {
62 }
63 
64 void Spacer::setMargins(qreal left, qreal top, qreal right, qreal bottom)
65 {
66  m_left = left;
67  m_top = top;
68  m_right = right;
69  m_bottom = bottom;
70 }
71 
72 
73 void Spacer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
74 {
75  Q_UNUSED(option)
76  Q_UNUSED(widget)
77 
78  painter->setRenderHint(QPainter::Antialiasing);
79  QPainterPath p = Plasma::PaintUtils::roundedRectangle(
80  contentsRect().adjusted(m_left, m_top, -m_right, -m_bottom), 4);
81 
82  QColor c = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
83  c.setAlphaF(0.3);
84  painter->fillPath(p, c);
85 }
86 
87 
88 Extender::Extender(Applet *applet)
89  : QGraphicsWidget(applet),
90  d(new ExtenderPrivate(applet, this))
91 {
92  //At multiple places in the extender code, we make the assumption that an applet doesn't have
93  //more then one extender. If a second extender is created, destroy the first one to avoid leaks.
94  if (applet->d->extender) {
95  kWarning() << "Applet already has an extender, and can have only one extender."
96  << "The previous extender will be destroyed.";
97  delete applet->d->extender.data();
98  } else if (PopupApplet *popup = qobject_cast<PopupApplet *>(applet)) {
99  if (!popup->d->graphicsWidget) {
100  // ensure the popup gets a dialog with us as the graphics widget
101  popup->d->popupConstraintsEvent(SizeConstraint);
102  }
103  }
104 
105  applet->d->extender = this;
106  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
107 
108  d->scrollWidget = new ScrollWidget(this);
109  d->scrollWidget->setOverflowBordersVisible(false);
110  d->scrollWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
111  d->mainWidget = new QGraphicsWidget(d->scrollWidget);
112  d->scrollWidget->setWidget(d->mainWidget);
113  d->mainWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
114  connect(d->scrollWidget, SIGNAL(viewportGeometryChanged(QRectF)),
115  this, SLOT(viewportGeometryChanged(QRectF)));
116 
117  d->layout = new QGraphicsLinearLayout(d->mainWidget);
118  d->layout->setOrientation(Qt::Vertical);
119  d->layout->setContentsMargins(0, 0, 0, 0);
120  d->layout->setSpacing(0);
121 
122 
123  QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical, this);
124  lay->addItem(d->scrollWidget);
125  setContentsMargins(0, 0, 0, 0);
126  lay->setContentsMargins(0, 0, 0, 0);
127 
128  d->loadExtenderItems();
129 
130  setAcceptDrops(true);
131 }
132 
133 Extender::~Extender()
134 {
135  d->destroying = true;
136 
137  // when deleting items that are connected to us, it can happen that
138  // other items which are in groups may get deleted as well. so we first
139  // build a new list of guarded pointers, and then use that list. that
140  // way when items are deleted behind our back, we are still safe.
141  // FIXME: having groups and items in the same collection is probably a mistake,
142  // so would be a good candidate for a refactoring exercise
143  QList<QWeakPointer<ExtenderItem> > guardedItems;
144 
145  foreach (ExtenderItem *item, d->attachedExtenderItems) {
146  guardedItems << QWeakPointer<ExtenderItem>(item);
147  }
148 
149  d->attachedExtenderItems.clear();
150 
151  foreach (const QWeakPointer<ExtenderItem> &guardedItem, guardedItems) {
152  ExtenderItem *item = guardedItem.data();
153  if (item) {
154  item->disconnect(this);
155  delete item;
156  }
157  }
158 
159 
160  delete d;
161 }
162 
163 void Extender::setEmptyExtenderMessage(const QString &message)
164 {
165  d->emptyExtenderMessage = message;
166 
167  if (d->emptyExtenderLabel) {
168  d->emptyExtenderLabel->setText(message);
169  }
170 }
171 
172 QString Extender::emptyExtenderMessage() const
173 {
174  return d->emptyExtenderMessage;
175 }
176 
177 QList<ExtenderItem*> Extender::items() const
178 {
179  QList<ExtenderItem*> result;
180 
181  //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
182  //iterate through all extenders we can find and check each extenders source applet.
183  if (!d->applet) {
184  return QList<ExtenderItem*>();
185  }
186 
187  Containment *containment = d->applet.data()->containment();
188  if (!containment) {
189  return result;
190  }
191 
192  foreach (Containment *c, containment->corona()->containments()) {
193  foreach (Applet *applet, c->applets()) {
194  if (applet->d->extender) {
195  foreach (ExtenderItem *item, applet->d->extender.data()->d->attachedExtenderItems) {
196  if (item->d->sourceApplet == d->applet.data()) {
197  result.append(item);
198  }
199  }
200  }
201  }
202  }
203 
204  return result;
205 }
206 
207 QList<ExtenderItem*> Extender::attachedItems() const
208 {
209  return d->attachedExtenderItems;
210 }
211 
212 QList<ExtenderItem*> Extender::detachedItems() const
213 {
214  QList<ExtenderItem*> result;
215 
216  //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
217  //iterate through all extenders we can find and check each extenders source applet.
218  if (!d->applet) {
219  return QList<ExtenderItem*>();
220  }
221  Containment *containment = d->applet.data()->containment();
222  if (!containment) {
223  return result;
224  }
225 
226  foreach (Containment *c, containment->corona()->containments()) {
227  foreach (Applet *applet, c->applets()) {
228  if (applet->d->extender) {
229  foreach (ExtenderItem *item, applet->d->extender.data()->attachedItems()) {
230  if (item->d->sourceApplet == d->applet.data() && item->isDetached()) {
231  result.append(item);
232  }
233  }
234  }
235  }
236  }
237 
238  return result;
239 }
240 
241 ExtenderItem *Extender::item(const QString &name) const
242 {
243  // chances are the item is in our own extender, so check there first
244  foreach (ExtenderItem *item, d->attachedExtenderItems) {
245  if (item->d->sourceApplet == d->applet.data() && item->name() == name) {
246  return item;
247  }
248  }
249 
250  // maybe it's been moved elsewhere, so lets search through the entire tree of elements
251  //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
252  //iterate through all extenders we can find and check each extenders source applet.
253  if (!d->applet) {
254  return 0;
255  }
256 
257  Containment *containment = d->applet.data()->containment();
258  if (!containment) {
259  return 0;
260  }
261 
262  QList<Containment *> containments;
263  if (containment->corona()) {
264  containments = containment->corona()->containments();
265  } else {
266  containments << containment;
267  }
268 
269  foreach (Containment *c, containments) {
270  foreach (Applet *applet, c->applets()) {
271  if (applet->d->extender) {
272  if (applet->d->extender.data() == this) {
273  // skip it, we checked it already
274  continue;
275  }
276 
277  if (!applet->d->extender) {
278  continue;
279  }
280 
281  foreach (ExtenderItem *item, applet->d->extender.data()->attachedItems()) {
282  if (item->d->sourceApplet == d->applet.data() && item->name() == name) {
283  return item;
284  }
285  }
286  }
287  }
288  }
289 
290  return 0;
291 }
292 
293 ExtenderGroup *Extender::group(const QString &name) const
294 {
295  return qobject_cast<ExtenderGroup*>(item(name));
296 }
297 
298 bool Extender::hasItem(const QString &name) const
299 {
300  if (item(name)) {
301  return true;
302  }
303 
304  if (!d->applet) {
305  return false;
306  }
307 
308  //if item(name) returns false, that doesn't mean that the item doesn't exist, just that it has
309  //not been instantiated yet. check to see if there's mention of this item existing in the
310  //plasma config's section DetachedExtenderItems
311  Corona *corona = qobject_cast<Corona *>(scene());
312  if (!corona) {
313  return false;
314  }
315 
316  KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
317  foreach (const QString &extenderItemId, extenderItemGroup.groupList()) {
318  KConfigGroup cg = extenderItemGroup.group(extenderItemId);
319  if (uint(cg.readEntry("sourceAppletId", 0)) == d->applet.data()->id() &&
320  cg.readEntry("extenderItemName", "") == name &&
321  cg.readEntry("sourceAppletPluginName", "") == d->applet.data()->pluginName()) {
322  return true;
323  }
324  }
325 
326  return false;
327 }
328 
329 void Extender::setAppearance(Appearance appearance)
330 {
331  if (d->appearance == appearance) {
332  return;
333  }
334 
335  d->appearance = appearance;
336  d->updateBorders();
337 }
338 
339 Extender::Appearance Extender::appearance() const
340 {
341  return d->appearance;
342 }
343 
344 QList<ExtenderGroup*> Extender::groups() const
345 {
346  QList<ExtenderGroup *> result;
347  foreach (ExtenderItem *item, d->attachedExtenderItems) {
348  if (item->isGroup() && !result.contains(item->group())) {
349  ExtenderGroup *group = qobject_cast<ExtenderGroup *>(item);
350  if (group) {
351  result.append(group);
352  }
353  }
354  }
355  return result;
356 }
357 
358 Applet *Extender::applet() const
359 {
360  return d->applet.data();
361 }
362 
363 void Extender::saveState()
364 {
365  foreach (ExtenderItem *item, attachedItems()) {
366  item->config().writeEntry("extenderItemPosition", item->geometry().y());
367  }
368 }
369 
370 QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
371 {
372  if (change == QGraphicsItem::ItemPositionHasChanged) {
373  emit geometryChanged();
374  }
375 
376  return QGraphicsWidget::itemChange(change, value);
377 }
378 
379 void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
380 {
381  QGraphicsWidget::resizeEvent(event);
382  emit geometryChanged();
383 }
384 
385 void Extender::mousePressEvent(QGraphicsSceneMouseEvent *event)
386 {
387  Q_UNUSED(event)
388 
389  if (!d->applet) {
390  return;
391  }
392 
393  PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
394  if (isEmpty() && popupApplet) {
395  popupApplet->hidePopup();
396  }
397 }
398 
399 void Extender::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
400 {
401  if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
402  event->accept();
403 
404  const ExtenderItemMimeData *mimeData =
405  qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
406 
407  if (mimeData) {
408  itemHoverEnterEvent(mimeData->extenderItem());
409 
410  PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
411  if (popupApplet) {
412  popupApplet->showPopup();
413  }
414  }
415  }
416 }
417 
418 void Extender::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
419 {
420  if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
421  const ExtenderItemMimeData *mimeData =
422  qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
423 
424  if (mimeData) {
425  itemHoverMoveEvent(mimeData->extenderItem(), event->pos());
426 
427  d->setPositionFromDragPosition(event->scenePos());
428  }
429  }
430 }
431 
432 void Extender::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
433 {
434  if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
435  const ExtenderItemMimeData *mimeData =
436  qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
437 
438  if (mimeData) {
439  itemHoverLeaveEvent(mimeData->extenderItem());
440 
441  //Some logic to conveniently show/hide popups or applets when logical.
442  Extender *sourceExtender = mimeData->extenderItem()->d->extender;
443 
444  //Hide popups when they're not the extender where we started, and we're leaving the
445  //extender. Use a small timeout here, to avoid accidental hides of extenders we're
446  //targetting.
447  PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
448  if (popupApplet && sourceExtender != this) {
449  kDebug() << "leaving another extender then the extender we started, so hide the popup.";
450  popupApplet->showPopup(250);
451  }
452 
453  //Hide popups when we drag the last item away.
454  if (popupApplet && sourceExtender == this && (attachedItems().count() < 2)) {
455  kDebug() << "leaving the extender, and there are no more attached items so hide the popup.";
456  popupApplet->hidePopup();
457  }
458 
459  //Hide empty internal extender containers when we drag the last item away. Avoids having
460  //an ugly empty applet on the desktop temporarily.
461  ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->applet.data());
462  if (extenderApplet && sourceExtender == this && attachedItems().count() < 2 &&
463  extenderApplet->formFactor() != Plasma::Horizontal &&
464  extenderApplet->formFactor() != Plasma::Vertical) {
465  kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
466  extenderApplet->hide();
467  AppletHandle *handle = dynamic_cast<AppletHandle*>(extenderApplet->parentItem());
468  if (handle) {
469  handle->hide();
470  }
471  }
472  }
473  }
474 }
475 
476 void Extender::dropEvent(QGraphicsSceneDragDropEvent *event)
477 {
478  if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
479  const ExtenderItemMimeData *mimeData =
480  qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
481 
482  if (mimeData) {
483  mimeData->extenderItem()->setExtender(this, event->pos());
484  QApplication::restoreOverrideCursor();
485  itemHoverLeaveEvent(0);
486  }
487  }
488 }
489 
490 void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
491 {
492  ExtenderGroup *group = item->isGroup() ? static_cast<ExtenderGroup*>(item) : 0;
493  if (group && group->autoHide() && group->items().isEmpty()) {
494  return;
495  }
496 
497  if (!item->group()) {
498  if (pos == QPointF(-1, -1)) {
499  //if it was already there, reposition
500  d->layout->removeItem(item);
501  //if just plain adding an item, add it at a sane position:
502  if (appearance() == BottomUpStacked) {
503  //at the top
504  d->layout->insertItem(0, item);
505  } else {
506  //at the bottom
507  d->layout->addItem(item);
508  }
509  } else {
510  kDebug() << "inserting at" << pos << d->insertIndexFromPos(pos) << item->size();
511  d->layout->insertItem(d->insertIndexFromPos(pos), item);
512  kDebug() << item->size();
513  }
514  }
515 
516  d->adjustMinimumSize();
517 
518  //remove the empty extender message if needed.
519  d->updateEmptyExtenderLabel();
520  d->updateBorders();
521 
522  d->adjustSize();
523 }
524 
525 void Extender::itemRemovedEvent(ExtenderItem *item)
526 {
527  if (d->destroying) {
528  return;
529  }
530 
531  d->layout->removeItem(item);
532 
533  if (d->spacerWidget) {
534  d->layout->removeItem(d->spacerWidget);
535  delete d->spacerWidget;
536  d->spacerWidget = 0;
537  }
538 
539  d->adjustMinimumSize();
540 
541  //add the empty extender message if needed.
542  d->updateEmptyExtenderLabel();
543  d->updateBorders();
544 
545  d->layout->updateGeometry();
546  static_cast<QGraphicsLayoutItem *>(d->scrollWidget)->updateGeometry();
547  updateGeometry();
548 
549  d->adjustSize();
550 }
551 
552 void Extender::itemHoverEnterEvent(ExtenderItem *item)
553 {
554  itemHoverMoveEvent(item, QPointF(0, 0));
555 }
556 
557 void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
558 {
559  if (d->spacerWidget && d->spacerWidget->geometry().contains(pos)) {
560  return;
561  }
562 
563  //Make sure we remove any spacer that might already be in the layout.
564  if (d->spacerWidget) {
565  d->layout->removeItem(d->spacerWidget);
566  }
567 
568  int insertIndex = d->insertIndexFromPos(pos);
569 
570  //Create a widget that functions as spacer, and add that to the layout.
571  if (!d->spacerWidget) {
572  Spacer *widget = new Spacer(this);
573  qreal left, top, right, bottom;
574  d->background->getMargins(left, top, right, bottom);
575  widget->setMargins(left, 4, right, 4);
576 
577  widget->setMinimumSize(item->minimumSize());
578  widget->setPreferredSize(item->preferredSize());
579  widget->setMaximumSize(item->maximumSize());
580  widget->setSizePolicy(item->sizePolicy());
581  d->spacerWidget = widget;
582  }
583  d->layout->insertItem(insertIndex, d->spacerWidget);
584 
585  //Make sure we remove any 'no detachables' label that might be there, and update the layout.
586  d->updateEmptyExtenderLabel();
587 }
588 
589 void Extender::itemHoverLeaveEvent(ExtenderItem *item)
590 {
591  Q_UNUSED(item);
592 
593  if (d->spacerWidget) {
594  //Remove any trace of the spacer widget.
595  d->layout->removeItem(d->spacerWidget);
596  delete d->spacerWidget;
597  d->spacerWidget = 0;
598 
599  d->currentSpacerIndex = -1;
600 
601  d->updateEmptyExtenderLabel();
602  }
603 }
604 
605 FrameSvg::EnabledBorders Extender::enabledBordersForItem(ExtenderItem *item) const
606 {
607  if (d->layout->count() < 1) {
608  return 0;
609  }
610 
611  ExtenderItem *topItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(0));
612  ExtenderItem *bottomItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(d->layout->count() - 1));
613 
614  FrameSvg::EnabledBorders borders = FrameSvg::NoBorder;
615 
616  if (item->group()) {
617  return FrameSvg::NoBorder;
618  } else if (d->appearance == TopDownStacked && bottomItem != item) {
619  borders = FrameSvg::LeftBorder | FrameSvg::BottomBorder | FrameSvg::RightBorder;
620  } else if (d->appearance == BottomUpStacked && topItem != item) {
621  borders = FrameSvg::LeftBorder | FrameSvg::TopBorder | FrameSvg::RightBorder;
622  } else if (d->appearance != NoBorders) {
623  borders = FrameSvg::LeftBorder | FrameSvg::RightBorder;
624  } else {
625  return FrameSvg::NoBorder;
626  }
627 
628  if (d->scrollWidget->viewportGeometry().height() < d->mainWidget->boundingRect().height()) {
629  if (QApplication::layoutDirection() == Qt::RightToLeft) {
630  borders &= ~FrameSvg::LeftBorder;
631  } else {
632  borders &= ~FrameSvg::RightBorder;
633  }
634  }
635 
636  //someone (i.e. a Dialog) told the extender to disable some border?
637  borders &= ~d->disabledBordersHint;
638 
639 
640  return borders;
641 }
642 
643 ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
644  q(extender),
645  applet(applet),
646  background(new FrameSvg(extender)),
647  disabledBordersHint(FrameSvg::NoBorder),
648  currentSpacerIndex(-1),
649  spacerWidget(0),
650  emptyExtenderMessage(QString()),
651  emptyExtenderLabel(0),
652  appearance(Extender::NoBorders),
653  destroying(false),
654  scrollbarVisible(false)
655 {
656  background->setImagePath("widgets/extender-background");
657 }
658 
659 ExtenderPrivate::~ExtenderPrivate()
660 {
661 }
662 
663 void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
664 {
665  if (attachedExtenderItems.contains(item)) {
666  pendingItems.remove(item);
667  q->itemAddedEvent(item, pos);
668  return;
669  }
670 
671  QObject::connect(item, SIGNAL(destroyed(Plasma::ExtenderItem*)), q, SLOT(extenderItemDestroyed(Plasma::ExtenderItem*)));
672  attachedExtenderItems.append(item);
673  q->itemHoverLeaveEvent(item);
674  pendingItems.insert(item, pos);
675  QTimer::singleShot(0, q, SLOT(delayItemAddedEvent()));
676 }
677 
678 void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
679 {
680  attachedExtenderItems.removeAll(item);
681  pendingItems.remove(item);
682 
683  //collapse the popupapplet if the last item is removed.
684  if (attachedExtenderItems.isEmpty()) {
685  PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet.data());
686  if (popupApplet) {
687  popupApplet->hidePopup();
688  }
689  }
690 
691  q->itemRemovedEvent(item);
692 }
693 
694 int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
695 {
696  int insertIndex = -1;
697 
698  //XXX: duplicated from panel
699  if (pos != QPointF(-1, -1)) {
700  for (int i = 0; i < layout->count(); ++i) {
701  QRectF siblingGeometry = layout->itemAt(i)->geometry();
702  qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
703  if (pos.y() < middle) {
704  insertIndex = i;
705  break;
706  } else if (pos.y() <= siblingGeometry.bottom()) {
707  insertIndex = i + 1;
708  break;
709  }
710  }
711  }
712 
713  return insertIndex;
714 }
715 
716 void ExtenderPrivate::loadExtenderItems()
717 {
718  if (!applet) {
719  return;
720  }
721 
722  KConfigGroup cg = applet.data()->config("ExtenderItems");
723 
724  //first create a list of extenderItems, and then sort them on their position, so the items get
725  //recreated in the correct order.
726  QList<QPair<int, QString> > groupList;
727  foreach (const QString &extenderItemId, cg.groupList()) {
728  KConfigGroup dg = cg.group(extenderItemId);
729  groupList.append(qMakePair(dg.readEntry("extenderItemPosition", 0), extenderItemId));
730  }
731  qSort(groupList);
732 
733  //iterate over the extender items
734  for (int i = 0; i < groupList.count(); i++) {
735  QPair<int, QString> pair = groupList[i];
736 
737  KConfigGroup dg = cg.group(pair.second);
738 
739  //load the relevant settings.
740  QString extenderItemId = dg.name();
741  QString extenderItemName = dg.readEntry("extenderItemName", "");
742  QString appletName = dg.readEntry("sourceAppletPluginName", "");
743  uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
744 
745  bool temporarySourceApplet = false;
746 
747  kDebug() << "applet id = " << applet.data()->id();
748  kDebug() << "sourceappletid = " << sourceAppletId;
749 
750  //find the source applet.
751  Applet *sourceApplet = 0;
752  if (applet.data()->id() == sourceAppletId) {
753  // it's ours!
754  sourceApplet = applet.data();
755  } else {
756  // maybe it's foreign?
757  Containment *containment = applet.data()->containment();
758 
759  if (containment) {
760  Corona *corona = containment->corona();
761 
762  if (sourceAppletId == q->applet()->id()) {
763  sourceApplet = q->applet();
764  } else {
765  foreach (Containment *containment, corona->containments()) {
766  foreach (Applet *applet, containment->applets()) {
767  if (applet->id() == sourceAppletId) {
768  sourceApplet = applet;
769  }
770  }
771  }
772  }
773  }
774  }
775 
776  //There is no source applet. We just instantiate one just for the sake of creating
777  //detachables.
778  if (!sourceApplet) {
779  kDebug() << "creating a temporary applet as factory";
780  sourceApplet = Applet::load(appletName);
781  temporarySourceApplet = true;
782  //TODO: maybe add an option to applet to indicate that it shouldn't be deleted after
783  //having used it as factory.
784  }
785 
786  if (!sourceApplet) {
787  kDebug() << "sourceApplet is null? appletName = " << appletName;
788  kDebug() << " extenderItemId = " << extenderItemId;
789  } else {
790  ExtenderItem *item;
791  if (dg.readEntry("isGroup", false)) {
792  item = new ExtenderGroup(q, extenderItemId.toInt());
793  } else {
794  item = new ExtenderItem(q, extenderItemId.toInt());
795  }
796  sourceApplet->initExtenderItem(item);
797  item->d->sourceApplet = sourceApplet;
798 
799  if (temporarySourceApplet) {
800  delete sourceApplet;
801  }
802  }
803  }
804 }
805 
806 void ExtenderPrivate::updateBorders()
807 {
808  foreach (ExtenderItem *item, attachedExtenderItems) {
809  if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
810  //call themeChanged to change the backgrounds enabled borders, and move all contained
811  //widgets according to it's changed margins.
812  item->d->themeChanged();
813  }
814  }
815 }
816 
817 void ExtenderPrivate::delayItemAddedEvent()
818 {
819  QHash<Plasma::ExtenderItem *, QPointF>::const_iterator i = pendingItems.constBegin();
820  while (i != pendingItems.constEnd()) {
821  q->itemAddedEvent(i.key(), i.value());
822  ++i;
823  }
824  pendingItems.clear();
825 }
826 
827 void ExtenderPrivate::updateEmptyExtenderLabel()
828 {
829  if (q->isEmpty() && !emptyExtenderLabel &&
830  !emptyExtenderMessage.isEmpty() && !spacerWidget ) {
831  //add the empty extender label.
832  emptyExtenderLabel = new Label(q);
833  emptyExtenderLabel->setAlignment(Qt::AlignCenter);
834  emptyExtenderLabel->setText(emptyExtenderMessage);
835 
836  qreal left, top, right, bottom;
837  background->getMargins(left, top, right, bottom);
838  emptyExtenderLabel->nativeWidget()->setMargin(left + right);
839 
840  emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
841  layout->addItem(emptyExtenderLabel);
842  } else if (!q->isEmpty() && emptyExtenderLabel) {
843  //remove the empty extender label.
844  layout->removeItem(emptyExtenderLabel);
845  delete emptyExtenderLabel;
846  emptyExtenderLabel = 0;
847  }
848 }
849 
850 void ExtenderPrivate::adjustMinimumSize()
851 {
852  //FIXME: hardcoded number for the scrollbar
853  scrollWidget->setMinimumWidth(mainWidget->effectiveSizeHint(Qt::MinimumSize).width() + 32);
854  //FIXME: hardcoded number
855  scrollWidget->setMinimumHeight(qMin((qreal)300, mainWidget->effectiveSizeHint(Qt::MinimumSize).height()));
856 }
857 
858 void ExtenderPrivate::setPositionFromDragPosition(const QPointF &pos)
859 {
860  const qreal ratio = (q->mapFromScene(pos).y()/scrollWidget->size().height());
861 
862  mainWidget->setPos(mainWidget->pos().x(), 30 + (ratio *(scrollWidget->size().height() - mainWidget->size().height() - 30)));
863 }
864 
865 ExtenderGroup *ExtenderPrivate::findGroup(const QString &name) const
866 {
867  foreach (ExtenderItem *item, attachedExtenderItems) {
868  if (item->isGroup() && item->name() == name) {
869  return qobject_cast<ExtenderGroup*>(item);
870  }
871  }
872 
873  return 0;
874 }
875 
876 void ExtenderPrivate::extenderItemDestroyed(Plasma::ExtenderItem *item)
877 {
878  pendingItems.remove(item);
879 
880  if (attachedExtenderItems.contains(item)) {
881  // removeExtenderItem also removes the item from attachedExtenderItems
882  removeExtenderItem(item);
883  }
884 }
885 
886 void ExtenderPrivate::viewportGeometryChanged(const QRectF &rect)
887 {
888  if (appearance != Extender::TopDownStacked && appearance != Extender::BottomUpStacked) {
889  scrollbarVisible = (rect.height() > mainWidget->boundingRect().height());
890  return;
891  }
892 
893  bool scroll = !(rect.height() >= mainWidget->boundingRect().height());
894 
895  if (scroll != scrollbarVisible) {
896  scrollbarVisible = scroll;
897  updateBorders();
898  }
899 }
900 
901 void ExtenderPrivate::setDisabledBordersHint(const FrameSvg::EnabledBorders borders)
902 {
903  if (disabledBordersHint == borders) {
904  return;
905  }
906 
907  disabledBordersHint = borders;
908  foreach (Plasma::ExtenderItem *item, attachedExtenderItems) {
909  item->d->themeChanged();
910  }
911 }
912 
913 void ExtenderPrivate::adjustSize()
914 {
915  QRect screenRect;
916  QSizeF size = mainWidget->effectiveSizeHint(Qt::PreferredSize);
917  if (applet) {
918  Containment *containment = applet.data()->containment();
919  if (containment && containment->corona()) {
920  screenRect = containment->corona()->screenGeometry(containment->screen());
921  }
922  }
923  q->resize(qMin(screenRect.width()/3, (int)size.width()),
924  qMin(screenRect.height()/3, (int)size.height()));
925 }
926 
927 bool Extender::isEmpty() const
928 {
929  //It's empty if it doesn't have items or has only group that are empty and autohide
930  foreach (ExtenderItem *item, d->attachedExtenderItems) {
931  if (!item->isGroup()) {
932  return false;
933  } else {
934  //a static_cast here should be safe, it's not the case apparently
935  ExtenderGroup *group = qobject_cast<ExtenderGroup *>(item);
936  if (group && (!group->autoHide() || group->items().size() > 0)) {
937  return false;
938  }
939  }
940  }
941 
942  return true;
943 }
944 
945 } // Plasma namespace
946 
947 #include "extender.moc"
Plasma::SizeConstraint
the size of the applet was changed
Definition: plasma.h:49
Plasma::Extender::Appearance
Appearance
Description on how to render the extender's items.
Definition: extender.h:79
Plasma::Extender::isEmpty
bool isEmpty() const
Definition: extender.cpp:927
Plasma::Vertical
The applet is constrained horizontally, but can expand vertically.
Definition: plasma.h:77
Plasma::Corona::screenGeometry
virtual QRect screenGeometry(int id) const
Returns the geometry of a given screen.
Definition: corona.cpp:441
Plasma::Extender
Extends applets to allow detachable parts.
Definition: extender.h:65
Plasma::ExtenderGroup::items
QList< ExtenderItem * > items() const
Definition: extendergroup.cpp:109
Plasma::ExtenderItem::config
KConfigGroup config() const
fetch the configuration of this widget.
Definition: extenderitem.cpp:240
Plasma::Corona::containments
QList< Containment * > containments() const
Definition: corona.cpp:328
extender.h
QWidget
Plasma::Containment::applets
Applet::List applets() const
Definition: containment.cpp:950
theme.h
Plasma::PopupApplet::hidePopup
void hidePopup()
Hides the popup.
Definition: popupapplet.cpp:635
containment.h
Plasma::FrameSvg
Provides an SVG with borders.
Definition: framesvg.h:76
Plasma::Horizontal
The applet is constrained vertically, but can expand horizontally.
Definition: plasma.h:75
Plasma::Containment::corona
Corona * corona() const
Returns the Corona (if any) that this Containment is hosted by.
Definition: containment.cpp:525
Plasma::ExtenderItem::isGroup
bool isGroup() const
Definition: extenderitem.cpp:488
Plasma::Applet
The base Applet class.
Definition: applet.h:77
Plasma::Extender::TopDownStacked
Draws no borders on the bottom extenderitem, but draws the left, bottom and right border on subsequen...
Definition: extender.h:89
Plasma::Corona::config
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
Definition: corona.cpp:340
Plasma::Applet::id
uint id
Definition: applet.h:91
Plasma::PaintUtils::roundedRectangle
QPainterPath roundedRectangle(const QRectF &rect, qreal radius)
Returns a nicely rounded rectanglular path for painting.
Definition: paintutils.cpp:159
Plasma::Extender::group
Q_INVOKABLE ExtenderGroup * group(const QString &name) const
Extra convenience function for obtaining groups specified by name.
Definition: extender.cpp:293
Plasma::PopupApplet
Allows applets to automatically 'collapse' into an icon when put in an panel, and is a convenient bas...
Definition: popupapplet.h:52
Plasma::Extender::BottomUpStacked
Draws no borders on the topmost extenderitem, but draws the left, top and right border on subsequent ...
Definition: extender.h:83
paintutils.h
Plasma::ExtenderItem::group
ExtenderGroup * group() const
Definition: extenderitem.cpp:483
applet.h
Plasma::Extender::item
Q_INVOKABLE ExtenderItem * item(const QString &name) const
This function can be used for obtaining the extender item specified by name.
Definition: extender.cpp:241
Plasma::Applet::containment
Containment * containment() const
Definition: applet.cpp:1518
Plasma::PopupApplet::showPopup
void showPopup(uint displayTime=0)
Shows the dialog showing the widget if the applet is in a panel.
Definition: popupapplet.cpp:589
scrollwidget.h
Plasma::ExtenderItem::name
QString name
Definition: extenderitem.h:85
Plasma::Applet::config
KConfigGroup config() const
Returns the KConfigGroup to access the applets configuration.
Definition: applet.cpp:450
dialog.h
corona.h
framesvg.h
Plasma::ExtenderGroup
Allows for grouping of extender items.
Definition: extendergroup.h:50
label.h
Plasma::ScrollWidget
A container of widgets that can have scrollbars.
Definition: scrollwidget.h:43
Plasma::ExtenderGroup::autoHide
bool autoHide
Definition: extendergroup.h:53
Plasma::Containment
The base class for plugins that provide backgrounds and applet grouping containers.
Definition: containment.h:72
extendergroup.h
popupapplet.h
Plasma::ExtenderItem
Provides detachable items for an Extender.
Definition: extenderitem.h:80
QStyleOptionGraphicsItem
extenderitem.h
svg.h
Plasma::Corona
A QGraphicsScene for Plasma::Applets.
Definition: corona.h:48
QGraphicsWidget
Plasma::Applet::load
static Applet * load(const QString &name, uint appletId=0, const QVariantList &args=QVariantList())
Attempts to load an applet.
Definition: applet.cpp:2422
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