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)
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);
90 d(new ExtenderPrivate(applet, this))
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);
163void Extender::setEmptyExtenderMessage(
const QString &message)
165 d->emptyExtenderMessage = message;
167 if (d->emptyExtenderLabel) {
168 d->emptyExtenderLabel->setText(message);
172QString Extender::emptyExtenderMessage()
const
174 return d->emptyExtenderMessage;
177QList<ExtenderItem*> Extender::items()
const
179 QList<ExtenderItem*> result;
184 return QList<ExtenderItem*>();
187 Containment *containment = d->applet.data()->containment();
194 if (
applet->d->extender) {
196 if (
item->d->sourceApplet == d->applet.data()) {
207QList<ExtenderItem*> Extender::attachedItems()
const
209 return d->attachedExtenderItems;
212QList<ExtenderItem*> Extender::detachedItems()
const
214 QList<ExtenderItem*> result;
219 return QList<ExtenderItem*>();
221 Containment *containment = d->applet.data()->containment();
228 if (
applet->d->extender) {
245 if (
item->d->sourceApplet == d->applet.data() &&
item->
name() == name) {
257 Containment *containment = d->applet.data()->containment();
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));
298bool Extender::hasItem(
const QString &name)
const
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;
344QList<ExtenderGroup*> Extender::groups()
const
346 QList<ExtenderGroup *> result;
351 result.append(
group);
360 return d->applet.data();
363void Extender::saveState()
366 item->
config().writeEntry(
"extenderItemPosition",
item->geometry().y());
370QVariant Extender::itemChange(GraphicsItemChange change,
const QVariant &value)
372 if (change == QGraphicsItem::ItemPositionHasChanged) {
376 return QGraphicsWidget::itemChange(change, value);
379void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
381 QGraphicsWidget::resizeEvent(event);
385void Extender::mousePressEvent(QGraphicsSceneMouseEvent *event)
393 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
394 if (
isEmpty() && popupApplet) {
399void Extender::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
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());
418void Extender::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
420 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
421 const ExtenderItemMimeData *mimeData =
422 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
427 d->setPositionFromDragPosition(event->scenePos());
432void Extender::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
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());
476void Extender::dropEvent(QGraphicsSceneDragDropEvent *event)
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();
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();
557void Extender::itemHoverMoveEvent(
ExtenderItem *item,
const QPointF &pos)
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();
605FrameSvg::EnabledBorders Extender::enabledBordersForItem(
ExtenderItem *item)
const
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);
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;
751 Applet *sourceApplet = 0;
752 if (applet.data()->
id() == sourceAppletId) {
754 sourceApplet = applet.data();
757 Containment *containment = applet.data()->
containment();
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";
780 sourceApplet = Applet::load(appletName);
781 temporarySourceApplet =
true;
787 kDebug() <<
"sourceApplet is null? appletName = " << appletName;
788 kDebug() <<
" extenderItemId = " << extenderItemId;
791 if (dg.readEntry(
"isGroup",
false)) {
792 item =
new ExtenderGroup(q, extenderItemId.toInt());
794 item =
new ExtenderItem(q, extenderItemId.toInt());
796 sourceApplet->initExtenderItem(item);
797 item->d->sourceApplet = sourceApplet;
799 if (temporarySourceApplet) {
806void ExtenderPrivate::updateBorders()
808 foreach (ExtenderItem *item, attachedExtenderItems) {
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
867 foreach (ExtenderItem *item, attachedExtenderItems) {
868 if (item->isGroup() && item->name() == name) {
869 return qobject_cast<ExtenderGroup*>(item);
878 pendingItems.remove(item);
880 if (attachedExtenderItems.contains(item)) {
882 removeExtenderItem(item);
886void ExtenderPrivate::viewportGeometryChanged(
const QRectF &rect)
888 if (appearance != Extender::TopDownStacked && appearance != Extender::BottomUpStacked) {
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;
909 item->d->themeChanged();
913void ExtenderPrivate::adjustSize()
916 QSizeF size = mainWidget->effectiveSizeHint(Qt::PreferredSize);
918 Containment *containment = applet.data()->
containment();
919 if (containment && containment->corona()) {
923 q->resize(qMin(screenRect.width()/3, (
int)size.width()),
924 qMin(screenRect.height()/3, (
int)size.height()));
927bool Extender::isEmpty()
const
947#include "extender.moc"
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.
virtual QRect screenGeometry(int id) const
Returns the geometry of a given screen.
Allows for grouping of extender items.
QList< ExtenderItem * > items() const
Provides detachable items for an Extender.
KConfigGroup config() const
fetch the configuration of this widget.
ExtenderGroup * group() const
Extends applets to allow detachable parts.
QList< ExtenderItem * > attachedItems
Q_INVOKABLE ExtenderItem * item(const QString &name) const
This function can be used for obtaining the extender item specified by name.
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.
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 itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
Gets called when an ExtenderItem is hovering over this extender.
Provides an SVG with borders.
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.