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

Plasma

  • 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
61namespace Plasma
62{
63
64class ExtenderItemToolbox : public QGraphicsWidget
65{
66public:
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
113protected:
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
122private:
123 FrameSvg *m_background;
124 QString m_prefix;
125 qreal m_iconSize;
126};
127
128ExtenderItem::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
234ExtenderItem::~ExtenderItem()
235{
236 emit destroyed(this);
237 delete d;
238}
239
240KConfigGroup 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
277void 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
286QString ExtenderItem::title() const
287{
288 return d->titleLabel->text();
289}
290
291void ExtenderItem::setName(const QString &name)
292{
293 d->name = name;
294 config().writeEntry("extenderItemName", name);
295}
296
297QString ExtenderItem::name() const
298{
299 return d->name;
300}
301
302void 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
320QGraphicsItem *ExtenderItem::widget() const
321{
322 return d->widget.data();
323}
324
325void 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
334void 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
343QIcon ExtenderItem::icon() const
344{
345 return d->collapseIcon->icon();
346}
347
348void 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
436Extender *ExtenderItem::extender() const
437{
438 return d->extender;
439}
440
441//TODO KDE5: only one setGroup()
442void ExtenderItem::setGroup(ExtenderGroup *group)
443{
444 setGroup(group, QPointF(-1, -1));
445}
446
447void 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
483ExtenderGroup *ExtenderItem::group() const
484{
485 return d->group;
486}
487
488bool ExtenderItem::isGroup() const
489{
490 return (config().readEntry("isGroup", false) && qobject_cast<const Plasma::ExtenderGroup *>(this));
491}
492
493bool ExtenderItem::isCollapsed() const
494{
495 return d->collapsed;
496}
497
498void 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
522uint ExtenderItem::autoExpireDelay() const
523{
524 if (d->expirationTimer) {
525 return d->expirationTimer->interval();
526 } else {
527 return 0;
528 }
529}
530
531bool ExtenderItem::isDetached() const
532{
533 if (d->hostApplet()) {
534 return (d->sourceApplet != d->hostApplet());
535 } else {
536 return false;
537 }
538}
539
540void 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
554QAction *ExtenderItem::action(const QString &name) const
555{
556 return d->actions.value(name, 0);
557}
558
559void ExtenderItem::showCloseButton()
560{
561 if (d->destroyActionVisibility) {
562 return;
563 }
564
565 d->destroyActionVisibility = true;
566 d->updateToolBox();
567}
568
569void ExtenderItem::hideCloseButton()
570{
571 if (!d->destroyActionVisibility) {
572 return;
573 }
574
575 d->destroyActionVisibility = false;
576 d->updateToolBox();
577}
578
579void 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
602void 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
633void 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
644void 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
654void ExtenderItem::moveEvent(QGraphicsSceneMoveEvent *event)
655{
656 Q_UNUSED(event)
657 //not needed anymore, but here for binary compatibility
658}
659
660void 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
668void 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
677void 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
775void ExtenderItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
776{
777 if (d->dragHandleRect().contains(event->pos())) {
778 d->toggleCollapse();
779 }
780}
781
782bool ExtenderItem::sceneEventFilter(QGraphicsItem *, QEvent *)
783{
784 return false;
785}
786
787void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
788{
789 Q_UNUSED(event)
790 //not needed anymore, but here for binary compatibility
791}
792
793void ExtenderItem::hoverMoveEvent(QGraphicsSceneHoverEvent *)
794{
795}
796
797void ExtenderItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
798{
799}
800
801QSizeF ExtenderItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
802{
803 return QGraphicsWidget::sizeHint(which, constraint);
804}
805
806void ExtenderItem::setTransient(const bool transient)
807{
808 d->transient = transient;
809}
810
811bool ExtenderItem::isTransient() const
812{
813 return d->transient;
814}
815
816ExtenderItemPrivate::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
832ExtenderItemPrivate::~ExtenderItemPrivate()
833{
834 delete widget.data();
835}
836
837//returns a Rect containing the area of the detachable where the draghandle will be drawn.
838QRectF ExtenderItemPrivate::dragHandleRect()
839{
840 return toolbox->boundingRect();
841}
842
843void ExtenderItemPrivate::toggleCollapse()
844{
845 q->setCollapsed(!q->isCollapsed());
846}
847
848void 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
1000Applet *ExtenderItemPrivate::hostApplet() const
1001{
1002 if (extender) {
1003 return extender->d->applet.data();
1004 } else {
1005 return 0;
1006 }
1007}
1008
1009void 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
1037void 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
1045void 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
1065void 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
1076uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
1077
1078} // namespace Plasma
1079
1080#include "extenderitem.moc"
applet.h
IconWidget
Provides a generic icon.
Plasma::Applet
The base Applet class.
Definition: applet.h:78
Plasma::Applet::id
uint id
Definition: applet.h:91
Plasma::Applet::pluginName
QString pluginName
Definition: applet.h:82
Plasma::Containment
The base class for plugins that provide backgrounds and applet grouping containers.
Definition: containment.h:73
Plasma::Containment::applets
Applet::List applets() const
Definition: containment.cpp:950
Plasma::Corona
A QGraphicsScene for Plasma::Applets.
Definition: corona.h:49
Plasma::Corona::removeOffscreenWidget
void removeOffscreenWidget(QGraphicsWidget *widget)
Removes a widget from the topleft quadrant in the scene.
Definition: corona.cpp:414
Plasma::Corona::containments
QList< Containment * > containments() const
Definition: corona.cpp:328
Plasma::Corona::config
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
Definition: corona.cpp:340
Plasma::Corona::addOffscreenWidget
void addOffscreenWidget(QGraphicsWidget *widget)
Adds a widget in the topleft quadrant in the scene.
Definition: corona.cpp:377
Plasma::ExtenderGroup
Allows for grouping of extender items.
Definition: extendergroup.h:51
Plasma::ExtenderGroup::items
QList< ExtenderItem * > items() const
Definition: extendergroup.cpp:109
Plasma::ExtenderGroup::expandGroup
void expandGroup()
Expands this group to show all ExtenderItems that are contained in this group.
Definition: extendergroup.cpp:162
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::ExtenderItem
Provides detachable items for an Extender.
Definition: extenderitem.h:81
Plasma::ExtenderItem::widget
QGraphicsItem * widget
Definition: extenderitem.h:83
Plasma::ExtenderItem::isDetached
bool isDetached() const
Definition: extenderitem.cpp:531
Plasma::ExtenderItem::collapsed
bool collapsed
Definition: extenderitem.h:88
Plasma::ExtenderItem::title
QString title
Definition: extenderitem.h:84
Plasma::ExtenderItem::name
QString name
Definition: extenderitem.h:85
Plasma::ExtenderItem::config
KConfigGroup config() const
fetch the configuration of this widget.
Definition: extenderitem.cpp:240
Plasma::ExtenderItem::action
QAction * action(const QString &name) const
Definition: extenderitem.cpp:554
Plasma::ExtenderItem::icon
QIcon icon
Definition: extenderitem.h:86
Plasma::ExtenderItem::isGroup
bool isGroup() const
Definition: extenderitem.cpp:488
Plasma::ExtenderItem::group
ExtenderGroup * group() const
Definition: extenderitem.cpp:483
Plasma::ExtenderItem::destroyed
void destroyed(Plasma::ExtenderItem *item)
Emitted when the extender item is destroyed.
Plasma::ExtenderItem::destroy
void destroy()
Destroys the extender item.
Definition: extenderitem.cpp:579
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::ExtenderItem::extender
Extender * extender
Definition: extenderitem.h:87
Plasma::ExtenderItem::setExtender
void setExtender(Extender *extender, const QPointF &pos=QPointF(-1, -1))
Definition: extenderitem.cpp:348
Plasma::Extender
Extends applets to allow detachable parts.
Definition: extender.h:66
Plasma::Extender::applet
Applet * applet() const
Definition: extender.cpp:358
Plasma::Extender::NoBorders
@ NoBorders
Draws no borders on the extender's items.
Definition: extender.h:80
Plasma::FrameSvg
Provides an SVG with borders.
Definition: framesvg.h:77
Plasma::FrameSvg::LeftBorder
@ LeftBorder
Definition: framesvg.h:91
Plasma::FrameSvg::RightBorder
@ RightBorder
Definition: framesvg.h:92
Plasma::IconWidget
Definition: iconwidget.h:57
Plasma::Label
Provides a plasma-themed QLabel.
Definition: label.h:41
Plasma::Theme
Interface to the Plasma theme.
Definition: theme.h:57
Plasma::Theme::font
Q_INVOKABLE QFont font(FontRole role) const
Returns the font to be used by themed items.
Definition: theme.cpp:970
Plasma::Theme::DefaultFont
@ DefaultFont
The standard text font.
Definition: theme.h:80
Plasma::Theme::defaultTheme
static Theme * defaultTheme()
Singleton pattern accessor.
Definition: theme.cpp:544
QGraphicsView
QGraphicsWidget
QObject
QStyleOptionGraphicsItem
QWidget
containment.h
corona.h
dialog.h
extender.h
extendergroup.h
extenderitem.h
framesvg.h
iconwidget.h
label.h
Plasma
Namespace for everything in libplasma.
Definition: abstractdialogmanager.cpp:25
Plasma::ImmutabilityType
ImmutabilityType
Defines the immutability of items like applets, corona and containments they can be free to modify,...
Definition: plasma.h:197
Plasma::Mutable
@ Mutable
The item can be modified in any way.
Definition: plasma.h:198
Plasma::Horizontal
@ Horizontal
The applet is constrained vertically, but can expand horizontally.
Definition: plasma.h:75
Plasma::Vertical
@ Vertical
The applet is constrained horizontally, but can expand vertically.
Definition: plasma.h:77
popupapplet.h
pushbutton.h
theme.h
view.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 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-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 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
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
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