23#include <QDesktopWidget>
25#include <QGraphicsSceneDragDropEvent>
26#include <QGraphicsGridLayout>
27#include <QGraphicsLinearLayout>
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"
55Spacer::Spacer(QGraphicsItem *parent)
56 : QGraphicsWidget(parent)
64void Spacer::setMargins(qreal left, qreal top, qreal right, qreal bottom)
78 painter->setRenderHint(QPainter::Antialiasing);
80 contentsRect().adjusted(m_left, m_top, -m_right, -m_bottom), 4);
84 painter->fillPath(p, c);
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();
99 if (!popup->d->graphicsWidget) {
105 applet->d->extender =
this;
106 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
109 d->scrollWidget->setOverflowBordersVisible(
false);
110 d->scrollWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
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)));
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);
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);
128 d->loadExtenderItems();
130 setAcceptDrops(
true);
135 d->destroying =
true;
143 QList<QWeakPointer<ExtenderItem> > guardedItems;
146 guardedItems << QWeakPointer<ExtenderItem>(
item);
149 d->attachedExtenderItems.clear();
151 foreach (
const QWeakPointer<ExtenderItem> &guardedItem, guardedItems) {
154 item->disconnect(
this);
165 d->emptyExtenderMessage = message;
167 if (d->emptyExtenderLabel) {
168 d->emptyExtenderLabel->setText(message);
174 return d->emptyExtenderMessage;
179 QList<ExtenderItem*> result;
184 return QList<ExtenderItem*>();
194 if (
applet->d->extender) {
196 if (
item->d->sourceApplet == d->applet.data()) {
209 return d->attachedExtenderItems;
214 QList<ExtenderItem*> result;
219 return QList<ExtenderItem*>();
228 if (
applet->d->extender) {
230 if (
item->d->sourceApplet == d->applet.data() &&
item->isDetached()) {
245 if (
item->d->sourceApplet == d->applet.data() &&
item->name() == name) {
262 QList<Containment *> containments;
263 if (containment->
corona()) {
266 containments << containment;
271 if (
applet->d->extender) {
272 if (
applet->d->extender.data() ==
this) {
277 if (!
applet->d->extender) {
282 if (
item->d->sourceApplet == d->applet.data() &&
item->name() == name) {
295 return qobject_cast<ExtenderGroup*>(
item(name));
311 Corona *corona = qobject_cast<Corona *>(scene());
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()) {
341 return d->appearance;
346 QList<ExtenderGroup *> result;
348 if (
item->isGroup() && !result.contains(
item->group())) {
351 result.append(
group);
360 return d->applet.data();
366 item->config().writeEntry(
"extenderItemPosition",
item->geometry().y());
372 if (change == QGraphicsItem::ItemPositionHasChanged) {
376 return QGraphicsWidget::itemChange(change, value);
381 QGraphicsWidget::resizeEvent(event);
393 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
394 if (
isEmpty() && popupApplet) {
401 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
404 const ExtenderItemMimeData *mimeData =
405 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
410 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
420 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
421 const ExtenderItemMimeData *mimeData =
422 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
427 d->setPositionFromDragPosition(event->scenePos());
434 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
435 const ExtenderItemMimeData *mimeData =
436 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
442 Extender *sourceExtender = mimeData->extenderItem()->d->extender;
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.";
454 if (popupApplet && sourceExtender ==
this && (
attachedItems().count() < 2)) {
455 kDebug() <<
"leaving the extender, and there are no more attached items so hide the popup.";
461 ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->applet.data());
462 if (extenderApplet && sourceExtender ==
this &&
attachedItems().count() < 2 &&
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());
478 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
479 const ExtenderItemMimeData *mimeData =
480 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
483 mimeData->extenderItem()->setExtender(
this, event->pos());
484 QApplication::restoreOverrideCursor();
497 if (!
item->group()) {
498 if (pos == QPointF(-1, -1)) {
500 d->layout->removeItem(
item);
504 d->layout->insertItem(0,
item);
507 d->layout->addItem(
item);
510 kDebug() <<
"inserting at" << pos << d->insertIndexFromPos(pos) <<
item->size();
511 d->layout->insertItem(d->insertIndexFromPos(pos),
item);
512 kDebug() <<
item->size();
516 d->adjustMinimumSize();
519 d->updateEmptyExtenderLabel();
531 d->layout->removeItem(
item);
533 if (d->spacerWidget) {
534 d->layout->removeItem(d->spacerWidget);
535 delete d->spacerWidget;
539 d->adjustMinimumSize();
542 d->updateEmptyExtenderLabel();
545 d->layout->updateGeometry();
546 static_cast<QGraphicsLayoutItem *
>(d->scrollWidget)->updateGeometry();
559 if (d->spacerWidget && d->spacerWidget->geometry().contains(pos)) {
564 if (d->spacerWidget) {
565 d->layout->removeItem(d->spacerWidget);
568 int insertIndex = d->insertIndexFromPos(pos);
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);
577 widget->setMinimumSize(
item->minimumSize());
578 widget->setPreferredSize(
item->preferredSize());
579 widget->setMaximumSize(
item->maximumSize());
580 widget->setSizePolicy(
item->sizePolicy());
581 d->spacerWidget = widget;
583 d->layout->insertItem(insertIndex, d->spacerWidget);
586 d->updateEmptyExtenderLabel();
593 if (d->spacerWidget) {
595 d->layout->removeItem(d->spacerWidget);
596 delete d->spacerWidget;
599 d->currentSpacerIndex = -1;
601 d->updateEmptyExtenderLabel();
607 if (d->layout->count() < 1) {
628 if (d->scrollWidget->viewportGeometry().height() < d->mainWidget->boundingRect().height()) {
629 if (QApplication::layoutDirection() == Qt::RightToLeft) {
637 borders &= ~d->disabledBordersHint;
643ExtenderPrivate::ExtenderPrivate(
Applet *applet,
Extender *extender) :
647 disabledBordersHint(
FrameSvg::NoBorder),
648 currentSpacerIndex(-1),
650 emptyExtenderMessage(QString()),
651 emptyExtenderLabel(0),
654 scrollbarVisible(false)
656 background->setImagePath(
"widgets/extender-background");
659ExtenderPrivate::~ExtenderPrivate()
663void ExtenderPrivate::addExtenderItem(
ExtenderItem *item,
const QPointF &pos)
665 if (attachedExtenderItems.contains(item)) {
666 pendingItems.remove(item);
667 q->itemAddedEvent(item, pos);
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()));
678void ExtenderPrivate::removeExtenderItem(
ExtenderItem *item)
680 attachedExtenderItems.removeAll(item);
681 pendingItems.remove(item);
684 if (attachedExtenderItems.isEmpty()) {
685 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet.data());
687 popupApplet->hidePopup();
691 q->itemRemovedEvent(item);
694int ExtenderPrivate::insertIndexFromPos(
const QPointF &pos)
const
696 int insertIndex = -1;
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) {
706 }
else if (pos.y() <= siblingGeometry.bottom()) {
716void ExtenderPrivate::loadExtenderItems()
722 KConfigGroup cg = applet.data()->
config(
"ExtenderItems");
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));
734 for (
int i = 0; i < groupList.count(); i++) {
735 QPair<int, QString> pair = groupList[i];
737 KConfigGroup dg = cg.group(pair.second);
740 QString extenderItemId = dg.name();
741 QString extenderItemName = dg.readEntry(
"extenderItemName",
"");
742 QString appletName = dg.readEntry(
"sourceAppletPluginName",
"");
743 uint sourceAppletId = dg.readEntry(
"sourceAppletId", 0);
745 bool temporarySourceApplet =
false;
747 kDebug() <<
"applet id = " << applet.data()->
id();
748 kDebug() <<
"sourceappletid = " << sourceAppletId;
752 if (applet.data()->
id() == sourceAppletId) {
754 sourceApplet = applet.data();
760 Corona *corona = containment->corona();
762 if (sourceAppletId == q->applet()->id()) {
763 sourceApplet = q->applet();
765 foreach (
Containment *containment, corona->containments()) {
766 foreach (
Applet *applet, containment->applets()) {
767 if (applet->id() == sourceAppletId) {
768 sourceApplet = applet;
779 kDebug() <<
"creating a temporary applet as factory";
781 temporarySourceApplet =
true;
787 kDebug() <<
"sourceApplet is null? appletName = " << appletName;
788 kDebug() <<
" extenderItemId = " << extenderItemId;
791 if (dg.readEntry(
"isGroup",
false)) {
796 sourceApplet->initExtenderItem(item);
797 item->d->sourceApplet = sourceApplet;
799 if (temporarySourceApplet) {
806void ExtenderPrivate::updateBorders()
809 if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
812 item->d->themeChanged();
817void ExtenderPrivate::delayItemAddedEvent()
819 QHash<Plasma::ExtenderItem *, QPointF>::const_iterator i = pendingItems.constBegin();
820 while (i != pendingItems.constEnd()) {
821 q->itemAddedEvent(i.key(), i.value());
824 pendingItems.clear();
827void ExtenderPrivate::updateEmptyExtenderLabel()
829 if (q->isEmpty() && !emptyExtenderLabel &&
830 !emptyExtenderMessage.isEmpty() && !spacerWidget ) {
832 emptyExtenderLabel =
new Label(q);
833 emptyExtenderLabel->setAlignment(Qt::AlignCenter);
834 emptyExtenderLabel->setText(emptyExtenderMessage);
836 qreal left, top, right, bottom;
837 background->getMargins(left, top, right, bottom);
838 emptyExtenderLabel->nativeWidget()->setMargin(left + right);
840 emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
841 layout->addItem(emptyExtenderLabel);
842 }
else if (!q->isEmpty() && emptyExtenderLabel) {
844 layout->removeItem(emptyExtenderLabel);
845 delete emptyExtenderLabel;
846 emptyExtenderLabel = 0;
850void ExtenderPrivate::adjustMinimumSize()
853 scrollWidget->setMinimumWidth(mainWidget->effectiveSizeHint(Qt::MinimumSize).width() + 32);
855 scrollWidget->setMinimumHeight(qMin((qreal)300, mainWidget->effectiveSizeHint(Qt::MinimumSize).height()));
858void ExtenderPrivate::setPositionFromDragPosition(
const QPointF &pos)
860 const qreal ratio = (q->mapFromScene(pos).y()/scrollWidget->size().height());
862 mainWidget->setPos(mainWidget->pos().x(), 30 + (ratio *(scrollWidget->size().height() - mainWidget->size().height() - 30)));
865ExtenderGroup *ExtenderPrivate::findGroup(
const QString &name)
const
868 if (item->isGroup() && item->name() == name) {
869 return qobject_cast<ExtenderGroup*>(item);
876void ExtenderPrivate::extenderItemDestroyed(Plasma::ExtenderItem *item)
878 pendingItems.remove(item);
880 if (attachedExtenderItems.contains(item)) {
882 removeExtenderItem(item);
886void ExtenderPrivate::viewportGeometryChanged(
const QRectF &rect)
889 scrollbarVisible = (rect.height() > mainWidget->boundingRect().height());
893 bool scroll = !(rect.height() >= mainWidget->boundingRect().height());
895 if (scroll != scrollbarVisible) {
896 scrollbarVisible = scroll;
901void ExtenderPrivate::setDisabledBordersHint(
const FrameSvg::EnabledBorders borders)
903 if (disabledBordersHint == borders) {
907 disabledBordersHint = borders;
908 foreach (Plasma::ExtenderItem *item, attachedExtenderItems) {
909 item->d->themeChanged();
913void ExtenderPrivate::adjustSize()
916 QSizeF size = mainWidget->effectiveSizeHint(Qt::PreferredSize);
919 if (containment && containment->corona()) {
920 screenRect = containment->corona()->screenGeometry(containment->screen());
923 q->resize(qMin(screenRect.width()/3, (
int)size.width()),
924 qMin(screenRect.height()/3, (
int)size.height()));
931 if (!
item->isGroup()) {
947#include "extender.moc"
static Applet * load(const QString &name, uint appletId=0, const QVariantList &args=QVariantList())
Attempts to load an applet.
KConfigGroup config() const
Returns the KConfigGroup to access the applets configuration.
Containment * containment() const
The base class for plugins that provide backgrounds and applet grouping containers.
Corona * corona() const
Returns the Corona (if any) that this Containment is hosted by.
Applet::List applets() const
A QGraphicsScene for Plasma::Applets.
QList< Containment * > containments() const
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
Allows for grouping of extender items.
Provides detachable items for an Extender.
Extends applets to allow detachable parts.
QString emptyExtenderMessage
QList< ExtenderItem * > items
void dragEnterEvent(QGraphicsSceneDragDropEvent *event)
Reimplemented from QGraphicsWidget.
void resizeEvent(QGraphicsSceneResizeEvent *event)
Reimplemented from QGraphicsWidget.
virtual void itemAddedEvent(ExtenderItem *item, const QPointF &pos=QPointF(-1, -1))
Get's called after an item has been added to this extender.
QList< ExtenderItem * > attachedItems
friend class ExtenderItem
Extender(Applet *applet)
Creates an extender.
virtual void saveState()
This function get's called for every extender when plasma exits.
QVariant itemChange(GraphicsItemChange change, const QVariant &value)
Reimplemented from QGraphicsWidget.
Q_INVOKABLE ExtenderItem * item(const QString &name) const
This function can be used for obtaining the extender item specified by name.
Q_INVOKABLE bool hasItem(const QString &name) const
This function can be used for easily determining if a certain item is already displayed in an extende...
Appearance
Description on how to render the extender's items.
@ NoBorders
Draws no borders on the extender's items.
@ TopDownStacked
Draws no borders on the bottom extenderitem, but draws the left, bottom and right border on subsequen...
@ BottomUpStacked
Draws no borders on the topmost extenderitem, but draws the left, top and right border on subsequent ...
void geometryChanged()
Fires when an extender's preferred size changes.
Q_INVOKABLE ExtenderGroup * group(const QString &name) const
Extra convenience function for obtaining groups specified by name.
QList< ExtenderItem * > detachedItems
void dragMoveEvent(QGraphicsSceneDragDropEvent *event)
Reimplemented from QGraphicsWidget.
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
Reimplemented from QGraphicsWidget.
void mousePressEvent(QGraphicsSceneMouseEvent *event)
Reimplemented from QGraphicsWidget.
virtual void itemHoverLeaveEvent(ExtenderItem *item)
Get's called when an ExtenderItem that was previously hovering over this extender moves away from thi...
Appearance appearance() const
virtual void itemHoverEnterEvent(ExtenderItem *item)
Get's called when an ExtenderItem that get's dragged enters this extender.
virtual void itemRemovedEvent(ExtenderItem *item)
Get's called after an item has been removed from this extender.
void setAppearance(Appearance appearance)
Use this function to instruct the extender on how to render its items.
QList< ExtenderGroup * > groups
void setEmptyExtenderMessage(const QString &message)
friend class ExtenderGroup
void dropEvent(QGraphicsSceneDragDropEvent *event)
Reimplemented from QGraphicsWidget.
friend class ExtenderPrivate
virtual FrameSvg::EnabledBorders enabledBordersForItem(ExtenderItem *item) const
This function get's called on every item to determine which background border's to render.
virtual void itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
Gets called when an ExtenderItem is hovering over this extender.
Provides an SVG with borders.
Provides a plasma-themed QLabel.
Q_INVOKABLE QColor color(ColorRole role) const
Returns the text color to be used by items resting on the background.
static Theme * defaultTheme()
Singleton pattern accessor.
@ TextColor
the text color to be used by items resting on the background
QPainterPath roundedRectangle(const QRectF &rect, qreal radius)
Returns a nicely rounded rectanglular path for painting.
Namespace for everything in libplasma.
@ SizeConstraint
the size of the applet was changed
@ Horizontal
The applet is constrained vertically, but can expand horizontally.
@ Vertical
The applet is constrained horizontally, but can expand vertically.