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 <QGraphicsGridLayout>
00024 #include <QGraphicsLinearLayout>
00025 #include <QPainter>
00026
00027 #include "applet.h"
00028 #include "containment.h"
00029 #include "corona.h"
00030 #include "extenderitem.h"
00031 #include "framesvg.h"
00032 #include "paintutils.h"
00033 #include "popupapplet.h"
00034 #include "svg.h"
00035 #include "theme.h"
00036 #include "widgets/label.h"
00037
00038 #include "private/applet_p.h"
00039 #include "private/extender_p.h"
00040 #include "private/extenderitem_p.h"
00041
00042 namespace Plasma
00043 {
00044
00045
00046 class Spacer : public QGraphicsWidget
00047 {
00048 public:
00049 Spacer(QGraphicsWidget *parent)
00050 : QGraphicsWidget(parent)
00051 {
00052 }
00053
00054 ~Spacer()
00055 {
00056 }
00057
00058 protected:
00059 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget = 0)
00060 {
00061 Q_UNUSED(option)
00062 Q_UNUSED(widget)
00063
00064 painter->setRenderHint(QPainter::Antialiasing);
00065 QPainterPath p = Plasma::PaintUtils::roundedRectangle(contentsRect().adjusted(4, 4, -4, -4), 4);
00066
00067 QColor c = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
00068 c.setAlphaF(0.3);
00069 painter->fillPath(p, c);
00070 }
00071 };
00072
00073 Extender::Extender(Applet *applet)
00074 : QGraphicsWidget(applet),
00075 d(new ExtenderPrivate(applet, this))
00076 {
00077
00078
00079 if (applet->d->extender) {
00080 kWarning() << "Applet already has an extender, and can have only one extender."
00081 << "The previous extender will be destroyed.";
00082 delete applet->d->extender;
00083 }
00084 applet->d->extender = this;
00085
00086 setContentsMargins(0, 0, 0, 0);
00087 d->layout = new QGraphicsLinearLayout(this);
00088 d->layout->setOrientation(Qt::Vertical);
00089 d->layout->setContentsMargins(0, 0, 0, 0);
00090 d->layout->setSpacing(0);
00091 setLayout(d->layout);
00092
00093 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00094
00095 d->loadExtenderItems();
00096 }
00097
00098 Extender::~Extender()
00099 {
00100 d->applet->d->extender = 0;
00101 delete d;
00102 }
00103
00104 void Extender::setEmptyExtenderMessage(const QString &message)
00105 {
00106 d->emptyExtenderMessage = message;
00107
00108 if (d->emptyExtenderLabel) {
00109 d->emptyExtenderLabel->setText(message);
00110 }
00111 }
00112
00113 QString Extender::emptyExtenderMessage() const
00114 {
00115 return d->emptyExtenderMessage;
00116 }
00117
00118 QList<ExtenderItem*> Extender::items() const
00119 {
00120 QList<ExtenderItem*> result;
00121
00122
00123
00124 foreach (Containment *c, d->applet->containment()->corona()->containments()) {
00125 foreach (Applet *applet, c->applets()) {
00126 if (applet->d->extender) {
00127 foreach (ExtenderItem *item, applet->d->extender->attachedItems()) {
00128 if (item->d->sourceApplet == d->applet) {
00129 result.append(item);
00130 }
00131 }
00132 }
00133 }
00134 }
00135
00136 return result;
00137 }
00138
00139 QList<ExtenderItem*> Extender::attachedItems() const
00140 {
00141 return d->attachedExtenderItems;
00142 }
00143
00144 QList<ExtenderItem*> Extender::detachedItems() const
00145 {
00146 QList<ExtenderItem*> result;
00147
00148 foreach (ExtenderItem *item, items()) {
00149 if (item->isDetached()) {
00150 result.append(item);
00151 }
00152 }
00153
00154 return result;
00155 }
00156
00157 ExtenderItem *Extender::item(const QString &name) const
00158 {
00159 foreach (ExtenderItem *item, items()) {
00160 if (item->name() == name) {
00161 return item;
00162 }
00163 }
00164
00165 return 0;
00166 }
00167
00168 void Extender::setAppearance(Appearance appearance)
00169 {
00170 if (d->appearance == appearance) {
00171 return;
00172 }
00173
00174 d->appearance = appearance;
00175 d->updateBorders();
00176 }
00177
00178 Extender::Appearance Extender::appearance() const
00179 {
00180 return d->appearance;
00181 }
00182
00183 void Extender::saveState()
00184 {
00185 foreach (ExtenderItem *item, attachedItems()) {
00186 item->config().writeEntry("extenderItemPosition", item->geometry().y());
00187 }
00188 }
00189
00190 QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
00191 {
00192 if (change == QGraphicsItem::ItemPositionHasChanged) {
00193 emit geometryChanged();
00194 }
00195
00196 return QGraphicsWidget::itemChange(change, value);
00197 }
00198
00199 void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
00200 {
00201 QGraphicsWidget::resizeEvent(event);
00202 emit geometryChanged();
00203 }
00204
00205 void Extender::mousePressEvent(QGraphicsSceneMouseEvent *event)
00206 {
00207 Q_UNUSED(event)
00208 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet);
00209 if (attachedItems().isEmpty() && popupApplet) {
00210 popupApplet->hidePopup();
00211 }
00212 }
00213
00214 void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
00215 {
00216
00217 item->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00218
00219 if (pos == QPointF(-1, -1)) {
00220 d->layout->addItem(item);
00221 } else {
00222 d->layout->insertItem(d->insertIndexFromPos(pos), item);
00223 }
00224
00225
00226 d->updateEmptyExtenderLabel();
00227
00228
00229
00230
00231 if (item->widget()) {
00232 d->adjustSizeHints();
00233 }
00234 }
00235
00236 void Extender::itemRemovedEvent(ExtenderItem *item)
00237 {
00238 d->layout->removeItem(item);
00239
00240 if (d->spacerWidget) {
00241 d->layout->removeItem(d->spacerWidget);
00242 delete d->spacerWidget;
00243 d->spacerWidget = 0;
00244 }
00245
00246
00247 d->updateEmptyExtenderLabel();
00248 d->adjustSizeHints();
00249 }
00250
00251 void Extender::itemHoverEnterEvent(ExtenderItem *item)
00252 {
00253 itemHoverMoveEvent(item, QPointF(0, 0));
00254 }
00255
00256 void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
00257 {
00258 if (d->spacerWidget && d->spacerWidget->geometry().contains(pos)) {
00259 return;
00260 }
00261
00262
00263 if (d->spacerWidget) {
00264 d->layout->removeItem(d->spacerWidget);
00265 }
00266
00267 int insertIndex = d->insertIndexFromPos(pos);
00268
00269
00270 if (!d->spacerWidget) {
00271 Spacer *widget = new Spacer(this);
00272 widget->setMinimumSize(item->minimumSize());
00273 widget->setPreferredSize(item->preferredSize());
00274 widget->setMaximumSize(item->maximumSize());
00275 widget->setSizePolicy(item->sizePolicy());
00276 d->spacerWidget = widget;
00277 }
00278 d->layout->insertItem(insertIndex, d->spacerWidget);
00279
00280
00281 d->updateEmptyExtenderLabel();
00282 d->adjustSizeHints();
00283 }
00284
00285 void Extender::itemHoverLeaveEvent(ExtenderItem *item)
00286 {
00287 Q_UNUSED(item);
00288
00289 if (d->spacerWidget) {
00290
00291 d->layout->removeItem(d->spacerWidget);
00292 delete d->spacerWidget;
00293 d->spacerWidget = 0;
00294
00295 d->currentSpacerIndex = -1;
00296
00297 d->updateEmptyExtenderLabel();
00298 d->adjustSizeHints();
00299 }
00300 }
00301
00302 FrameSvg::EnabledBorders Extender::enabledBordersForItem(ExtenderItem *item) const
00303 {
00304 if (d->layout->count() < 1) {
00305 return 0;
00306 }
00307
00308 ExtenderItem *topItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(0));
00309 ExtenderItem *bottomItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(d->layout->count() - 1));
00310 if (d->appearance == TopDownStacked && bottomItem != item) {
00311 return FrameSvg::LeftBorder | FrameSvg::BottomBorder | FrameSvg::RightBorder;
00312 } else if (d->appearance == BottomUpStacked && topItem != item) {
00313 return FrameSvg::LeftBorder | FrameSvg::TopBorder | FrameSvg::RightBorder;
00314 } else if (d->appearance != NoBorders) {
00315 return FrameSvg::LeftBorder | FrameSvg::RightBorder;
00316 } else {
00317 return 0;
00318 }
00319 }
00320
00321 ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
00322 q(extender),
00323 applet(applet),
00324 currentSpacerIndex(-1),
00325 spacerWidget(0),
00326 emptyExtenderMessage(QString()),
00327 emptyExtenderLabel(0),
00328 appearance(Extender::NoBorders)
00329 {
00330 }
00331
00332 ExtenderPrivate::~ExtenderPrivate()
00333 {
00334 }
00335
00336 void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
00337 {
00338 attachedExtenderItems.append(item);
00339 q->itemHoverLeaveEvent(item);
00340 q->itemAddedEvent(item, pos);
00341 updateBorders();
00342 emit q->itemAttached(item);
00343 }
00344
00345 void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
00346 {
00347 attachedExtenderItems.removeOne(item);
00348
00349
00350 if (!q->attachedItems().count()) {
00351 PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet);
00352 if (popupApplet) {
00353 popupApplet->hidePopup();
00354 }
00355 }
00356
00357 q->itemRemovedEvent(item);
00358 updateBorders();
00359 }
00360
00361 int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
00362 {
00363 int insertIndex = -1;
00364
00365
00366 if (pos != QPointF(-1, -1)) {
00367 for (int i = 0; i < layout->count(); ++i) {
00368 QRectF siblingGeometry = layout->itemAt(i)->geometry();
00369 qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
00370 if (pos.y() < middle) {
00371 insertIndex = i;
00372 break;
00373 } else if (pos.y() <= siblingGeometry.bottom()) {
00374 insertIndex = i + 1;
00375 break;
00376 }
00377 }
00378 }
00379
00380 return insertIndex;
00381 }
00382
00383 void ExtenderPrivate::loadExtenderItems()
00384 {
00385 KConfigGroup cg = applet->config("ExtenderItems");
00386
00387
00388
00389 QList<QPair<int, QString> > groupList;
00390 foreach (const QString &extenderItemId, cg.groupList()) {
00391 KConfigGroup dg = cg.group(extenderItemId);
00392 groupList.append(qMakePair(dg.readEntry("extenderItemPosition", 0), extenderItemId));
00393 }
00394 qSort(groupList);
00395
00396
00397 for (int i = 0; i < groupList.count(); i++) {
00398 QPair<int, QString> pair = groupList[i];
00399
00400 KConfigGroup dg = cg.group(pair.second);
00401
00402
00403 QString extenderItemId = dg.name();
00404 QString extenderItemName = dg.readEntry("extenderItemName", "");
00405 QString appletName = dg.readEntry("sourceAppletPluginName", "");
00406 uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
00407
00408 bool temporarySourceApplet = false;
00409
00410 kDebug() << "applet id = " << applet->id();
00411 kDebug() << "sourceappletid = " << sourceAppletId;
00412
00413
00414 Corona *corona = applet->containment()->corona();
00415 Applet *sourceApplet = 0;
00416 foreach (Containment *containment, corona->containments()) {
00417 foreach (Applet *applet, containment->applets()) {
00418 if (applet->id() == sourceAppletId) {
00419 sourceApplet = applet;
00420 }
00421 }
00422 }
00423
00424
00425
00426 if (!sourceApplet) {
00427 kDebug() << "creating a temporary applet as factory";
00428 sourceApplet = Applet::load(appletName);
00429 temporarySourceApplet = true;
00430
00431
00432 }
00433
00434 if (!sourceApplet) {
00435 kDebug() << "sourceApplet is null? appletName = " << appletName;
00436 kDebug() << " extenderItemId = " << extenderItemId;
00437 } else {
00438 ExtenderItem *item = new ExtenderItem(q, extenderItemId.toInt());
00439 sourceApplet->initExtenderItem(item);
00440
00441 if (temporarySourceApplet) {
00442 delete sourceApplet;
00443 }
00444 }
00445 }
00446 }
00447
00448 void ExtenderPrivate::updateBorders()
00449 {
00450 foreach (ExtenderItem *item, q->attachedItems()) {
00451
00452
00453 if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
00454
00455
00456 item->d->themeChanged();
00457 }
00458 }
00459 }
00460
00461 void ExtenderPrivate::adjustSizeHints()
00462 {
00463
00464
00465
00466 if (layout) {
00467 layout->updateGeometry();
00468 q->setMinimumSize(layout->preferredSize());
00469 }
00470
00471 q->adjustSize();
00472
00473 emit q->geometryChanged();
00474 }
00475
00476 void ExtenderPrivate::updateEmptyExtenderLabel()
00477 {
00478 if (q->attachedItems().isEmpty() && !emptyExtenderLabel && !emptyExtenderMessage.isEmpty()
00479 && !spacerWidget ) {
00480
00481 emptyExtenderLabel = new Label(q);
00482 emptyExtenderLabel->setAlignment(Qt::AlignCenter);
00483 emptyExtenderLabel->setText(emptyExtenderMessage);
00484 emptyExtenderLabel->setMinimumSize(QSizeF(150, 64));
00485 emptyExtenderLabel->setPreferredSize(QSizeF(200, 64));
00486 emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00487 layout->addItem(emptyExtenderLabel);
00488 } else if (!q->attachedItems().isEmpty() && emptyExtenderLabel) {
00489
00490 layout->removeItem(emptyExtenderLabel);
00491 delete emptyExtenderLabel;
00492 emptyExtenderLabel = 0;
00493 }
00494 }
00495
00496 }
00497
00498 #include "extender.moc"