• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

extender.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin St, Fifth Floor,
00017  * Boston, MA  02110-1301  USA
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 //TODO: copied from panel containment. We'll probably want a spacer widget in libplasma for 4.3.
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     //At multiple places in the extender code, we make the assumption that an applet doesn't have
00078     //more then one extender. If a second extender is created, destroy the first one to avoid leaks.
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     //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
00123     //iterate through all extenders we can find and check each extenders source applet.
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     //this is a sane size policy imo.
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     //remove the empty extender message if needed.
00226     d->updateEmptyExtenderLabel();
00227 
00228     //if the item doesn't got a widget one will be set real soon, causing it's sizehints to
00229     //change. Don't adjust the size hints in that case, since that spares us a call to
00230     //adjustSizeHints()
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     //add the empty extender message if needed.
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     //Make sure we remove any spacer that might already be in the layout.
00263     if (d->spacerWidget) {
00264         d->layout->removeItem(d->spacerWidget);
00265     }
00266 
00267     int insertIndex = d->insertIndexFromPos(pos);
00268 
00269     //Create a widget that functions as spacer, and add that to the layout.
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     //Make sure we remove any 'no detachables' label that might be there, and update the layout.
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         //Remove any trace of the spacer widget.
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     //collapse the popupapplet if the last item is removed.
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     //XXX: duplicated from panel
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     //first create a list of extenderItems, and then sort them on their position, so the items get
00388     //recreated in the correct order.
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     //iterate over the extender items
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         //load the relevant settings.
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         //find the source applet.
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         //There is no source applet. We just instantiate one just for the sake of creating
00425         //detachables.
00426         if (!sourceApplet) {
00427             kDebug() << "creating a temporary applet as factory";
00428             sourceApplet = Applet::load(appletName);
00429             temporarySourceApplet = true;
00430             //TODO: maybe add an option to applet to indicate that it shouldn't be deleted after
00431             //having used it as factory.
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         //kDebug() << "checking" << (QObject*)item << item->d->background->enabledBorders()
00452         //         << q->enabledBordersForItem(item);
00453         if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
00454             //call themeChanged to change the backgrounds enabled borders, and move all contained
00455             //widgets according to it's changed margins.
00456             item->d->themeChanged();
00457         }
00458     }
00459 }
00460 
00461 void ExtenderPrivate::adjustSizeHints()
00462 {
00463     //FIXME: what happens in this function are some nasty workarounds for a bug in qt4.4's QGL.
00464     //Alexis has told me they are working on a fix for qt4.5, so this can be removed once the bug
00465     //has been fixed in Qt.
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         //add the empty extender label.
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         //remove the empty extender label.
00490         layout->removeItem(emptyExtenderLabel);
00491         delete emptyExtenderLabel;
00492         emptyExtenderLabel = 0;
00493     }
00494 }
00495 
00496 } // Plasma namespace
00497 
00498 #include "extender.moc"

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal