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"
KDE 4.6 API Reference