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"
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)));
119 d->layout->setContentsMargins(0, 0, 0, 0);
120 d->layout->setSpacing(0);
125 setContentsMargins(0, 0, 0, 0);
128 d->loadExtenderItems();
130 setAcceptDrops(
true);
133 Extender::~Extender()
135 d->destroying =
true;
145 foreach (
ExtenderItem *item, d->attachedExtenderItems) {
146 guardedItems << QWeakPointer<ExtenderItem>(item);
149 d->attachedExtenderItems.
clear();
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;
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()) {
209 return d->attachedExtenderItems;
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) {
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;
347 foreach (
ExtenderItem *item, d->attachedExtenderItems) {
360 return d->applet.
data();
363 void Extender::saveState()
366 item->
config().writeEntry(
"extenderItemPosition", item->
geometry().y());
372 if (change == QGraphicsItem::ItemPositionHasChanged) {
373 emit geometryChanged();
382 emit geometryChanged();
394 if (isEmpty() && popupApplet) {
404 const ExtenderItemMimeData *mimeData =
405 qobject_cast<
const ExtenderItemMimeData*>(
event->mimeData());
408 itemHoverEnterEvent(mimeData->extenderItem());
421 const ExtenderItemMimeData *mimeData =
422 qobject_cast<
const ExtenderItemMimeData*>(
event->mimeData());
425 itemHoverMoveEvent(mimeData->extenderItem(),
event->pos());
427 d->setPositionFromDragPosition(event->
scenePos());
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());
479 const ExtenderItemMimeData *mimeData =
480 qobject_cast<
const ExtenderItemMimeData*>(
event->mimeData());
483 mimeData->extenderItem()->setExtender(
this, event->
pos());
485 itemHoverLeaveEvent(0);
493 if (group && group->
autoHide() && group->
items().isEmpty()) {
497 if (!item->
group()) {
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();
554 itemHoverMoveEvent(item,
QPointF(0, 0));
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);
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()) {
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);
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;
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");
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++) {
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()
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()) {
920 screenRect = containment->corona()->screenGeometry(containment->screen());
923 q->resize(qMin(screenRect.
width()/3, (int)size.
width()),
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.
const QMimeData * mimeData() const
The applet is constrained horizontally, but can expand vertically.
void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
Qt::LayoutDirection layoutDirection()
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.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
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...
QGraphicsItemGroup * group() const
int count(const T &value) const
KSharedConfig::Ptr config() const
Returns the config file used to store the configuration for this Corona.
void append(const T &value)
QPainterPath roundedRectangle(const QRectF &rect, qreal radius)
Returns a nicely rounded rectanglular path for painting.
int toInt(bool *ok, int base) const
Draws no borders on the topmost extenderitem, but draws the left, top and right border on subsequent ...
ExtenderGroup * group() const
void restoreOverrideCursor()
Q_INVOKABLE ExtenderItem * item(const QString &name) const
This function can be used for obtaining the extender item specified by name.
bool contains(const T &value) const
QVariant data(int key) const
void addItem(QGraphicsLayoutItem *item)
Allows for grouping of extender items.
The base class for plugins that provide backgrounds and applet grouping containers.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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.