• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Plasma

extender.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright 2008, 2009 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 <QDesktopWidget>
00024 #include <QLabel>
00025 #include <QGraphicsSceneDragDropEvent>
00026 #include <QGraphicsGridLayout>
00027 #include <QGraphicsLinearLayout>
00028 #include <QPainter>
00029 
00030 #include "applet.h"
00031 #include "containment.h"
00032 #include "corona.h"
00033 #include "dialog.h"
00034 #include "extendergroup.h"
00035 #include "extenderitem.h"
00036 #include "framesvg.h"
00037 #include "paintutils.h"
00038 #include "popupapplet.h"
00039 #include "svg.h"
00040 #include "theme.h"
00041 #include "widgets/label.h"
00042 #include "widgets/scrollwidget.h"
00043 
00044 #include "private/applet_p.h"
00045 #include "private/applethandle_p.h"
00046 #include "private/extender_p.h"
00047 #include "private/extenderapplet_p.h"
00048 #include "private/extenderitem_p.h"
00049 #include "private/extenderitemmimedata_p.h"
00050 #include "private/popupapplet_p.h"
00051 
00052 namespace Plasma
00053 {
00054 
00055 Spacer::Spacer(QGraphicsItem *parent)
00056     : QGraphicsWidget(parent)
00057 {
00058 }
00059 
00060 Spacer::~Spacer()
00061 {
00062 }
00063 
00064 void Spacer::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 
00073 void Spacer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
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 
00088 Extender::Extender(Applet *applet)
00089         : QGraphicsWidget(applet),
00090           d(new ExtenderPrivate(applet, this))
00091 {
00092     //At multiple places in the extender code, we make the assumption that an applet doesn't have
00093     //more then one extender. If a second extender is created, destroy the first one to avoid leaks.
00094     if (applet->d->extender) {
00095         kWarning() << "Applet already has an extender, and can have only one extender."
00096                    << "The previous extender will be destroyed.";
00097         delete applet->d->extender.data();
00098     } else if (PopupApplet *popup = qobject_cast<PopupApplet *>(applet)) {
00099         if (!popup->d->graphicsWidget) {
00100             // ensure the popup gets a dialog with us as the graphics widget
00101             popup->d->popupConstraintsEvent(SizeConstraint);
00102         }
00103     }
00104 
00105     applet->d->extender = this;
00106     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00107 
00108     d->scrollWidget = new ScrollWidget(this);
00109     d->scrollWidget->setOverflowBordersVisible(false);
00110     d->scrollWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00111     d->mainWidget = new QGraphicsWidget(d->scrollWidget);
00112     d->scrollWidget->setWidget(d->mainWidget);
00113     d->mainWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
00114     connect(d->scrollWidget, SIGNAL(viewportGeometryChanged(const QRectF &)),
00115             this, SLOT(viewportGeometryChanged(const QRectF &)));
00116 
00117     d->layout = new QGraphicsLinearLayout(d->mainWidget);
00118     d->layout->setOrientation(Qt::Vertical);
00119     d->layout->setContentsMargins(0, 0, 0, 0);
00120     d->layout->setSpacing(0);
00121 
00122 
00123     QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(Qt::Vertical, this);
00124     lay->addItem(d->scrollWidget);
00125     setContentsMargins(0, 0, 0, 0);
00126     lay->setContentsMargins(0, 0, 0, 0);
00127 
00128     d->loadExtenderItems();
00129 
00130     setAcceptDrops(true);
00131 }
00132 
00133 Extender::~Extender()
00134 {
00135     d->destroying = true;
00136 
00137     // when deleting items that are connected to us, it can happen that
00138     // other items which are in groups may get deleted as well. so we first
00139     // build a new list of guarded pointers, and then use that list. that
00140     // way when items are deleted behind our back, we are still safe.
00141     // FIXME: having groups and items in the same collection is probably a mistake,
00142     // so would be a good candidate for a refactoring exercise
00143     QList<QWeakPointer<ExtenderItem> > guardedItems;
00144 
00145     foreach (ExtenderItem *item, d->attachedExtenderItems) {
00146         guardedItems << QWeakPointer<ExtenderItem>(item);
00147     }
00148 
00149     d->attachedExtenderItems.clear();
00150 
00151     foreach (const QWeakPointer<ExtenderItem> &guardedItem, guardedItems) {
00152         ExtenderItem *item = guardedItem.data();
00153         if (item) {
00154             item->disconnect(this);
00155             delete item;
00156         }
00157     }
00158 
00159 
00160     delete d;
00161 }
00162 
00163 void Extender::setEmptyExtenderMessage(const QString &message)
00164 {
00165     d->emptyExtenderMessage = message;
00166 
00167     if (d->emptyExtenderLabel) {
00168         d->emptyExtenderLabel->setText(message);
00169     }
00170 }
00171 
00172 QString Extender::emptyExtenderMessage() const
00173 {
00174     return d->emptyExtenderMessage;
00175 }
00176 
00177 QList<ExtenderItem*> Extender::items() const
00178 {
00179     QList<ExtenderItem*> result;
00180 
00181     //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
00182     //iterate through all extenders we can find and check each extenders source applet.
00183     if (!d->applet) {
00184         return QList<ExtenderItem*>();
00185     }
00186 
00187     Containment *containment = d->applet.data()->containment();
00188     if (!containment) {
00189         return result;
00190     }
00191 
00192     foreach (Containment *c, containment->corona()->containments()) {
00193         foreach (Applet *applet, c->applets()) {
00194             if (applet->d->extender) {
00195                 foreach (ExtenderItem *item, applet->d->extender.data()->d->attachedExtenderItems) {
00196                     if (item->d->sourceApplet == d->applet.data()) {
00197                         result.append(item);
00198                     }
00199                 }
00200             }
00201         }
00202     }
00203 
00204     return result;
00205 }
00206 
00207 QList<ExtenderItem*> Extender::attachedItems() const
00208 {
00209     return d->attachedExtenderItems;
00210 }
00211 
00212 QList<ExtenderItem*> Extender::detachedItems() const
00213 {
00214     QList<ExtenderItem*> result;
00215 
00216     //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
00217     //iterate through all extenders we can find and check each extenders source applet.
00218     if (!d->applet) {
00219         return QList<ExtenderItem*>();
00220     }
00221     Containment *containment = d->applet.data()->containment();
00222     if (!containment) {
00223         return result;
00224     }
00225 
00226     foreach (Containment *c, containment->corona()->containments()) {
00227         foreach (Applet *applet, c->applets()) {
00228             if (applet->d->extender) {
00229                 foreach (ExtenderItem *item, applet->d->extender.data()->attachedItems()) {
00230                     if (item->d->sourceApplet == d->applet.data() && item->isDetached()) {
00231                         result.append(item);
00232                     }
00233                 }
00234             }
00235         }
00236     }
00237 
00238     return result;
00239 }
00240 
00241 ExtenderItem *Extender::item(const QString &name) const
00242 {
00243     // chances are the item is in our own extender, so check there first
00244     foreach (ExtenderItem *item, d->attachedExtenderItems) {
00245         if (item->d->sourceApplet == d->applet.data() && item->name() == name) {
00246             return item;
00247         }
00248     }
00249 
00250     // maybe it's been moved elsewhere, so lets search through the entire tree of elements
00251     //FIXME: a triple nested loop ... ew. there should be a more efficient way to do this
00252     //iterate through all extenders we can find and check each extenders source applet.
00253     if (!d->applet) {
00254         return 0;
00255     }
00256 
00257     Containment *containment = d->applet.data()->containment();
00258     if (!containment) {
00259         return 0;
00260     }
00261 
00262     QList<Containment *> containments;
00263     if (containment->corona()) {
00264         containments = containment->corona()->containments();
00265     } else {
00266         containments << containment;
00267     }
00268 
00269     foreach (Containment *c, containments) {
00270         foreach (Applet *applet, c->applets()) {
00271             if (applet->d->extender) {
00272                 if (applet->d->extender.data() == this) {
00273                     // skip it, we checked it already
00274                     continue;
00275                 }
00276 
00277                 if (!applet->d->extender) {
00278                     continue;
00279                 }
00280 
00281                 foreach (ExtenderItem *item, applet->d->extender.data()->attachedItems()) {
00282                     if (item->d->sourceApplet == d->applet.data() && item->name() == name) {
00283                         return item;
00284                     }
00285                 }
00286             }
00287         }
00288     }
00289 
00290     return 0;
00291 }
00292 
00293 ExtenderGroup *Extender::group(const QString &name) const
00294 {
00295     return qobject_cast<ExtenderGroup*>(item(name));
00296 }
00297 
00298 bool Extender::hasItem(const QString &name) const
00299 {
00300     if (item(name)) {
00301         return true;
00302     }
00303 
00304     if (!d->applet) {
00305         return false;
00306     }
00307 
00308     //if item(name) returns false, that doesn't mean that the item doesn't exist, just that it has
00309     //not been instantiated yet. check to see if there's mention of this item existing in the
00310     //plasma config's section DetachedExtenderItems
00311     Corona *corona = qobject_cast<Corona *>(scene());
00312     if (!corona) {
00313         return false;
00314     }
00315 
00316     KConfigGroup extenderItemGroup(corona->config(), "DetachedExtenderItems");
00317     foreach (const QString &extenderItemId, extenderItemGroup.groupList()) {
00318         KConfigGroup cg = extenderItemGroup.group(extenderItemId);
00319         if (uint(cg.readEntry("sourceAppletId", 0)) == d->applet.data()->id() &&
00320             cg.readEntry("extenderItemName", "") == name &&
00321             cg.readEntry("sourceAppletPluginName", "") == d->applet.data()->pluginName()) {
00322             return true;
00323         }
00324     }
00325 
00326     return false;
00327 }
00328 
00329 void Extender::setAppearance(Appearance appearance)
00330 {
00331     if (d->appearance == appearance) {
00332         return;
00333     }
00334 
00335     d->appearance = appearance;
00336     d->updateBorders();
00337 }
00338 
00339 Extender::Appearance Extender::appearance() const
00340 {
00341     return d->appearance;
00342 }
00343 
00344 QList<ExtenderGroup*> Extender::groups() const
00345 {
00346     QList<ExtenderGroup *> result;
00347     foreach (ExtenderItem *item, d->attachedExtenderItems) {
00348         if (item->isGroup() && !result.contains(item->group())) {
00349            ExtenderGroup *group = qobject_cast<ExtenderGroup *>(item);
00350            if (group) {
00351                result.append(group);
00352            }
00353         }
00354     }
00355     return result;
00356 }
00357 
00358 Applet *Extender::applet() const
00359 {
00360     return d->applet.data();
00361 }
00362 
00363 void Extender::saveState()
00364 {
00365     foreach (ExtenderItem *item, attachedItems()) {
00366         item->config().writeEntry("extenderItemPosition", item->geometry().y());
00367     }
00368 }
00369 
00370 QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
00371 {
00372     if (change == QGraphicsItem::ItemPositionHasChanged) {
00373         emit geometryChanged();
00374     }
00375 
00376     return QGraphicsWidget::itemChange(change, value);
00377 }
00378 
00379 void Extender::resizeEvent(QGraphicsSceneResizeEvent *event)
00380 {
00381     QGraphicsWidget::resizeEvent(event);
00382     emit geometryChanged();
00383 }
00384 
00385 void Extender::mousePressEvent(QGraphicsSceneMouseEvent *event)
00386 {
00387     Q_UNUSED(event)
00388 
00389     if (!d->applet) {
00390         return;
00391     }
00392 
00393     PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
00394     if (isEmpty() && popupApplet) {
00395         popupApplet->hidePopup();
00396     }
00397 }
00398 
00399 void Extender::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00400 {
00401     if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00402         event->accept();
00403 
00404         const ExtenderItemMimeData *mimeData =
00405             qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00406 
00407         if (mimeData) {
00408             itemHoverEnterEvent(mimeData->extenderItem());
00409 
00410             PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
00411             if (popupApplet) {
00412                 popupApplet->showPopup();
00413             }
00414         }
00415     }
00416 }
00417 
00418 void Extender::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00419 {
00420     if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00421         const ExtenderItemMimeData *mimeData =
00422             qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00423 
00424         if (mimeData) {
00425             itemHoverMoveEvent(mimeData->extenderItem(), event->pos());
00426 
00427             d->setPositionFromDragPosition(event->scenePos());
00428         }
00429     }
00430 }
00431 
00432 void Extender::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
00433 {
00434     if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00435         const ExtenderItemMimeData *mimeData =
00436             qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00437 
00438         if (mimeData) {
00439             itemHoverLeaveEvent(mimeData->extenderItem());
00440 
00441             //Some logic to conveniently show/hide popups or applets when logical.
00442             Extender *sourceExtender = mimeData->extenderItem()->d->extender;
00443 
00444             //Hide popups when they're not the extender where we started, and we're leaving the
00445             //extender. Use a small timeout here, to avoid accidental hides of extenders we're
00446             //targetting.
00447             PopupApplet *popupApplet = qobject_cast<PopupApplet*>(d->applet.data());
00448             if (popupApplet && sourceExtender != this) {
00449                 kDebug() << "leaving another extender then the extender we started, so hide the popup.";
00450                 popupApplet->showPopup(250);
00451             }
00452 
00453             //Hide popups when we drag the last item away.
00454             if (popupApplet && sourceExtender == this && (attachedItems().count() < 2)) {
00455                 kDebug() << "leaving the extender, and there are no more attached items so hide the popup.";
00456                 popupApplet->hidePopup();
00457             }
00458 
00459             //Hide empty internal extender containers when we drag the last item away. Avoids having
00460             //an ugly empty applet on the desktop temporarily.
00461             ExtenderApplet *extenderApplet = qobject_cast<ExtenderApplet*>(d->applet.data());
00462             if (extenderApplet && sourceExtender == this && attachedItems().count() < 2 &&
00463                 extenderApplet->formFactor() != Plasma::Horizontal &&
00464                 extenderApplet->formFactor() != Plasma::Vertical) {
00465                 kDebug() << "leaving the internal extender container, so hide the applet and it's handle.";
00466                 extenderApplet->hide();
00467                 AppletHandle *handle = dynamic_cast<AppletHandle*>(extenderApplet->parentItem());
00468                 if (handle) {
00469                     handle->hide();
00470                 }
00471             }
00472         }
00473     }
00474 }
00475 
00476 void Extender::dropEvent(QGraphicsSceneDragDropEvent *event)
00477 {
00478     if (event->mimeData()->hasFormat(ExtenderItemMimeData::mimeType())) {
00479         const ExtenderItemMimeData *mimeData =
00480             qobject_cast<const ExtenderItemMimeData*>(event->mimeData());
00481 
00482         if (mimeData) {
00483             mimeData->extenderItem()->setExtender(this, event->pos());
00484             QApplication::restoreOverrideCursor();
00485             itemHoverLeaveEvent(0);
00486         }
00487     }
00488 }
00489 
00490 void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
00491 {
00492     ExtenderGroup *group = item->isGroup() ? static_cast<ExtenderGroup*>(item) : 0;
00493     if (group && group->autoHide() && group->items().isEmpty()) {
00494         return;
00495     }
00496 
00497     if (!item->group()) {
00498         if (pos == QPointF(-1, -1)) {
00499             //if it was already there, reposition
00500             d->layout->removeItem(item);
00501             //if just plain adding an item, add it at a sane position:
00502             if (appearance() == BottomUpStacked) {
00503                 //at the top
00504                 d->layout->insertItem(0, item);
00505             } else {
00506                 //at the bottom
00507                 d->layout->addItem(item);
00508             }
00509         } else {
00510             kDebug() << "inserting at" << pos << d->insertIndexFromPos(pos) << item->size();
00511             d->layout->insertItem(d->insertIndexFromPos(pos), item);
00512             kDebug() << item->size();
00513         }
00514     }
00515 
00516     d->adjustMinimumSize();
00517 
00518     //remove the empty extender message if needed.
00519     d->updateEmptyExtenderLabel();
00520     d->updateBorders();
00521 
00522     d->adjustSize();
00523 }
00524 
00525 void Extender::itemRemovedEvent(ExtenderItem *item)
00526 {
00527     if (d->destroying) {
00528         return;
00529     }
00530 
00531     d->layout->removeItem(item);
00532 
00533     if (d->spacerWidget) {
00534         d->layout->removeItem(d->spacerWidget);
00535         delete d->spacerWidget;
00536         d->spacerWidget = 0;
00537     }
00538 
00539     d->adjustMinimumSize();
00540 
00541     //add the empty extender message if needed.
00542     d->updateEmptyExtenderLabel();
00543     d->updateBorders();
00544 
00545     d->layout->updateGeometry();
00546     static_cast<QGraphicsLayoutItem *>(d->scrollWidget)->updateGeometry();
00547     updateGeometry();
00548 
00549     d->adjustSize();
00550 }
00551 
00552 void Extender::itemHoverEnterEvent(ExtenderItem *item)
00553 {
00554     itemHoverMoveEvent(item, QPointF(0, 0));
00555 }
00556 
00557 void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
00558 {
00559     if (d->spacerWidget && d->spacerWidget->geometry().contains(pos)) {
00560         return;
00561     }
00562 
00563     //Make sure we remove any spacer that might already be in the layout.
00564     if (d->spacerWidget) {
00565         d->layout->removeItem(d->spacerWidget);
00566     }
00567 
00568     int insertIndex = d->insertIndexFromPos(pos);
00569 
00570     //Create a widget that functions as spacer, and add that to the layout.
00571     if (!d->spacerWidget) {
00572         Spacer *widget = new Spacer(this);
00573         qreal left, top, right, bottom;
00574         d->background->getMargins(left, top, right, bottom);
00575         widget->setMargins(left, 4, right, 4);
00576 
00577         widget->setMinimumSize(item->minimumSize());
00578         widget->setPreferredSize(item->preferredSize());
00579         widget->setMaximumSize(item->maximumSize());
00580         widget->setSizePolicy(item->sizePolicy());
00581         d->spacerWidget = widget;
00582     }
00583     d->layout->insertItem(insertIndex, d->spacerWidget);
00584 
00585     //Make sure we remove any 'no detachables' label that might be there, and update the layout.
00586     d->updateEmptyExtenderLabel();
00587 }
00588 
00589 void Extender::itemHoverLeaveEvent(ExtenderItem *item)
00590 {
00591     Q_UNUSED(item);
00592 
00593     if (d->spacerWidget) {
00594         //Remove any trace of the spacer widget.
00595         d->layout->removeItem(d->spacerWidget);
00596         delete d->spacerWidget;
00597         d->spacerWidget = 0;
00598 
00599         d->currentSpacerIndex = -1;
00600 
00601         d->updateEmptyExtenderLabel();
00602     }
00603 }
00604 
00605 FrameSvg::EnabledBorders Extender::enabledBordersForItem(ExtenderItem *item) const
00606 {
00607     if (d->layout->count() < 1) {
00608         return 0;
00609     }
00610 
00611     ExtenderItem *topItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(0));
00612     ExtenderItem *bottomItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(d->layout->count() - 1));
00613 
00614     FrameSvg::EnabledBorders borders = FrameSvg::NoBorder;
00615 
00616     if (item->group()) {
00617         return FrameSvg::NoBorder;
00618     } else if (d->appearance == TopDownStacked && bottomItem != item) {
00619         borders = FrameSvg::LeftBorder | FrameSvg::BottomBorder | FrameSvg::RightBorder;
00620     } else if (d->appearance == BottomUpStacked && topItem != item) {
00621         borders =  FrameSvg::LeftBorder | FrameSvg::TopBorder | FrameSvg::RightBorder;
00622     } else if (d->appearance != NoBorders) {
00623         borders = FrameSvg::LeftBorder | FrameSvg::RightBorder;
00624     } else {
00625         return FrameSvg::NoBorder;
00626     }
00627 
00628     if (d->scrollWidget->viewportGeometry().height() < d->mainWidget->boundingRect().height()) {
00629         if (QApplication::layoutDirection() == Qt::RightToLeft) {
00630             borders &= ~FrameSvg::LeftBorder;
00631         } else {
00632             borders &= ~FrameSvg::RightBorder;
00633         }
00634     }
00635 
00636     //someone (i.e. a Dialog) told the extender to disable some border?
00637     borders &= ~d->disabledBordersHint;
00638 
00639 
00640     return borders;
00641 }
00642 
00643 ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
00644     q(extender),
00645     applet(applet),
00646     background(new FrameSvg(extender)),
00647     disabledBordersHint(FrameSvg::NoBorder),
00648     currentSpacerIndex(-1),
00649     spacerWidget(0),
00650     emptyExtenderMessage(QString()),
00651     emptyExtenderLabel(0),
00652     appearance(Extender::NoBorders),
00653     destroying(false)
00654 {
00655     background->setImagePath("widgets/extender-background");
00656 }
00657 
00658 ExtenderPrivate::~ExtenderPrivate()
00659 {
00660 }
00661 
00662 void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
00663 {
00664     if (attachedExtenderItems.contains(item)) {
00665         pendingItems.remove(item);
00666         q->itemAddedEvent(item, pos);
00667         return;
00668     }
00669 
00670     QObject::connect(item, SIGNAL(destroyed(Plasma::ExtenderItem*)), q, SLOT(extenderItemDestroyed(Plasma::ExtenderItem*)));
00671     attachedExtenderItems.append(item);
00672     q->itemHoverLeaveEvent(item);
00673     pendingItems.insert(item, pos);
00674     QTimer::singleShot(0, q, SLOT(delayItemAddedEvent()));
00675 }
00676 
00677 void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
00678 {
00679     attachedExtenderItems.removeAll(item);
00680     pendingItems.remove(item);
00681 
00682     //collapse the popupapplet if the last item is removed.
00683     if (attachedExtenderItems.isEmpty()) {
00684         PopupApplet *popupApplet = qobject_cast<PopupApplet*>(applet.data());
00685         if (popupApplet) {
00686             popupApplet->hidePopup();
00687         }
00688     }
00689 
00690     q->itemRemovedEvent(item);
00691 }
00692 
00693 int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
00694 {
00695     int insertIndex = -1;
00696 
00697     //XXX: duplicated from panel
00698     if (pos != QPointF(-1, -1)) {
00699         for (int i = 0; i < layout->count(); ++i) {
00700             QRectF siblingGeometry = layout->itemAt(i)->geometry();
00701             qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
00702             if (pos.y() < middle) {
00703                 insertIndex = i;
00704                 break;
00705             } else if (pos.y() <= siblingGeometry.bottom()) {
00706                 insertIndex = i + 1;
00707                 break;
00708             }
00709         }
00710     }
00711 
00712     return insertIndex;
00713 }
00714 
00715 void ExtenderPrivate::loadExtenderItems()
00716 {
00717     if (!applet) {
00718         return;
00719     }
00720 
00721     KConfigGroup cg = applet.data()->config("ExtenderItems");
00722 
00723     //first create a list of extenderItems, and then sort them on their position, so the items get
00724     //recreated in the correct order.
00725     QList<QPair<int, QString> > groupList;
00726     foreach (const QString &extenderItemId, cg.groupList()) {
00727         KConfigGroup dg = cg.group(extenderItemId);
00728         groupList.append(qMakePair(dg.readEntry("extenderItemPosition", 0), extenderItemId));
00729     }
00730     qSort(groupList);
00731 
00732     //iterate over the extender items
00733     for (int i = 0; i < groupList.count(); i++) {
00734         QPair<int, QString> pair = groupList[i];
00735 
00736         KConfigGroup dg = cg.group(pair.second);
00737 
00738         //load the relevant settings.
00739         QString extenderItemId = dg.name();
00740         QString extenderItemName = dg.readEntry("extenderItemName", "");
00741         QString appletName = dg.readEntry("sourceAppletPluginName", "");
00742         uint sourceAppletId = dg.readEntry("sourceAppletId", 0);
00743 
00744         bool temporarySourceApplet = false;
00745 
00746         kDebug() << "applet id = " << applet.data()->id();
00747         kDebug() << "sourceappletid = " << sourceAppletId;
00748 
00749         //find the source applet.
00750         Applet *sourceApplet = 0;
00751         if (applet.data()->id() == sourceAppletId) {
00752             // it's ours!
00753             sourceApplet = applet.data();
00754         } else {
00755             // maybe it's foreign?
00756             Containment *containment = applet.data()->containment();
00757 
00758             if (containment) {
00759                 Corona *corona = containment->corona();
00760 
00761                 if (sourceAppletId == q->applet()->id()) {
00762                     sourceApplet = q->applet();
00763                 } else {
00764                     foreach (Containment *containment, corona->containments()) {
00765                         foreach (Applet *applet, containment->applets()) {
00766                             if (applet->id() == sourceAppletId) {
00767                                 sourceApplet = applet;
00768                             }
00769                         }
00770                     }
00771                 }
00772             }
00773         }
00774 
00775         //There is no source applet. We just instantiate one just for the sake of creating
00776         //detachables.
00777         if (!sourceApplet) {
00778             kDebug() << "creating a temporary applet as factory";
00779             sourceApplet = Applet::load(appletName);
00780             temporarySourceApplet = true;
00781             //TODO: maybe add an option to applet to indicate that it shouldn't be deleted after
00782             //having used it as factory.
00783         }
00784 
00785         if (!sourceApplet) {
00786             kDebug() << "sourceApplet is null? appletName = " << appletName;
00787             kDebug() << "                      extenderItemId = " << extenderItemId;
00788         } else {
00789             ExtenderItem *item;
00790             if (dg.readEntry("isGroup", false)) {
00791                 item = new ExtenderGroup(q, extenderItemId.toInt());
00792             } else {
00793                 item = new ExtenderItem(q, extenderItemId.toInt());
00794             }
00795             sourceApplet->initExtenderItem(item);
00796             item->d->sourceApplet = sourceApplet;
00797 
00798             if (temporarySourceApplet) {
00799                 delete sourceApplet;
00800             }
00801         }
00802     }
00803 }
00804 
00805 void ExtenderPrivate::updateBorders()
00806 {
00807     foreach (ExtenderItem *item, attachedExtenderItems) {
00808         if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
00809             //call themeChanged to change the backgrounds enabled borders, and move all contained
00810             //widgets according to it's changed margins.
00811             item->d->themeChanged();
00812         }
00813     }
00814 }
00815 
00816 void ExtenderPrivate::delayItemAddedEvent()
00817 {
00818     QHash<Plasma::ExtenderItem *, QPointF>::const_iterator i = pendingItems.constBegin();
00819     while (i != pendingItems.constEnd()) {
00820         q->itemAddedEvent(i.key(), i.value());
00821         ++i;
00822     }
00823     pendingItems.clear();
00824 }
00825 
00826 void ExtenderPrivate::updateEmptyExtenderLabel()
00827 {
00828     if (q->isEmpty() && !emptyExtenderLabel &&
00829         !emptyExtenderMessage.isEmpty() && !spacerWidget ) {
00830         //add the empty extender label.
00831         emptyExtenderLabel = new Label(q);
00832         emptyExtenderLabel->setAlignment(Qt::AlignCenter);
00833         emptyExtenderLabel->setText(emptyExtenderMessage);
00834 
00835         qreal left, top, right, bottom;
00836         background->getMargins(left, top, right, bottom);
00837         emptyExtenderLabel->nativeWidget()->setMargin(left + right);
00838 
00839         emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00840         layout->addItem(emptyExtenderLabel);
00841     } else if (!q->isEmpty() && emptyExtenderLabel) {
00842         //remove the empty extender label.
00843         layout->removeItem(emptyExtenderLabel);
00844         delete emptyExtenderLabel;
00845         emptyExtenderLabel = 0;
00846     }
00847 }
00848 
00849 void ExtenderPrivate::adjustMinimumSize()
00850 {
00851     //FIXME: hardcoded number for the scrollbar
00852     scrollWidget->setMinimumWidth(mainWidget->effectiveSizeHint(Qt::MinimumSize).width() + 32);
00853     //FIXME: hardcoded number
00854     scrollWidget->setMinimumHeight(qMin((qreal)300, mainWidget->effectiveSizeHint(Qt::MinimumSize).height()));
00855 }
00856 
00857 void ExtenderPrivate::setPositionFromDragPosition(const QPointF &pos)
00858 {
00859     const qreal ratio = (q->mapFromScene(pos).y()/scrollWidget->size().height());
00860 
00861     mainWidget->setPos(mainWidget->pos().x(), 30 + (ratio *(scrollWidget->size().height() - mainWidget->size().height() - 30)));
00862 }
00863 
00864 ExtenderGroup *ExtenderPrivate::findGroup(const QString &name) const
00865 {
00866     foreach (ExtenderItem *item, attachedExtenderItems) {
00867         if (item->isGroup() && item->name() == name) {
00868             return qobject_cast<ExtenderGroup*>(item);
00869         }
00870     }
00871 
00872     return 0;
00873 }
00874 
00875 void ExtenderPrivate::extenderItemDestroyed(Plasma::ExtenderItem *item)
00876 {
00877     pendingItems.remove(item);
00878 
00879     if (attachedExtenderItems.contains(item)) {
00880         // removeExtenderItem also removes the item from attachedExtenderItems
00881         removeExtenderItem(item);
00882     }
00883 }
00884 
00885 void ExtenderPrivate::viewportGeometryChanged(const QRectF &rect)
00886 {
00887     if (appearance != Extender::TopDownStacked && appearance != Extender::BottomUpStacked) {
00888         scrollbarVisible = (rect.height() > mainWidget->boundingRect().height());
00889         return;
00890     }
00891 
00892     bool scroll = !(rect.height() >= mainWidget->boundingRect().height());
00893 
00894     if (scroll != scrollbarVisible) {
00895         scrollbarVisible = scroll;
00896         updateBorders();
00897     }
00898 }
00899 
00900 void ExtenderPrivate::setDisabledBordersHint(const FrameSvg::EnabledBorders borders)
00901 {
00902     if (disabledBordersHint == borders) {
00903         return;
00904     }
00905 
00906     disabledBordersHint = borders;
00907     foreach (Plasma::ExtenderItem *item, attachedExtenderItems) {
00908         item->d->themeChanged();
00909     }
00910 }
00911 
00912 void ExtenderPrivate::adjustSize()
00913 {
00914     QRect screenRect;
00915     QSizeF size = mainWidget->effectiveSizeHint(Qt::PreferredSize);
00916     if (applet && applet.data()->containment() && applet.data()->containment()->corona()) {
00917         screenRect = applet.data()->containment()->corona()->screenGeometry(applet.data()->containment()->screen());
00918     }
00919     q->resize(qMin(screenRect.width()/3, (int)size.width()),
00920               qMin(screenRect.height()/3, (int)size.height()));
00921 }
00922 
00923 bool Extender::isEmpty() const
00924 {
00925     //It's empty if it doesn't have items or has only group that are empty and autohide
00926     foreach (ExtenderItem *item, d->attachedExtenderItems) {
00927         if (!item->isGroup()) {
00928             return false;
00929         } else {
00930             //a static_cast here should be safe, it's not the case apparently
00931             ExtenderGroup *group = qobject_cast<ExtenderGroup *>(item);
00932             if (group && (!group->autoHide() || group->items().size() > 0)) {
00933                 return false;
00934             }
00935         }
00936     }
00937 
00938     return true;
00939 }
00940 
00941 } // Plasma namespace
00942 
00943 #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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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