23 #include <QApplication>
26 #include <QGraphicsSceneResizeEvent>
27 #include <QGraphicsSceneMouseEvent>
28 #include <QGraphicsLinearLayout>
36 #include <kiconloader.h>
37 #include <ksharedconfig.h>
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"
69 m_background(new FrameSvg(this))
71 m_background->setImagePath(
"widgets/extender-dragger");
72 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
83 m_background->paintFrame(painter, option->exposedRect, option->exposedRect);
90 m_background->resize();
91 QSizeF size = m_background->elementSize(
"hint-preferred-icon-size");
92 size = size.expandedTo(QSizeF(KIconLoader::SizeSmall,KIconLoader::SizeSmall));
96 QFontMetrics fm(font);
97 m_iconSize = qMax(size.height(), (qreal) fm.height());
100 void setBackgroundPrefix(
const QString &
string)
102 if (
string.isEmpty() || m_background->hasElementPrefix(
string)) {
103 m_background->setElementPrefix(
string);
108 const QString backgroundPrefix()
const
110 return m_background->prefix();
114 void resizeEvent(QGraphicsSceneResizeEvent *)
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);
123 FrameSvg *m_background;
130 d(new ExtenderItemPrivate(this, hostExtender))
132 Q_ASSERT(hostExtender);
133 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
136 if (extenderItemId) {
137 d->extenderItemId = extenderItemId;
138 ExtenderItemPrivate::s_maxExtenderItemId =
139 qMax(ExtenderItemPrivate::s_maxExtenderItemId, extenderItemId);
141 d->extenderItemId = ++ExtenderItemPrivate::s_maxExtenderItemId;
145 d->toolbox =
new ExtenderItemToolbox(
this);
146 d->toolboxLayout =
new QGraphicsLinearLayout(d->toolbox);
149 KConfigGroup cg = hostExtender->d->applet.data()->config(
"ExtenderItems");
150 KConfigGroup dg = KConfigGroup(&cg, QString::number(d->extenderItemId));
153 d->layout =
new QGraphicsLinearLayout(
Qt::Vertical,
this);
154 d->layout->addItem(d->toolbox);
156 uint sourceAppletId = dg.readEntry(
"sourceAppletId", 0);
160 d->collapseIcon->setCursor(Qt::ArrowCursor);
161 d->titleLabel =
new Label(d->toolbox);
162 d->titleLabel->setWordWrap(
false);
163 d->titleLabel->setAlignment(Qt::AlignCenter);
165 d->toolboxLayout->addItem(d->collapseIcon);
166 d->toolboxLayout->addItem(d->titleLabel);
167 d->toolboxLayout->setStretchFactor(d->titleLabel, 10);
169 if (!sourceAppletId) {
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()));
178 d->name = dg.readEntry(
"extenderItemName",
"");
179 d->titleLabel->setText(dg.readEntry(
"extenderTitle",
""));
182 QString iconName = dg.readEntry(
"extenderIconName",
"utilities-desktop-extra");
183 if (iconName.isEmpty()) {
184 iconName =
"utilities-desktop-extra";
186 d->collapseIcon->setIcon(iconName);
189 QString groupName = dg.readEntry(
"group",
"");
190 d->group = hostExtender->d->findGroup(groupName);
194 if (hostExtender && hostExtender->d->applet && hostExtender->d->applet.data()->containment()) {
195 corona = hostExtender->d->applet.data()->containment()->corona();
197 if (sourceAppletId == hostExtender->
applet()->
id()) {
198 d->sourceApplet = hostExtender->
applet();
202 if (applet->
id() == sourceAppletId &&
203 applet->
pluginName() == dg.readEntry(
"sourceAppletPluginName",
"")) {
204 d->sourceApplet = applet;
213 if (d->sourceApplet) {
214 connect(d->sourceApplet, SIGNAL(
destroyed()),
this, SLOT(sourceAppletRemoved()));
217 connect(d->collapseIcon, SIGNAL(clicked()),
this, SLOT(toggleCollapse()));
228 setAcceptsHoverEvents(
true);
231 d->setMovable(d->extender->d->applet.data()->immutability() ==
Plasma::Mutable);
242 if (!d->extender->d->applet) {
243 return KConfigGroup();
246 KConfigGroup cg = d->extender->d->applet.data()->config(
"ExtenderItems");
247 KConfigGroup itemCg = KConfigGroup(&cg, QString::number(d->extenderItemId));
254 if (!d->transientConfig) {
255 d->transientConfig = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
256 KConfigGroup dummyGroup = KConfigGroup(d->transientConfig,
"ExtenderItems");
257 itemCg.reparent(&dummyGroup);
260 KConfigGroup dummyGroup = KConfigGroup(d->transientConfig,
"ExtenderItems");
261 dummyGroup = KConfigGroup(&dummyGroup, QString::number(d->extenderItemId));
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();
279 if (d->titleLabel->text() !=
title) {
280 d->titleLabel->setText(title);
281 config().writeEntry(
"extenderTitle", title);
288 return d->titleLabel->text();
294 config().writeEntry(
"extenderItemName", name);
304 if (d->widget.data()) {
305 d->widget.data()->removeSceneEventFilter(
this);
306 d->layout->removeItem(d->widget.data());
307 d->widget.data()->deleteLater();
310 if (!widget || !widget->isWidget()) {
314 widget->setParentItem(
this);
316 d->layout->insertItem(1, d->widget.data());
317 d->widget.data()->setVisible(!d->collapsed);
322 return d->widget.data();
327 if (d->collapseIcon->icon().isNull() || icon.cacheKey() != d->collapseIcon->icon().cacheKey()) {
329 d->collapseIcon->setIcon(icon);
330 d->collapseIcon->setVisible(!icon.isNull());
336 if (icon != d->iconName) {
337 d->collapseIcon->setIcon(icon);
339 config().writeEntry(
"extenderIconName", icon);
345 return d->collapseIcon->icon();
354 d->dragStarted =
false;
357 QList<ExtenderItem*> childItems;
359 childItems = group->
items();
362 if (extender == d->extender) {
364 setParentItem(extender);
365 extender->d->addExtenderItem(
this, pos);
371 d->extender->d->removeExtenderItem(
this);
374 if (!d->transient && d->hostApplet() && (extender != d->extender)) {
375 KConfigGroup c = extender->d->applet.data()->config(
"ExtenderItems");
380 emit d->extender->itemDetached(
this);
382 setParentItem(extender);
385 disconnect(d->extender->applet(), SIGNAL(immutabilityChanged(
Plasma::ImmutabilityType)),
this, SLOT(updateToolBox()));
391 extender->d->addExtenderItem(
this, pos);
395 d->expirationTimer->stop();
396 delete d->expirationTimer;
397 d->expirationTimer = 0;
406 KConfigGroup extenderItemGroup(corona->
config(),
"DetachedExtenderItems");
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));
451 kWarning() <<
"Nesting ExtenderGroups is not supported";
459 d->toolbox->setBackgroundPrefix(
"grouped");
463 kDebug() <<
"moving to another extender because we're joining a detached group.";
466 group->d->addItemToGroup(
this, pos);
469 d->toolbox->setBackgroundPrefix(
"root");
471 d->toolbox->setBackgroundPrefix(QString());
473 d->toolbox->setBackgroundPrefix(QString());
475 oldGroup->d->removeItemFromGroup(
this);
477 config().deleteEntry(
"group");
479 d->dragStarted =
false;
490 return (
config().readEntry(
"isGroup",
false) && qobject_cast<const Plasma::ExtenderGroup *>(
this));
501 if (d->expirationTimer) {
502 d->expirationTimer->stop();
503 delete d->expirationTimer;
504 d->expirationTimer = 0;
510 if (!d->expirationTimer) {
511 d->expirationTimer =
new QTimer(
this);
512 connect(d->expirationTimer, SIGNAL(timeout()),
this, SLOT(
destroy()));
515 d->expirationTimer->stop();
516 d->expirationTimer->setSingleShot(
true);
517 d->expirationTimer->setInterval(time);
518 d->expirationTimer->start();
524 if (d->expirationTimer) {
525 return d->expirationTimer->interval();
533 if (d->hostApplet()) {
534 return (d->sourceApplet != d->hostApplet());
543 if (d->actionsInOrder.contains(action)) {
547 d->actions.insert(name, action);
548 d->actionsInOrder.append(action);
549 connect(action, SIGNAL(changed()),
this, SLOT(updateToolBox()));
556 return d->actions.value(name, 0);
561 if (d->destroyActionVisibility) {
565 d->destroyActionVisibility =
true;
571 if (!d->destroyActionVisibility) {
575 d->destroyActionVisibility =
false;
581 if (d->dragStarted) {
589 KConfigGroup extenderItemGroup(corona->
config(),
"DetachedExtenderItems");
590 if (extenderItemGroup.hasGroup(QString::number(d->extenderItemId))) {
591 extenderItemGroup.deleteGroup(QString::number(d->extenderItemId));
595 d->hostApplet()->config(
"ExtenderItems").deleteGroup(QString::number(d->extenderItemId));
596 d->extender->d->removeExtenderItem(
this);
597 emit d->extender->itemDetached(
this);
608 config().writeEntry(
"isCollapsed", 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);
614 d->layout->removeItem(d->widget.data());
616 d->layout->insertItem(1, d->widget.data());
622 static_cast<QGraphicsLayoutItem *
>(
extender()->d->mainWidget)->updateGeometry();
624 group()->layout()->invalidate();
625 static_cast<QGraphicsLayoutItem *
>(
group())->updateGeometry();
635 if (!d || !d->sourceApplet) {
639 if (d->sourceApplet->d) {
647 d->background->enabledBorders()) {
650 d->background->paintFrame(painter, option->exposedRect, option->exposedRect);
664 d->background->resizeFrame(size());
670 if (!(d->dragHandleRect().contains(event->pos())) ||
679 QPoint mousePressPos =
event->buttonDownPos(Qt::LeftButton).toPoint();
680 if (!(event->buttons() & Qt::LeftButton) ||
681 (event->pos().toPoint() - mousePressPos).manhattanLength()
682 < QApplication::startDragDistance()) {
686 if (!d->extender->d->applet) {
691 d->dragStarted =
true;
692 QPointF curPos = pos();
696 d->extender->itemRemovedEvent(
this);
706 QSize screenSize(view.mapFromScene(sceneBoundingRect()).boundingRect().size());
707 QPixmap pixmap(screenSize);
708 pixmap.fill(Qt::transparent);
713 view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
714 view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
715 view.setFrameShape(QFrame::NoFrame);
718 view.resize(screenSize);
719 view.setSceneRect(sceneBoundingRect());
720 view.render(&p, QRectF(QPointF(0, 0), pixmap.size()), QRect(QPoint(0, 0), screenSize));
723 ExtenderItemMimeData *mimeData =
new ExtenderItemMimeData();
724 mimeData->setExtenderItem(
this);
725 mimeData->setPointerOffset(mousePressPos);
729 ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->extender->d->applet.data());
730 if (extenderApplet && d->extender->attachedItems().count() < 2 &&
733 kDebug() <<
"leaving the internal extender container, so hide the applet and it's handle.";
734 extenderApplet->hide();
738 bool collapsedGroup =
false;
740 collapsedGroup = group->d->collapsed;
750 QDrag *drag =
new QDrag(dragParent);
751 drag->setPixmap(pixmap);
752 drag->setMimeData(mimeData);
753 drag->setHotSpot(mousePressPos);
755 Qt::DropAction
action = drag->exec();
758 d->dragStarted =
false;
760 if (!action || !drag->target()) {
764 d->extender->itemAddedEvent(
this, curPos);
765 if (extenderApplet) {
766 extenderApplet->show();
770 if (
isGroup() && !collapsedGroup) {
777 if (d->dragHandleRect().contains(event->pos())) {
803 return QGraphicsWidget::sizeHint(which, constraint);
808 d->transient =
transient;
819 extender(hostExtender),
822 background(new
FrameSvg(extenderItem)),
826 destroyActionVisibility(false),
832 ExtenderItemPrivate::~ExtenderItemPrivate()
834 delete widget.data();
838 QRectF ExtenderItemPrivate::dragHandleRect()
840 return toolbox->boundingRect();
843 void ExtenderItemPrivate::toggleCollapse()
845 q->setCollapsed(!q->isCollapsed());
848 void ExtenderItemPrivate::updateToolBox()
851 Q_ASSERT(toolboxLayout);
854 QAction *closeAction = actions.value(
"close");
855 QAction *returnToSourceAction = actions.value(
"extenderItemReturnToSource");
856 bool returnToSourceVisibility = q->isDetached() && sourceApplet && (hostApplet()->immutability() ==
Plasma::Mutable);
858 int returnToSourceIndex = -1;
859 const int startingIndex = 2;
861 const QSizeF widgetSize = collapseIcon->sizeFromIconSize(toolbox->iconSize());
863 QSet<QAction*> shownActions = actionsInOrder.toSet();
865 QHash<QAction *, QGraphicsWidget *> actionWidgets;
866 for (
int index = startingIndex; index < toolboxLayout->count(); ++index) {
868 QAction *widgetAction = 0;
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();
881 if (closeIndex == -1 && destroyActionVisibility &&
882 closeAction && widgetAction == closeAction) {
887 if (returnToSourceIndex == -1 && returnToSourceVisibility &&
888 returnToSourceAction && widgetAction == returnToSourceAction) {
889 returnToSourceIndex = index;
893 if (shownActions.contains(widgetAction)) {
894 actionWidgets.insert(widgetAction, widget);
898 toolboxLayout->removeAt(index);
899 widget->deleteLater();
904 collapseIcon->setMinimumSize(widgetSize);
905 collapseIcon->setMaximumSize(widgetSize);
908 foreach (QAction *action, actionsInOrder) {
909 if (action->isVisible() && action != closeAction) {
911 PushButton *button = qobject_cast<PushButton*>(actionWidgets.value(action));
913 if (action->icon().isNull() && !action->text().isNull()) {
915 button =
new PushButton(q);
916 button->setAction(action);
919 button->setMinimumHeight(widgetSize.height());
920 button->setMaximumHeight(widgetSize.height());
921 button->setCursor(Qt::ArrowCursor);
922 toolboxLayout->insertItem(startingIndex, button);
927 icon->setAction(action);
930 if (action->icon().isNull()) {
931 icon->setText(action->text());
933 icon->setMinimumSize(widgetSize);
934 icon->setMaximumSize(widgetSize);
935 icon->setCursor(Qt::ArrowCursor);
936 toolboxLayout->insertItem(startingIndex, icon);
943 if (returnToSourceVisibility && returnToSourceIndex == -1) {
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()));
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);
958 if (closeIndex == -1) {
959 toolboxLayout->addItem(returnToSourceIcon);
961 toolboxLayout->insertItem(closeIndex - 1, returnToSourceIcon);
967 if (destroyActionVisibility && closeIndex == -1) {
970 closeAction =
new QAction(q);
971 actions.insert(
"close", closeAction);
972 if (returnToSourceAction) {
973 returnToSourceAction->setToolTip(i18n(
"Close"));
975 QObject::connect(closeAction, SIGNAL(triggered()), q, SLOT(destroy()));
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);
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);
993 toolboxLayout->setContentsMargins(0, 0, KIconLoader::SizeSmall, 0);
996 toolboxLayout->setContentsMargins(0, 0, 0, 0);
1000 Applet *ExtenderItemPrivate::hostApplet()
const
1003 return extender->d->applet.data();
1009 void ExtenderItemPrivate::themeChanged()
1013 background->setImagePath(
"opaque/dialogs/background");
1016 background->setImagePath(
"widgets/extender-background");
1017 background->setEnabledBorders(extender->enabledBordersForItem(q));
1020 qreal left, top, right, bottom;
1021 background->getMargins(left, top, right, bottom);
1022 layout->setContentsMargins(left, top, right, bottom);
1025 toolbox->setBackgroundPrefix(
"grouped");
1027 if (extender->items().count() <= 1 || extender->items().first() == q || extender->appearance() !=
Extender::NoBorders) {
1028 toolbox->setBackgroundPrefix(
"root");
1030 toolbox->setBackgroundPrefix(QString());
1034 toolbox->updateTheme();
1037 void ExtenderItemPrivate::sourceAppletRemoved()
1045 void ExtenderItemPrivate::actionDestroyed(
QObject *o)
1047 QAction *action =
static_cast<QAction *
>(o);
1048 QMutableHashIterator<QString, QAction *> hit(actions);
1049 while (hit.hasNext()) {
1050 if (hit.next().value() == action) {
1056 QMutableListIterator<QAction *> lit(actionsInOrder);
1057 while (lit.hasNext()) {
1058 if (lit.next() == action) {
1065 void ExtenderItemPrivate::setMovable(
bool movable)
1068 titleLabel->setCursor(Qt::OpenHandCursor);
1069 toolbox->setCursor(Qt::OpenHandCursor);
1071 titleLabel->unsetCursor();
1072 toolbox->unsetCursor();
1076 uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
1080 #include "extenderitem.moc"