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"
55 Spacer::Spacer(QGraphicsItem *parent)
64 void 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);
82 QColor c = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
84 painter->fillPath(p, c);
90 d(new ExtenderPrivate(applet, this))
94 if (applet->d->extender) {
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();
98 }
else if (
PopupApplet *popup = qobject_cast<PopupApplet *>(applet)) {
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);
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);
133 Extender::~Extender()
135 d->destroying =
true;
143 QList<QWeakPointer<ExtenderItem> > guardedItems;
145 foreach (
ExtenderItem *item, d->attachedExtenderItems) {
146 guardedItems << QWeakPointer<ExtenderItem>(item);
149 d->attachedExtenderItems.clear();
151 foreach (
const QWeakPointer<ExtenderItem> &guardedItem, guardedItems) {
154 item->disconnect(
this);
163 void Extender::setEmptyExtenderMessage(
const QString &message)
165 d->emptyExtenderMessage = message;
167 if (d->emptyExtenderLabel) {
168 d->emptyExtenderLabel->setText(message);
172 QString Extender::emptyExtenderMessage()
const
174 return d->emptyExtenderMessage;
177 QList<ExtenderItem*> Extender::items()
const
179 QList<ExtenderItem*> result;
184 return QList<ExtenderItem*>();
187 Containment *containment = d->applet.data()->containment();
192 foreach (Containment *c, containment->corona()->containments()) {
193 foreach (Applet *applet, c->applets()) {
194 if (applet->d->extender) {
195 foreach (ExtenderItem *item, applet->d->extender.data()->d->attachedExtenderItems) {
196 if (item->d->sourceApplet == d->applet.data()) {
207 QList<ExtenderItem*> Extender::attachedItems()
const
209 return d->attachedExtenderItems;
212 QList<ExtenderItem*> Extender::detachedItems()
const
214 QList<ExtenderItem*> result;
219 return QList<ExtenderItem*>();
221 Containment *containment = d->applet.data()->containment();
226 foreach (Containment *c, containment->corona()->containments()) {
227 foreach (Applet *applet, c->applets()) {
228 if (applet->d->extender) {
229 foreach (ExtenderItem *item, applet->d->extender.data()->attachedItems()) {
230 if (item->d->sourceApplet == d->applet.data() && item->isDetached()) {
244 foreach (
ExtenderItem *item, d->attachedExtenderItems) {
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) {
281 foreach (
ExtenderItem *item, applet->d->extender.data()->attachedItems()) {
282 if (item->d->sourceApplet == d->applet.data() && item->
name() == name) {
298 bool Extender::hasItem(
const QString &name)
const
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()) {
331 if (d->appearance == appearance) {
335 d->appearance = appearance;
341 return d->appearance;
344 QList<ExtenderGroup*> Extender::groups()
const
346 QList<ExtenderGroup *> result;
347 foreach (
ExtenderItem *item, d->attachedExtenderItems) {
348 if (item->
isGroup() && !result.contains(item->
group())) {
351 result.append(group);
360 return d->applet.data();
363 void Extender::saveState()
366 item->
config().writeEntry(
"extenderItemPosition", item->geometry().y());
370 QVariant Extender::itemChange(GraphicsItemChange change,
const QVariant &value)
372 if (change == QGraphicsItem::ItemPositionHasChanged) {
373 emit geometryChanged();
376 return QGraphicsWidget::itemChange(change, value);
379 void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
381 QGraphicsWidget::resizeEvent(event);
382 emit geometryChanged();
385 void Extender::mousePressEvent(QGraphicsSceneMouseEvent *event)
394 if (isEmpty() && popupApplet) {
399 void Extender::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
401 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
404 const ExtenderItemMimeData *mimeData =
405 qobject_cast<
const ExtenderItemMimeData*>(
event->mimeData());
408 itemHoverEnterEvent(mimeData->extenderItem());
418 void Extender::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
420 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
421 const ExtenderItemMimeData *mimeData =
422 qobject_cast<
const ExtenderItemMimeData*>(
event->mimeData());
425 itemHoverMoveEvent(mimeData->extenderItem(),
event->pos());
427 d->setPositionFromDragPosition(event->scenePos());
432 void Extender::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
434 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
435 const ExtenderItemMimeData *mimeData =
436 qobject_cast<
const ExtenderItemMimeData*>(
event->mimeData());
439 itemHoverLeaveEvent(mimeData->extenderItem());
442 Extender *sourceExtender = mimeData->extenderItem()->d->extender;
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());
476 void 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();
485 itemHoverLeaveEvent(0);
493 if (group && group->
autoHide() && group->
items().isEmpty()) {
497 if (!item->
group()) {
498 if (pos == QPointF(-1, -1)) {
500 d->layout->removeItem(item);
502 if (appearance() == BottomUpStacked) {
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();
554 itemHoverMoveEvent(item, QPointF(0, 0));
557 void 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();
605 FrameSvg::EnabledBorders Extender::enabledBordersForItem(
ExtenderItem *item)
const
607 if (d->layout->count() < 1) {
614 FrameSvg::EnabledBorders borders = FrameSvg::NoBorder;
617 return FrameSvg::NoBorder;
618 }
else if (d->appearance == TopDownStacked && bottomItem != item) {
619 borders = FrameSvg::LeftBorder | FrameSvg::BottomBorder | FrameSvg::RightBorder;
620 }
else if (d->appearance == BottomUpStacked && topItem != item) {
621 borders = FrameSvg::LeftBorder | FrameSvg::TopBorder | FrameSvg::RightBorder;
622 }
else if (d->appearance != NoBorders) {
623 borders = FrameSvg::LeftBorder | FrameSvg::RightBorder;
625 return FrameSvg::NoBorder;
628 if (d->scrollWidget->viewportGeometry().height() < d->mainWidget->boundingRect().height()) {
629 if (QApplication::layoutDirection() == Qt::RightToLeft) {
637 borders &= ~d->disabledBordersHint;
643 ExtenderPrivate::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");
659 ExtenderPrivate::~ExtenderPrivate()
663 void 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()));
678 void 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);
694 int 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()) {
716 void 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";
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) {
806 void ExtenderPrivate::updateBorders()
808 foreach (ExtenderItem *item, attachedExtenderItems) {
809 if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
812 item->d->themeChanged();
817 void 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();
827 void 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;
850 void ExtenderPrivate::adjustMinimumSize()
853 scrollWidget->setMinimumWidth(mainWidget->effectiveSizeHint(Qt::MinimumSize).width() + 32);
855 scrollWidget->setMinimumHeight(qMin((qreal)300, mainWidget->effectiveSizeHint(Qt::MinimumSize).height()));
858 void 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)));
865 ExtenderGroup *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);
886 void 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;
901 void ExtenderPrivate::setDisabledBordersHint(
const FrameSvg::EnabledBorders borders)
903 if (disabledBordersHint == borders) {
907 disabledBordersHint = borders;
909 item->d->themeChanged();
913 void 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()));
930 foreach (
ExtenderItem *item, d->attachedExtenderItems) {
936 if (group && (!group->
autoHide() || group->
items().size() > 0)) {
947 #include "extender.moc"
the size of the applet was changed
Appearance
Description on how to render the extender's items.
The applet is constrained horizontally, but can expand vertically.
virtual QRect screenGeometry(int id) const
Returns the geometry of a given screen.
Extends applets to allow detachable parts.
QList< ExtenderItem * > items() const
KConfigGroup config() const
fetch the configuration of this widget.
QList< Containment * > containments() const
Applet::List applets() const
Provides an SVG with borders.
The applet is constrained vertically, but can expand horizontally.
Corona * corona() const
Returns the Corona (if any) that this Containment is hosted by.
Draws no borders on the bottom extenderitem, but draws the left, bottom and right border on subsequen...
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
QPainterPath roundedRectangle(const QRectF &rect, qreal radius)
Returns a nicely rounded rectanglular path for painting.
Q_INVOKABLE ExtenderGroup * group(const QString &name) const
Extra convenience function for obtaining groups specified by name.
Draws no borders on the topmost extenderitem, but draws the left, top and right border on subsequent ...
ExtenderGroup * group() const
Q_INVOKABLE ExtenderItem * item(const QString &name) const
This function can be used for obtaining the extender item specified by name.
Containment * containment() const
KConfigGroup config() const
Returns the KConfigGroup to access the applets configuration.
Allows for grouping of extender items.
The base class for plugins that provide backgrounds and applet grouping containers.
Provides detachable items for an Extender.
A QGraphicsScene for Plasma::Applets.
static Applet * load(const QString &name, uint appletId=0, const QVariantList &args=QVariantList())
Attempts to load an applet.