00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "extender.h"
00021
00022 #include <QAction>
00023 #include <QLabel>
00024 #include <QGraphicsSceneDragDropEvent>
00025 #include <QGraphicsGridLayout>
00026 #include <QGraphicsLinearLayout>
00027 #include <QPainter>
00028
00029 #include "applet.h"
00030 #include "containment.h"
00031 #include "corona.h"
00032 #include "extendergroup.h"
00033 #include "extenderitem.h"
00034 #include "framesvg.h"
00035 #include "paintutils.h"
00036 #include "popupapplet.h"
00037 #include "svg.h"
00038 #include "theme.h"
00039 #include "widgets/label.h"
00040
00041 #include "private/applet_p.h"
00042 #include "private/applethandle_p.h"
00043 #include "private/extender_p.h"
00044 #include "private/extenderapplet_p.h"
00045 #include "private/extenderitem_p.h"
00046 #include "private/extenderitemmimedata_p.h"
00047
00048 namespace Plasma
00049 {
00050
00051
00052 class Spacer : public QGraphicsWidget
00053 {
00054 public:
00055 Spacer(Extender *parent)
00056 : QGraphicsWidget(parent)
00057 {
00058 }
00059
00060 ~Spacer()
00061 {
00062 }
00063
00064 void setMargins(qreal left, qreal top, qreal right, qreal bottom)
00065 {
00066 m_left = left;
00067 m_top = top;
00068 m_right = right;
00069 m_bottom = bottom;
00070 }
00071
00072 protected:
00073 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget = 0)
00074 {
00075 Q_UNUSED(option)
00076 Q_UNUSED(widget)
00077
00078 painter->setRenderHint(QPainter::Antialiasing);
00079 QPainterPath p = Plasma::PaintUtils::roundedRectangle(
00080 contentsRect().adjusted(m_left, m_top, -m_right, -m_bottom), 4);
00081
00082 QColor c = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
00083 c.setAlphaF(0.3);
00084 painter->fillPath(p, c);
00085 }
00086
00087 private:
00088 qreal m_left;
00089 qreal m_top;
00090 qreal m_right;
00091 qreal m_bottom;
00092 };
00093
00094 Extender::Extender(Applet *applet)
00095 : QGraphicsWidget(applet),
00096 d(new ExtenderPrivate(applet, this))
00097 {
00098
00099
00100 if (applet->d->extender) {
00101 kWarning() << "Applet already has an extender, and can have only one extender."
00102 << "The previous extender will be destroyed.";
00103 delete applet->d->extender;
00104 }
00105 applet->d->extender = this;
00106
00107 setContentsMargins(0, 0, 0, 0);
00108 d->layout = new QGraphicsLinearLayout(this);
00109 d->layout->setOrientation(Qt::Vertical);
00110 d->layout->setContentsMargins(0, 0, 0, 0);
00111 d->layout->setSpacing(0);
00112 setLayout(d->layout);
00113
00114 d->loadExtenderItems();
00115
00116 setAcceptDrops(true);
00117 }
00118
00119 Extender::~Extender()
00120 {
00121 d->applet->d->extender = 0;
00122 delete d;
00123 }
00124
00125 void Extender::setEmptyExtenderMessage(const QString &message)
00126 {
00127 d->emptyExtenderMessage = message;
00128
00129 if (d->emptyExtenderLabel) {
00130 d->emptyExtenderLabel->setText(message);
00131 }
00132 }
00133
00134 QString Extender::emptyExtenderMessage() const
00135 {
00136 return d->emptyExtenderMessage;
00137 }
00138
00139 QList<ExtenderItem*> Extender::items() const
00140 {
00141 QList<ExtenderItem*> result;
00142
00143
00144
00145 Containment *containment = d->applet->containment();
00146 if (!containment) {
00147 return result;
00148 }
00149
00150 foreach (Containment *c, containment->corona()->containments()) {
00151 foreach (Applet *applet, c->applets()) {
00152 if (applet->d->extender) {
00153 foreach (ExtenderItem *item, applet->d->extender->attachedItems()) {
00154 if (item->d->sourceApplet == d->applet) {
00155 result.append(item);
00156 }
00157 }
00158 }
00159 }
00160 }
00161
00162 return result;
00163 }
00164
00165 QList<ExtenderItem*> Extender::attachedItems() const
00166 {
00167 return d->attachedExtenderItems;
00168 }
00169
00170 QList<ExtenderItem*> Extender::detachedItems() const
00171 {
00172 QList<ExtenderItem*> result;
00173
00174
00175
00176 Containment *containment = d->applet->containment();
00177 if (!containment) {
00178 return result;
00179 }
00180
00181 foreach (Containment *c, containment->corona()->containments()) {
00182 foreach (Applet *applet, c->applets()) {
00183 if (applet->d->extender) {
00184 foreach (ExtenderItem *item, applet->d->extender->attachedItems()) {
00185 if (item->d->sourceApplet == d->applet && item->isDetached()) {
00186 result.append(item);
00187 }
00188 }
00189 }
00190 }
00191 }
00192
00193 return result;
00194 }
00195
00196 ExtenderItem *Extender::item(const QString &name) const
00197 {
00198
00199 foreach (ExtenderItem *item, d->attachedExtenderItems) {
00200 if (item->d->sourceApplet == d->applet && item->name() == name) {
00201 return item;
00202 }
00203 }
00204
00205
00206
00207
00208 Containment *containment = d->applet->containment();
00209 if (!containment) {
00210 return 0;
00211 }
00212
00213 foreach (Containment *c, containment->corona()->containments()) {
00214 foreach (Applet *applet, c->applets()) {
00215 if (applet->d->extender) {
00216 if (applet->d->extender == this) {
00217
00218 continue;
00219 }
00220
00221 foreach (ExtenderItem *item, applet->d->extender->attachedItems()) {
00222 if (item->d->sourceApplet == d->applet && item->name() == name) {
00223 return item;
00224 }
00225 }
00226 }
00227 }
00228 }
00229
00230 return 0;
00231 }
00232
00233 ExtenderGroup *Extender::group(const QString &name) const
00234 {
00235 return qobject_cast<ExtenderGroup*>(item(name));
00236 }
00237
00238 bool Extender::hasItem(const QString &name) const
00239 {
00240 if (item(name)) {
00241 return true;
00242 }
00243
00244
00245
00246
00247 Corona *corona = qobject_cast<Corona *>(scene());
00248 if (!corona) {
00249 return false;
00250 }
00251
00252 KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
00253 foreach (const QString &extenderItemId, extenderItemGroup.groupList()) {
00254 KConfigGroup cg = extenderItemGroup.group(extenderItemId);
00255 if (uint(cg.readEntry("sourceAppletId", 0)) == d->applet->id() &&
00256 cg.readEntry("extenderItemName", "") == name &&
00257 cg.readEntry("sourceAppletPluginName", "") == d->applet->pluginName()) {
00258 return true;
00259 }
00260 }
00261
00262 return false;
00263 }
00264
00265 void Extender::setAppearance(Appearance appearance)
00266 {
00267 if (d->appearance == appearance) {
00268 return;
00269 }
00270
00271 d->appearance = appearance;
00272 d->updateBorders();
00273 }
00274
00275 Extender::Appearance Extender::appearance() const
00276 {
00277 return d->appearance;
00278 }
00279
00280 QList<ExtenderGroup*> Extender::groups() const
00281 {
00282 QList<ExtenderGroup*> result;
00283 foreach (ExtenderItem *item, d->attachedExtenderItems) {
00284 if (item->isGroup() && !result.contains(item->group())) {
00285 result.append(item->group());
00286 }
00287 }
00288 return result;
00289 }
00290
00291 void Extender::saveState()
00292 {
00293 foreach (ExtenderItem *item, attachedItems()) {
00294 item->config().writeEntry("extenderItemPosition", item->geometry().y());
00295 }
00296 }
00297
00298 QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
00299 {
00300 if (change == QGraphicsItem::ItemPositionHasChanged) {
00301 emit geometryChanged();
00302 }
00303
00304 return QGraphicsWidget::itemChange(change, value);
00305 }
00306
00307 void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
00308 {
00309 QGraphicsWidget::resizeEvent(event);
00310 emit geometryChanged();
00311 }
00312
00313 void Extender::mousePressEvent(QGraphicsSceneMouseEvent *event)
00314 {
00315 Q_UNUSED(event)
00316 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet);
00317 if (isEmpty() && popupApplet) {
00318 popupApplet->hidePopup();
00319 }
00320 }
00321
00322 void Extender::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00323 {
00324 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00325 event->accept();
00326
00327 const ExtenderItemMimeData *mimeData =
00328 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00329
00330 if (mimeData) {
00331 itemHoverEnterEvent(mimeData->extenderItem());
00332
00333 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet);
00334 if (popupApplet) {
00335 popupApplet->showPopup();
00336 }
00337 }
00338 }
00339 }
00340
00341 void Extender::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00342 {
00343 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00344 const ExtenderItemMimeData *mimeData =
00345 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00346
00347 if (mimeData) {
00348 itemHoverMoveEvent(mimeData->extenderItem(), event->pos());
00349 }
00350 }
00351 }
00352
00353 void Extender::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
00354 {
00355 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00356 const ExtenderItemMimeData *mimeData =
00357 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00358
00359 if (mimeData) {
00360 itemHoverLeaveEvent(mimeData->extenderItem());
00361
00362
00363 Extender *sourceExtender = mimeData->extenderItem()->d->extender;
00364
00365
00366
00367
00368 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet);
00369 if (popupApplet && sourceExtender != this) {
00370 kDebug() << "leaving another extender then the extender we started, so hide the popup.";
00371 popupApplet->showPopup(250);
00372 }
00373
00374
00375 if (popupApplet && sourceExtender == this && (attachedItems().count() < 2)) {
00376 kDebug() << "leaving the extender, and there are no more attached items so hide the popup.";
00377 popupApplet->hidePopup();
00378 }
00379
00380
00381
00382 ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->applet);
00383 if (extenderApplet && sourceExtender == this && attachedItems().count() < 2 &&
00384 extenderApplet->formFactor() != Plasma::Horizontal &&
00385 extenderApplet->formFactor() != Plasma::Vertical) {
00386 kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
00387 extenderApplet->hide();
00388 AppletHandle *handle = dynamic_cast<AppletHandle*>(extenderApplet->parentItem());
00389 if (handle) {
00390 handle->hide();
00391 }
00392 }
00393 }
00394 }
00395 }
00396
00397 void Extender::dropEvent(QGraphicsSceneDragDropEvent *event)
00398 {
00399 if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00400 const ExtenderItemMimeData *mimeData =
00401 qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00402
00403 if (mimeData) {
00404 mimeData->extenderItem()->setExtender(this, event->pos());
00405 QApplication::restoreOverrideCursor();
00406 itemHoverLeaveEvent(0);
00407 }
00408 }
00409 }
00410
00411 void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
00412 {
00413 if (pos == QPointF(-1, -1)) {
00414
00415 if (!item->group()) {
00416 if (appearance() == BottomUpStacked) {
00417
00418 d->layout->insertItem(0, item);
00419 } else {
00420
00421 d->layout->addItem(item);
00422 }
00423 } else {
00424
00425 d->layout->insertItem(d->insertIndexFromPos(item->group()->pos()) + 1, item);
00426 }
00427 } else {
00428 d->layout->insertItem(d->insertIndexFromPos(pos), item);
00429 }
00430
00431
00432 d->updateEmptyExtenderLabel();
00433 d->updateBorders();
00434 }
00435
00436 void Extender::itemRemovedEvent(ExtenderItem *item)
00437 {
00438 d->layout->removeItem(item);
00439
00440 if (d->spacerWidget) {
00441 d->layout->removeItem(d->spacerWidget);
00442 delete d->spacerWidget;
00443 d->spacerWidget = 0;
00444 }
00445
00446
00447 d->updateEmptyExtenderLabel();
00448 d->updateBorders();
00449 }
00450
00451 void Extender::itemHoverEnterEvent(ExtenderItem *item)
00452 {
00453 itemHoverMoveEvent(item, QPointF(0, 0));
00454 }
00455
00456 void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
00457 {
00458 if (d->spacerWidget && d->spacerWidget->geometry().contains(pos)) {
00459 return;
00460 }
00461
00462
00463 if (d->spacerWidget) {
00464 d->layout->removeItem(d->spacerWidget);
00465 }
00466
00467 int insertIndex = d->insertIndexFromPos(pos);
00468
00469
00470 if (!d->spacerWidget) {
00471 Spacer *widget = new Spacer(this);
00472 qreal left, top, right, bottom;
00473 d->background->getMargins(left, top, right, bottom);
00474 widget->setMargins(left, 4, right, 4);
00475
00476 widget->setMinimumSize(item->minimumSize());
00477 widget->setPreferredSize(item->preferredSize());
00478 widget->setMaximumSize(item->maximumSize());
00479 widget->setSizePolicy(item->sizePolicy());
00480 d->spacerWidget = widget;
00481 }
00482 d->layout->insertItem(insertIndex, d->spacerWidget);
00483
00484
00485 d->updateEmptyExtenderLabel();
00486 }
00487
00488 void Extender::itemHoverLeaveEvent(ExtenderItem *item)
00489 {
00490 Q_UNUSED(item);
00491
00492 if (d->spacerWidget) {
00493
00494 d->layout->removeItem(d->spacerWidget);
00495 delete d->spacerWidget;
00496 d->spacerWidget = 0;
00497
00498 d->currentSpacerIndex = -1;
00499
00500 d->updateEmptyExtenderLabel();
00501 }
00502 }
00503
00504 FrameSvg::EnabledBorders Extender::enabledBordersForItem(ExtenderItem *item) const
00505 {
00506 if (d->layout->count() < 1) {
00507 return 0;
00508 }
00509
00510 ExtenderItem *topItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(0));
00511 ExtenderItem *bottomItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(d->layout->count() - 1));
00512 if (item->group()) {
00513 return FrameSvg::LeftBorder | FrameSvg::RightBorder;
00514 } else if (d->appearance == TopDownStacked && bottomItem != item) {
00515 return FrameSvg::LeftBorder | FrameSvg::BottomBorder | FrameSvg::RightBorder;
00516 } else if (d->appearance == BottomUpStacked && topItem != item) {
00517 return FrameSvg::LeftBorder | FrameSvg::TopBorder | FrameSvg::RightBorder;
00518 } else if (d->appearance != NoBorders) {
00519 return FrameSvg::LeftBorder | FrameSvg::RightBorder;
00520 } else {
00521 return 0;
00522 }
00523 }
00524
00525 ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
00526 q(extender),
00527 applet(applet),
00528 background(new FrameSvg(extender)),
00529 currentSpacerIndex(-1),
00530 spacerWidget(0),
00531 emptyExtenderMessage(QString()),
00532 emptyExtenderLabel(0),
00533 appearance(Extender::NoBorders)
00534 {
00535 background->setImagePath("widgets/extender-background");
00536 }
00537
00538 ExtenderPrivate::~ExtenderPrivate()
00539 {
00540 }
00541
00542 void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
00543 {
00544 attachedExtenderItems.append(item);
00545 q->itemHoverLeaveEvent(item);
00546 q->itemAddedEvent(item, pos);
00547 emit q->itemAttached(item);
00548 }
00549
00550 void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
00551 {
00552 attachedExtenderItems.removeOne(item);
00553
00554
00555 if (!q->attachedItems().count()) {
00556 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet);
00557 if (popupApplet) {
00558 popupApplet->hidePopup();
00559 }
00560 }
00561
00562 q->itemRemovedEvent(item);
00563 }
00564
00565 int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
00566 {
00567 int insertIndex = -1;
00568
00569
00570 if (pos != QPointF(-1, -1)) {
00571 for (int i = 0; i < layout->count(); ++i) {
00572 QRectF siblingGeometry = layout->itemAt(i)->geometry();
00573 qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
00574 if (pos.y() < middle) {
00575 insertIndex = i;
00576 break;
00577 } else if (pos.y() <= siblingGeometry.bottom()) {
00578 insertIndex = i + 1;
00579 break;
00580 }
00581 }
00582 }
00583
00584 return insertIndex;
00585 }
00586
00587 void ExtenderPrivate::loadExtenderItems()
00588 {
00589 KConfigGroup cg = applet->config("ExtenderItems");
00590
00591
00592
00593 QList<QPair<int, QString> > groupList;
00594 foreach (const QString &extenderItemId, cg.groupList()) {
00595 KConfigGroup dg = cg.group(extenderItemId);
00596 groupList.append(qMakePair(dg.readEntry("extenderItemPosition", 0), extenderItemId));
00597 }
00598 qSort(groupList);
00599
00600
00601 for (int i = 0; i < groupList.count(); i++) {
00602 QPair<int, QString> pair = groupList[i];
00603
00604 KConfigGroup dg = cg.group(pair.second);
00605
00606
00607 QString extenderItemId = dg.name();
00608 QString extenderItemName = dg.readEntry("extenderItemName", "");
00609 QString appletName = dg.readEntry("sourceAppletPluginName", "");
00610 uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
00611
00612 bool temporarySourceApplet = false;
00613
00614 kDebug() << "applet id = " << applet->id();
00615 kDebug() << "sourceappletid = " << sourceAppletId;
00616
00617
00618 Applet *sourceApplet = 0;
00619 if (applet->id() == sourceAppletId) {
00620
00621 sourceApplet = applet;
00622 } else {
00623
00624 Containment *containment = applet->containment();
00625
00626 if (containment) {
00627 Corona *corona = containment->corona();
00628
00629 foreach (Containment *containment, corona->containments()) {
00630 foreach (Applet *applet, containment->applets()) {
00631 if (applet->id() == sourceAppletId) {
00632 sourceApplet = applet;
00633 }
00634 }
00635 }
00636 }
00637 }
00638
00639
00640
00641 if (!sourceApplet) {
00642 kDebug() << "creating a temporary applet as factory";
00643 sourceApplet = Applet::load(appletName);
00644 temporarySourceApplet = true;
00645
00646
00647 }
00648
00649 if (!sourceApplet) {
00650 kDebug() << "sourceApplet is null? appletName = " << appletName;
00651 kDebug() << " extenderItemId = " << extenderItemId;
00652 } else {
00653 ExtenderItem *item;
00654 if (dg.readEntry("isGroup", false)) {
00655 item = new ExtenderGroup(q, extenderItemId.toInt());
00656 } else {
00657 item = new ExtenderItem(q, extenderItemId.toInt());
00658 }
00659 sourceApplet->initExtenderItem(item);
00660
00661 if (temporarySourceApplet) {
00662 delete sourceApplet;
00663 }
00664 }
00665 }
00666 }
00667
00668 void ExtenderPrivate::updateBorders()
00669 {
00670 foreach (ExtenderItem *item, q->attachedItems()) {
00671 if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
00672
00673
00674 item->d->themeChanged();
00675 }
00676 }
00677 }
00678
00679 void ExtenderPrivate::updateEmptyExtenderLabel()
00680 {
00681 if (q->isEmpty() && !emptyExtenderLabel &&
00682 !emptyExtenderMessage.isEmpty() && !spacerWidget ) {
00683
00684 emptyExtenderLabel = new Label(q);
00685 emptyExtenderLabel->setAlignment(Qt::AlignCenter);
00686 emptyExtenderLabel->setText(emptyExtenderMessage);
00687
00688 qreal left, top, right, bottom;
00689 background->getMargins(left, top, right, bottom);
00690 emptyExtenderLabel->nativeWidget()->setMargin(left + right);
00691
00692 emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00693 layout->addItem(emptyExtenderLabel);
00694 } else if (!q->isEmpty() && emptyExtenderLabel) {
00695
00696 layout->removeItem(emptyExtenderLabel);
00697 delete emptyExtenderLabel;
00698 emptyExtenderLabel = 0;
00699 }
00700 }
00701
00702 ExtenderGroup *ExtenderPrivate::findGroup(const QString &name) const
00703 {
00704 foreach (ExtenderItem *item, q->attachedItems()) {
00705 if (item->isGroup() && item->name() == name) {
00706 return qobject_cast<ExtenderGroup*>(item);
00707 }
00708 }
00709
00710 return 0;
00711 }
00712
00713 bool Extender::isEmpty() const
00714 {
00715
00716 foreach (ExtenderItem *item, d->attachedExtenderItems) {
00717 if (!item->isGroup()) {
00718 return false;
00719 }
00720 }
00721
00722 return true;
00723 }
00724
00725 }
00726
00727 #include "extender.moc"