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"
void setTitle(const QString &title)
void returnToSource()
Returns the extender item to its source applet.
The applet is constrained horizontally, but can expand vertically.
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event)
ImmutabilityType
Defines the immutability of items like applets, corona and containments they can be free to modify...
void expandGroup()
Expands this group to show all ExtenderItems that are contained in this group.
Extends applets to allow detachable parts.
void setGroup(ExtenderGroup *group)
void setCollapsed(bool collapsed)
Collapse or expand the extender item.
Draws no borders on the extender's items.
QList< ExtenderItem * > items() const
KConfigGroup config() const
fetch the configuration of this widget.
void setName(const QString &name)
You can assign names to extender items to look them up through the item() function.
QList< Containment * > containments() const
void addAction(const QString &name, QAction *action)
void resizeEvent(QGraphicsSceneResizeEvent *event)
Q_INVOKABLE QFont font(FontRole role) const
Returns the font to be used by themed items.
Applet::List applets() const
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
Provides an SVG with borders.
void moveEvent(QGraphicsSceneMoveEvent *event)
QGraphicsItem * widget() const
The applet is constrained vertically, but can expand horizontally.
QAction * action(const QString &name) const
The item can be modified in any way.
void destroy()
Destroys the extender item.
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
void setExtender(Extender *extender, const QPointF &pos=QPointF(-1,-1))
void destroyed(Plasma::ExtenderItem *item)
Emitted when the extender item is destroyed.
Provides a plasma-themed QLabel.
ExtenderGroup * group() const
void hideCloseButton()
Hides the close button in this item's drag handle.
void showCloseButton()
Shows a close button in this item's drag handle.
Interface to the Plasma theme.
ExtenderItem(Extender *hostExtender, uint extenderItemId=0)
The constructor takes care of adding this item to an extender.
static Theme * defaultTheme()
Singleton pattern accessor.
void setTransient(const bool transient)
Set the ExtenderItem as transient: won't be saved in the Plasma config and won't be restored...
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
Allows for grouping of extender items.
void mousePressEvent(QGraphicsSceneMouseEvent *event)
void collapseGroup()
Collapses this group to hide all ExtenderItems that are contained in this group, and shows the summar...
The base class for plugins that provide backgrounds and applet grouping containers.
void setIcon(const QString &icon)
void addOffscreenWidget(QGraphicsWidget *widget)
Adds a widget in the topleft quadrant in the scene.
void removeOffscreenWidget(QGraphicsWidget *widget)
Removes a widget from the topleft quadrant in the scene.
Provides detachable items for an Extender.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
Extender * extender() const
void setWidget(QGraphicsItem *widget)
uint autoExpireDelay() const
void setAutoExpireDelay(uint time)
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
A QGraphicsScene for Plasma::Applets.
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
void hoverMoveEvent(QGraphicsSceneHoverEvent *event)