Plasma
corona.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2007 Matt Broadstone <mbroadst@gmail.com> 00003 * Copyright 2007-2011 Aaron Seigo <aseigo@kde.org> 00004 * Copyright 2007 Riccardo Iaconelli <riccardo@kde.org> 00005 * Copyright (c) 2009 Chani Armitage <chani@kde.org> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU Library General Public License as 00009 * published by the Free Software Foundation; either version 2, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details 00016 * 00017 * You should have received a copy of the GNU Library General Public 00018 * License along with this program; if not, write to the 00019 * Free Software Foundation, Inc., 00020 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include "corona.h" 00024 #include "private/corona_p.h" 00025 00026 #include <QApplication> 00027 #include <QDesktopWidget> 00028 #include <QGraphicsView> 00029 #include <QGraphicsSceneDragDropEvent> 00030 #include <QGraphicsGridLayout> 00031 #include <QMimeData> 00032 #include <QPainter> 00033 #include <QTimer> 00034 00035 #include <cmath> 00036 00037 #include <kaction.h> 00038 #include <kdebug.h> 00039 #include <kglobal.h> 00040 #include <klocale.h> 00041 #include <kmimetype.h> 00042 #include <kshortcutsdialog.h> 00043 #include <kwindowsystem.h> 00044 00045 #include "animator.h" 00046 #include "abstracttoolbox.h" 00047 #include "containment.h" 00048 #include "containmentactionspluginsconfig.h" 00049 #include "view.h" 00050 #include "private/animator_p.h" 00051 #include "private/applet_p.h" 00052 #include "private/containment_p.h" 00053 #include "tooltipmanager.h" 00054 #include "abstractdialogmanager.h" 00055 00056 using namespace Plasma; 00057 00058 namespace Plasma 00059 { 00060 00061 bool CoronaPrivate::s_positioningContainments = false; 00062 00063 Corona::Corona(QObject *parent) 00064 : QGraphicsScene(parent), 00065 d(new CoronaPrivate(this)) 00066 { 00067 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Corona ctor start"; 00068 d->init(); 00069 ToolTipManager::self()->m_corona = this; 00070 //setViewport(new QGLWidget(QGLFormat(QGL::StencilBuffer | QGL::AlphaChannel))); 00071 } 00072 00073 Corona::~Corona() 00074 { 00075 KConfigGroup trans(KGlobal::config(), "PlasmaTransientsConfig"); 00076 trans.deleteGroup(); 00077 00078 // FIXME: Same fix as in Plasma::View - make sure that when the focused widget is 00079 // destroyed we don't try to transfer it to something that's already been 00080 // deleted. 00081 clearFocus(); 00082 delete d; 00083 } 00084 00085 void Corona::setAppletMimeType(const QString &type) 00086 { 00087 d->mimetype = type; 00088 } 00089 00090 QString Corona::appletMimeType() 00091 { 00092 return d->mimetype; 00093 } 00094 00095 void Corona::setDefaultContainmentPlugin(const QString &name) 00096 { 00097 // we could check if it is in: 00098 // Containment::listContainments().contains(name) || 00099 // Containment::listContainments(QString(), KGlobal::mainComponent().componentName()).contains(name) 00100 // but that seems like overkill 00101 d->defaultContainmentPlugin = name; 00102 } 00103 00104 QString Corona::defaultContainmentPlugin() const 00105 { 00106 return d->defaultContainmentPlugin; 00107 } 00108 00109 void Corona::saveLayout(const QString &configName) const 00110 { 00111 KSharedConfigPtr c; 00112 00113 if (configName.isEmpty() || configName == d->configName) { 00114 c = config(); 00115 } else { 00116 c = KSharedConfig::openConfig(configName, KConfig::SimpleConfig); 00117 } 00118 00119 d->saveLayout(c); 00120 } 00121 00122 void Corona::exportLayout(KConfigGroup &config, QList<Containment*> containments) 00123 { 00124 foreach (const QString &group, config.groupList()) { 00125 KConfigGroup cg(&config, group); 00126 cg.deleteGroup(); 00127 } 00128 00129 //temporarily unlock so that removal works 00130 ImmutabilityType oldImm = immutability(); 00131 d->immutability = Mutable; 00132 00133 KConfigGroup dest(&config, "Containments"); 00134 KConfigGroup dummy; 00135 foreach (Plasma::Containment *c, containments) { 00136 c->save(dummy); 00137 c->config().reparent(&dest); 00138 00139 //ensure the containment is unlocked 00140 //this is done directly because we have to bypass any SystemImmutable checks 00141 c->Applet::d->immutability = Mutable; 00142 foreach (Applet *a, c->applets()) { 00143 a->d->immutability = Mutable; 00144 } 00145 00146 c->destroy(false); 00147 } 00148 00149 //restore immutability 00150 d->immutability = oldImm; 00151 00152 config.sync(); 00153 } 00154 00155 void Corona::requestConfigSync() 00156 { 00157 // constant controlling how long between requesting a configuration sync 00158 // and one happening should occur. currently 10 seconds 00159 static const int CONFIG_SYNC_TIMEOUT = 10000; 00160 00161 // TODO: should we check into our immutability before doing this? 00162 00163 //NOTE: this is a pretty simplistic model: we simply save no more than CONFIG_SYNC_TIMEOUT 00164 // after the first time this is called. not much of a heuristic for save points, but 00165 // it should at least compress these activities a bit and provide a way for applet 00166 // authors to ween themselves from the sync() disease. A more interesting/dynamic 00167 // algorithm for determining when to actually sync() to disk might be better, though. 00168 if (!d->configSyncTimer.isActive()) { 00169 d->configSyncTimer.start(CONFIG_SYNC_TIMEOUT); 00170 } 00171 } 00172 00173 void Corona::requireConfigSync() 00174 { 00175 d->syncConfig(); 00176 } 00177 00178 void Corona::initializeLayout(const QString &configName) 00179 { 00180 clearContainments(); 00181 loadLayout(configName); 00182 00183 if (d->containments.isEmpty()) { 00184 loadDefaultLayout(); 00185 if (!d->containments.isEmpty()) { 00186 requestConfigSync(); 00187 } 00188 } 00189 00190 if (config()->isImmutable()) { 00191 setImmutability(SystemImmutable); 00192 } else { 00193 KConfigGroup coronaConfig(config(), "General"); 00194 setImmutability((ImmutabilityType)coronaConfig.readEntry("immutability", (int)Mutable)); 00195 } 00196 } 00197 00198 bool containmentSortByPosition(const Containment *c1, const Containment *c2) 00199 { 00200 return c1->id() < c2->id(); 00201 } 00202 00203 void Corona::layoutContainments() 00204 { 00205 if (CoronaPrivate::s_positioningContainments) { 00206 return; 00207 } 00208 00209 CoronaPrivate::s_positioningContainments = true; 00210 00211 //TODO: we should avoid running this too often; consider compressing requests 00212 // with a timer. 00213 QList<Containment*> c = containments(); 00214 QMutableListIterator<Containment*> it(c); 00215 00216 while (it.hasNext()) { 00217 Containment *containment = it.next(); 00218 if (containment->containmentType() == Containment::PanelContainment || 00219 containment->containmentType() == Containment::CustomPanelContainment || 00220 offscreenWidgets().contains(containment)) { 00221 // weed out all containments we don't care about at all 00222 // e.g. Panels and ourself 00223 it.remove(); 00224 continue; 00225 } 00226 } 00227 00228 qSort(c.begin(), c.end(), containmentSortByPosition); 00229 00230 if (c.isEmpty()) { 00231 CoronaPrivate::s_positioningContainments = false; 00232 return; 00233 } 00234 00235 int column = 0; 00236 int x = 0; 00237 int y = 0; 00238 int rowHeight = 0; 00239 00240 it.toFront(); 00241 while (it.hasNext()) { 00242 Containment *containment = it.next(); 00243 containment->setPos(x, y); 00244 //kDebug() << ++count << "setting to" << x << y; 00245 00246 int height = containment->size().height(); 00247 if (height > rowHeight) { 00248 rowHeight = height; 00249 } 00250 00251 ++column; 00252 00253 if (column == CONTAINMENT_COLUMNS) { 00254 column = 0; 00255 x = 0; 00256 y += rowHeight + INTER_CONTAINMENT_MARGIN + TOOLBOX_MARGIN; 00257 rowHeight = 0; 00258 } else { 00259 x += containment->size().width() + INTER_CONTAINMENT_MARGIN; 00260 } 00261 //kDebug() << "column: " << column << "; x " << x << "; y" << y << "; width was" 00262 // << containment->size().width(); 00263 } 00264 00265 CoronaPrivate::s_positioningContainments = false; 00266 } 00267 00268 00269 void Corona::loadLayout(const QString &configName) 00270 { 00271 if (!configName.isEmpty() && configName != d->configName) { 00272 // if we have a new config name passed in, then use that as the config file for this Corona 00273 d->config = 0; 00274 d->configName = configName; 00275 } 00276 00277 KSharedConfigPtr conf = config(); 00278 d->importLayout(*conf, false); 00279 } 00280 00281 QList<Plasma::Containment *> Corona::importLayout(const KConfigGroup &conf) 00282 { 00283 return d->importLayout(conf, true); 00284 } 00285 00286 #ifndef KDE_NO_DEPRECATED 00287 QList<Plasma::Containment *> Corona::importLayout(const KConfigBase &conf) 00288 { 00289 return d->importLayout(conf, true); 00290 } 00291 #endif 00292 00293 Containment *Corona::containmentForScreen(int screen, int desktop) const 00294 { 00295 foreach (Containment *containment, d->containments) { 00296 if (containment->screen() == screen && 00297 (desktop < 0 || containment->desktop() == desktop) && 00298 (containment->containmentType() == Containment::DesktopContainment || 00299 containment->containmentType() == Containment::CustomContainment)) { 00300 return containment; 00301 } 00302 } 00303 00304 return 0; 00305 } 00306 00307 Containment *Corona::containmentForScreen(int screen, int desktop, 00308 const QString &defaultPluginIfNonExistent, const QVariantList &defaultArgs) 00309 { 00310 Containment *containment = containmentForScreen(screen, desktop); 00311 if (!containment && !defaultPluginIfNonExistent.isEmpty()) { 00312 // screen requests are allowed to bypass immutability 00313 if (screen >= 0 && screen < numScreens() && 00314 desktop >= -1 && desktop < KWindowSystem::numberOfDesktops()) { 00315 containment = d->addContainment(defaultPluginIfNonExistent, defaultArgs, 0, false); 00316 if (containment) { 00317 containment->setScreen(screen, desktop); 00318 } 00319 } 00320 } 00321 00322 return containment; 00323 } 00324 00325 QList<Containment*> Corona::containments() const 00326 { 00327 return d->containments; 00328 } 00329 00330 void Corona::clearContainments() 00331 { 00332 foreach (Containment *containment, d->containments) { 00333 containment->clearApplets(); 00334 } 00335 } 00336 00337 KSharedConfigPtr Corona::config() const 00338 { 00339 if (!d->config) { 00340 d->config = KSharedConfig::openConfig(d->configName, KConfig::SimpleConfig); 00341 } 00342 00343 return d->config; 00344 } 00345 00346 Containment *Corona::addContainment(const QString &name, const QVariantList &args) 00347 { 00348 if (d->immutability == Mutable) { 00349 return d->addContainment(name, args, 0, false); 00350 } 00351 00352 return 0; 00353 } 00354 00355 Containment *Corona::addContainmentDelayed(const QString &name, const QVariantList &args) 00356 { 00357 if (d->immutability == Mutable) { 00358 return d->addContainment(name, args, 0, true); 00359 } 00360 00361 return 0; 00362 } 00363 00364 void Corona::mapAnimation(Animator::Animation from, Animator::Animation to) 00365 { 00366 AnimatorPrivate::mapAnimation(from, to); 00367 } 00368 00369 void Corona::mapAnimation(Animator::Animation from, const QString &to) 00370 { 00371 AnimatorPrivate::mapAnimation(from, to); 00372 } 00373 00374 void Corona::addOffscreenWidget(QGraphicsWidget *widget) 00375 { 00376 foreach (QGraphicsWidget *w, d->offscreenWidgets) { 00377 if (w == widget) { 00378 kDebug() << "widget is already an offscreen widget!"; 00379 return; 00380 } 00381 } 00382 00383 //search for an empty spot in the topleft quadrant of the scene. each 'slot' is QWIDGETSIZE_MAX 00384 //x QWIDGETSIZE_MAX, so we're guaranteed to never have to move widgets once they're placed here. 00385 int i = 0; 00386 while (d->offscreenWidgets.contains(i)) { 00387 i++; 00388 } 00389 00390 d->offscreenWidgets[i] = widget; 00391 widget->setPos((-i - 1) * QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX); 00392 00393 QGraphicsWidget *pw = widget->parentWidget(); 00394 widget->setParentItem(0); 00395 if (pw) { 00396 widget->setParent(pw); 00397 } 00398 00399 //kDebug() << "adding offscreen widget at slot " << i; 00400 if (!widget->scene()) { 00401 addItem(widget); 00402 } 00403 00404 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(offscreenWidgetDestroyed(QObject*))); 00405 } 00406 00407 void Corona::removeOffscreenWidget(QGraphicsWidget *widget) 00408 { 00409 QMutableHashIterator<uint, QGraphicsWidget *> it(d->offscreenWidgets); 00410 00411 while (it.hasNext()) { 00412 if (it.next().value() == widget) { 00413 it.remove(); 00414 return; 00415 } 00416 } 00417 } 00418 00419 QList <QGraphicsWidget *> Corona::offscreenWidgets() const 00420 { 00421 return d->offscreenWidgets.values(); 00422 } 00423 00424 void CoronaPrivate::offscreenWidgetDestroyed(QObject *o) 00425 { 00426 // at this point, it's just a QObject, not a QGraphicsWidget, but we still need 00427 // a pointer of the appropriate type. 00428 // WARNING: DO NOT USE THE WIDGET POINTER FOR ANYTHING OTHER THAN POINTER COMPARISONS 00429 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(o); 00430 q->removeOffscreenWidget(widget); 00431 } 00432 00433 int Corona::numScreens() const 00434 { 00435 return 1; 00436 } 00437 00438 QRect Corona::screenGeometry(int id) const 00439 { 00440 Q_UNUSED(id); 00441 QGraphicsView *v = views().value(0); 00442 if (v) { 00443 QRect r = sceneRect().toRect(); 00444 r.moveTo(v->mapToGlobal(QPoint(0, 0))); 00445 return r; 00446 } 00447 00448 return sceneRect().toRect(); 00449 } 00450 00451 QRegion Corona::availableScreenRegion(int id) const 00452 { 00453 return QRegion(screenGeometry(id)); 00454 } 00455 00456 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s) 00457 { 00458 return popupPosition(item, s, Qt::AlignLeft); 00459 } 00460 00461 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s, Qt::AlignmentFlag alignment) 00462 { 00463 // TODO: merge both methods (also these in Applet) into one (with optional alignment) when we can break compatibility 00464 // TODO: add support for more flags in the future? 00465 00466 const QGraphicsItem *actualItem = item; 00467 00468 const QGraphicsView *v = viewFor(item); 00469 00470 if (!v) { 00471 return QPoint(0, 0); 00472 } 00473 00474 //its own view could be hidden, for instance if item is in an hidden Dialog 00475 //try to position it using the parent applet as the item 00476 if (!v->isVisible()) { 00477 actualItem = item->parentItem(); 00478 if (!actualItem) { 00479 const QGraphicsWidget *widget = qgraphicsitem_cast<const QGraphicsWidget*>(item); 00480 if (widget) { 00481 actualItem = qobject_cast<QGraphicsItem*>(widget->parent()); 00482 } 00483 } 00484 00485 //kDebug() << actualItem; 00486 00487 if (actualItem) { 00488 v = viewFor(actualItem); 00489 if (!v) { 00490 return QPoint(0, 0); 00491 } 00492 } 00493 } 00494 00495 if (!actualItem) { 00496 actualItem = item; 00497 } 00498 00499 QPoint pos; 00500 QTransform sceneTransform = actualItem->sceneTransform(); 00501 00502 //swap direction if necessary 00503 if (QApplication::isRightToLeft() && alignment != Qt::AlignCenter) { 00504 if (alignment == Qt::AlignRight) { 00505 alignment = Qt::AlignLeft; 00506 } else { 00507 alignment = Qt::AlignRight; 00508 } 00509 } 00510 00511 //if the applet is rotated the popup position has to be un-transformed 00512 if (sceneTransform.isRotating()) { 00513 qreal angle = acos(sceneTransform.m11()); 00514 QTransform newTransform; 00515 QPointF center = actualItem->sceneBoundingRect().center(); 00516 00517 newTransform.translate(center.x(), center.y()); 00518 newTransform.rotateRadians(-angle); 00519 newTransform.translate(-center.x(), -center.y()); 00520 pos = v->mapFromScene(newTransform.inverted().map(actualItem->scenePos())); 00521 } else { 00522 pos = v->mapFromScene(actualItem->scenePos()); 00523 } 00524 00525 pos = v->mapToGlobal(pos); 00526 //kDebug() << "==> position is" << actualItem->scenePos() << v->mapFromScene(actualItem->scenePos()) << pos; 00527 const Plasma::View *pv = qobject_cast<const Plasma::View *>(v); 00528 00529 Plasma::Location loc = Floating; 00530 if (pv && pv->containment()) { 00531 loc = pv->containment()->location(); 00532 } 00533 00534 switch (loc) { 00535 case BottomEdge: 00536 case TopEdge: { 00537 if (alignment == Qt::AlignCenter) { 00538 pos.setX(pos.x() + actualItem->boundingRect().width()/2 - s.width()/2); 00539 } else if (alignment == Qt::AlignRight) { 00540 pos.setX(pos.x() + actualItem->boundingRect().width() - s.width()); 00541 } 00542 00543 if (pos.x() + s.width() > v->geometry().x() + v->geometry().width()) { 00544 pos.setX((v->geometry().x() + v->geometry().width()) - s.width()); 00545 } else { 00546 pos.setX(qMax(pos.x(), v->geometry().left())); 00547 } 00548 break; 00549 } 00550 case LeftEdge: 00551 case RightEdge: { 00552 if (alignment == Qt::AlignCenter) { 00553 pos.setY(pos.y() + actualItem->boundingRect().height()/2 - s.height()/2); 00554 } else if (alignment == Qt::AlignRight) { 00555 pos.setY(pos.y() + actualItem->boundingRect().height() - s.height()); 00556 } 00557 00558 if (pos.y() + s.height() > v->geometry().y() + v->geometry().height()) { 00559 pos.setY((v->geometry().y() + v->geometry().height()) - s.height()); 00560 } else { 00561 pos.setY(qMax(pos.y(), v->geometry().top())); 00562 } 00563 break; 00564 } 00565 default: 00566 if (alignment == Qt::AlignCenter) { 00567 pos.setX(pos.x() + actualItem->boundingRect().width()/2 - s.width()/2); 00568 } else if (alignment == Qt::AlignRight) { 00569 pos.setX(pos.x() + actualItem->boundingRect().width() - s.width()); 00570 } 00571 break; 00572 } 00573 00574 00575 //are we out of screen? 00576 int screen = ((pv && pv->containment()) ? pv->containment()->screen() : -1); 00577 if (screen == -1) { 00578 if (pv) { 00579 screen = pv->screen(); 00580 } else { 00581 // fall back to asking the actual system what screen the view is on 00582 // in the case we are dealing with a non-PlasmaView QGraphicsView 00583 screen = QApplication::desktop()->screenNumber(v); 00584 } 00585 } 00586 00587 QRect screenRect = screenGeometry(screen); 00588 00589 switch (loc) { 00590 case BottomEdge: 00591 pos.setY(v->geometry().y() - s.height()); 00592 break; 00593 case TopEdge: 00594 pos.setY(v->geometry().y() + v->geometry().height()); 00595 break; 00596 case LeftEdge: 00597 pos.setX(v->geometry().x() + v->geometry().width()); 00598 break; 00599 case RightEdge: 00600 pos.setX(v->geometry().x() - s.width()); 00601 break; 00602 default: 00603 if (pos.y() - s.height() > screenRect.top()) { 00604 pos.ry() = pos.y() - s.height(); 00605 } else { 00606 pos.ry() = pos.y() + (int)actualItem->boundingRect().size().height() + 1; 00607 } 00608 } 00609 00610 //kDebug() << "==> rect for" << screen << "is" << screenRect; 00611 00612 if (loc != LeftEdge && pos.x() + s.width() > screenRect.x() + screenRect.width()) { 00613 pos.rx() -= ((pos.x() + s.width()) - (screenRect.x() + screenRect.width())); 00614 } 00615 00616 if (loc != TopEdge && pos.y() + s.height() > screenRect.y() + screenRect.height()) { 00617 pos.ry() -= ((pos.y() + s.height()) - (screenRect.y() + screenRect.height())); 00618 } 00619 00620 pos.rx() = qMax(0, pos.x()); 00621 pos.ry() = qMax(0, pos.y()); 00622 return pos; 00623 } 00624 00625 void Corona::loadDefaultLayout() 00626 { 00627 } 00628 00629 void Corona::setPreferredToolBoxPlugin(const Containment::Type type, const QString &plugin) 00630 { 00631 d->toolBoxPlugins[type] = plugin; 00632 //TODO: react to plugin changes on the fly? still don't see the use case (maybe for laptops that become tablets?) 00633 } 00634 00635 QString Corona::preferredToolBoxPlugin(const Containment::Type type) const 00636 { 00637 return d->toolBoxPlugins.value(type); 00638 } 00639 00640 void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event) 00641 { 00642 QGraphicsScene::dragEnterEvent(event); 00643 } 00644 00645 void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) 00646 { 00647 QGraphicsScene::dragLeaveEvent(event); 00648 } 00649 00650 void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event) 00651 { 00652 QGraphicsScene::dragMoveEvent(event); 00653 } 00654 00655 ImmutabilityType Corona::immutability() const 00656 { 00657 return d->immutability; 00658 } 00659 00660 void Corona::setImmutability(const ImmutabilityType immutable) 00661 { 00662 if (d->immutability == immutable || d->immutability == SystemImmutable) { 00663 return; 00664 } 00665 00666 kDebug() << "setting immutability to" << immutable; 00667 d->immutability = immutable; 00668 d->updateContainmentImmutability(); 00669 //tell non-containments that might care (like plasmaapp or a custom corona) 00670 emit immutabilityChanged(immutable); 00671 00672 //update our actions 00673 QAction *action = d->actions.action("lock widgets"); 00674 if (action) { 00675 if (d->immutability == SystemImmutable) { 00676 action->setEnabled(false); 00677 action->setVisible(false); 00678 } else { 00679 bool unlocked = d->immutability == Mutable; 00680 action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets")); 00681 action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked")); 00682 action->setEnabled(true); 00683 action->setVisible(true); 00684 } 00685 } 00686 00687 if (d->immutability != SystemImmutable) { 00688 KConfigGroup cg(config(), "General"); 00689 00690 // we call the dptr member directly for locked since isImmutable() 00691 // also checks kiosk and parent containers 00692 cg.writeEntry("immutability", (int)d->immutability); 00693 requestConfigSync(); 00694 } 00695 } 00696 00697 QList<Plasma::Location> Corona::freeEdges(int screen) const 00698 { 00699 QList<Plasma::Location> freeEdges; 00700 freeEdges << Plasma::TopEdge << Plasma::BottomEdge 00701 << Plasma::LeftEdge << Plasma::RightEdge; 00702 00703 foreach (Containment *containment, containments()) { 00704 if (containment->screen() == screen && 00705 freeEdges.contains(containment->location())) { 00706 freeEdges.removeAll(containment->location()); 00707 } 00708 } 00709 00710 return freeEdges; 00711 } 00712 00713 QAction *Corona::action(QString name) const 00714 { 00715 return d->actions.action(name); 00716 } 00717 00718 void Corona::addAction(QString name, QAction *action) 00719 { 00720 d->actions.addAction(name, action); 00721 } 00722 00723 KAction* Corona::addAction(QString name) 00724 { 00725 return d->actions.addAction(name); 00726 } 00727 00728 QList<QAction*> Corona::actions() const 00729 { 00730 return d->actions.actions(); 00731 } 00732 00733 void Corona::enableAction(const QString &name, bool enable) 00734 { 00735 QAction *action = d->actions.action(name); 00736 if (action) { 00737 action->setEnabled(enable); 00738 action->setVisible(enable); 00739 } 00740 } 00741 00742 void Corona::updateShortcuts() 00743 { 00744 QMutableListIterator<QWeakPointer<KActionCollection> > it(d->actionCollections); 00745 while (it.hasNext()) { 00746 it.next(); 00747 KActionCollection *collection = it.value().data(); 00748 if (!collection) { 00749 // get rid of KActionCollections that have been deleted behind our backs 00750 it.remove(); 00751 continue; 00752 } 00753 00754 collection->readSettings(); 00755 if (d->shortcutsDlg) { 00756 d->shortcutsDlg.data()->addCollection(collection); 00757 } 00758 } 00759 } 00760 00761 void Corona::addShortcuts(KActionCollection *newShortcuts) 00762 { 00763 d->actionCollections << newShortcuts; 00764 if (d->shortcutsDlg) { 00765 d->shortcutsDlg.data()->addCollection(newShortcuts); 00766 } 00767 } 00768 00769 void Corona::setContainmentActionsDefaults(Containment::Type containmentType, const ContainmentActionsPluginsConfig &config) 00770 { 00771 d->containmentActionsDefaults.insert(containmentType, config); 00772 } 00773 00774 ContainmentActionsPluginsConfig Corona::containmentActionsDefaults(Containment::Type containmentType) 00775 { 00776 return d->containmentActionsDefaults.value(containmentType); 00777 } 00778 00779 void Corona::setDialogManager(AbstractDialogManager *dialogManager) 00780 { 00781 d->dialogManager = dialogManager; 00782 } 00783 00784 AbstractDialogManager *Corona::dialogManager() 00785 { 00786 return d->dialogManager.data(); 00787 } 00788 00789 CoronaPrivate::CoronaPrivate(Corona *corona) 00790 : q(corona), 00791 immutability(Mutable), 00792 mimetype("text/x-plasmoidservicename"), 00793 defaultContainmentPlugin("desktop"), 00794 config(0), 00795 actions(corona) 00796 { 00797 if (KGlobal::hasMainComponent()) { 00798 configName = KGlobal::mainComponent().componentName() + "-appletsrc"; 00799 } else { 00800 configName = "plasma-appletsrc"; 00801 } 00802 } 00803 00804 CoronaPrivate::~CoronaPrivate() 00805 { 00806 qDeleteAll(containments); 00807 } 00808 00809 void CoronaPrivate::init() 00810 { 00811 q->setStickyFocus(true); 00812 configSyncTimer.setSingleShot(true); 00813 QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig())); 00814 00815 //some common actions 00816 actions.setConfigGroup("Shortcuts"); 00817 00818 KAction *lockAction = actions.addAction("lock widgets"); 00819 QObject::connect(lockAction, SIGNAL(triggered(bool)), q, SLOT(toggleImmutability())); 00820 lockAction->setText(i18n("Lock Widgets")); 00821 lockAction->setAutoRepeat(true); 00822 lockAction->setIcon(KIcon("object-locked")); 00823 lockAction->setData(AbstractToolBox::ControlTool); 00824 lockAction->setShortcut(KShortcut("alt+d, l")); 00825 lockAction->setShortcutContext(Qt::ApplicationShortcut); 00826 00827 //FIXME this doesn't really belong here. desktop KCM maybe? 00828 //but should the shortcuts be per-app or really-global? 00829 //I don't know how to make kactioncollections use plasmarc 00830 KAction *action = actions.addAction("configure shortcuts"); 00831 QObject::connect(action, SIGNAL(triggered()), q, SLOT(showShortcutConfig())); 00832 action->setText(i18n("Shortcut Settings")); 00833 action->setIcon(KIcon("configure-shortcuts")); 00834 action->setAutoRepeat(false); 00835 action->setData(AbstractToolBox::ConfigureTool); 00836 //action->setShortcut(KShortcut("ctrl+h")); 00837 action->setShortcutContext(Qt::ApplicationShortcut); 00838 00839 //fake containment/applet actions 00840 KActionCollection *containmentActions = AppletPrivate::defaultActions(q); //containment has to start with applet stuff 00841 ContainmentPrivate::addDefaultActions(containmentActions); //now it's really containment 00842 actionCollections << &actions << AppletPrivate::defaultActions(q) << containmentActions; 00843 q->updateShortcuts(); 00844 } 00845 00846 void CoronaPrivate::showShortcutConfig() 00847 { 00848 //show a kshortcutsdialog with the actions 00849 KShortcutsDialog *dlg = shortcutsDlg.data(); 00850 if (!dlg) { 00851 dlg = new KShortcutsDialog(); 00852 dlg->setModal(false); 00853 dlg->setAttribute(Qt::WA_DeleteOnClose, true); 00854 QObject::connect(dlg, SIGNAL(saved()), q, SIGNAL(shortcutsChanged())); 00855 00856 dlg->addCollection(&actions); 00857 QMutableListIterator<QWeakPointer<KActionCollection> > it(actionCollections); 00858 while (it.hasNext()) { 00859 it.next(); 00860 KActionCollection *collection = it.value().data(); 00861 if (!collection) { 00862 // get rid of KActionCollections that have been deleted behind our backs 00863 it.remove(); 00864 continue; 00865 } 00866 00867 dlg->addCollection(collection); 00868 } 00869 } 00870 00871 KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop()); 00872 dlg->configure(); 00873 dlg->raise(); 00874 } 00875 00876 void CoronaPrivate::toggleImmutability() 00877 { 00878 if (immutability == Mutable) { 00879 q->setImmutability(UserImmutable); 00880 } else { 00881 q->setImmutability(Mutable); 00882 } 00883 } 00884 00885 void CoronaPrivate::saveLayout(KSharedConfigPtr cg) const 00886 { 00887 KConfigGroup containmentsGroup(cg, "Containments"); 00888 foreach (const Containment *containment, containments) { 00889 QString cid = QString::number(containment->id()); 00890 KConfigGroup containmentConfig(&containmentsGroup, cid); 00891 containment->save(containmentConfig); 00892 } 00893 } 00894 00895 void CoronaPrivate::updateContainmentImmutability() 00896 { 00897 foreach (Containment *c, containments) { 00898 // we need to tell each containment that immutability has been altered 00899 c->updateConstraints(ImmutableConstraint); 00900 } 00901 } 00902 00903 void CoronaPrivate::containmentDestroyed(QObject *obj) 00904 { 00905 // we do a static_cast here since it really isn't an Containment by this 00906 // point anymore since we are in the qobject dtor. we don't actually 00907 // try and do anything with it, we just need the value of the pointer 00908 // so this unsafe looking code is actually just fine. 00909 Containment* containment = static_cast<Plasma::Containment*>(obj); 00910 int index = containments.indexOf(containment); 00911 00912 if (index > -1) { 00913 containments.removeAt(index); 00914 q->requestConfigSync(); 00915 } 00916 } 00917 00918 void CoronaPrivate::syncConfig() 00919 { 00920 q->config()->sync(); 00921 emit q->configSynced(); 00922 } 00923 00924 Containment *CoronaPrivate::addContainment(const QString &name, const QVariantList &args, uint id, bool delayedInit) 00925 { 00926 QString pluginName = name; 00927 Containment *containment = 0; 00928 Applet *applet = 0; 00929 00930 //kDebug() << "Loading" << name << args << id; 00931 00932 if (pluginName.isEmpty() || pluginName == "default") { 00933 // default to the desktop containment 00934 pluginName = defaultContainmentPlugin; 00935 } 00936 00937 bool loadingNull = pluginName == "null"; 00938 if (!loadingNull) { 00939 applet = Applet::load(pluginName, id, args); 00940 containment = dynamic_cast<Containment*>(applet); 00941 } 00942 00943 if (!containment) { 00944 if (!loadingNull) { 00945 kDebug() << "loading of containment" << name << "failed."; 00946 } 00947 00948 // in case we got a non-Containment from Applet::loadApplet or 00949 // a null containment was requested 00950 if (applet) { 00951 // the applet probably doesn't know what's hit it, so let's pretend it can be 00952 // initialized to make assumptions in the applet's dtor safer 00953 q->addItem(applet); 00954 applet->init(); 00955 q->removeItem(applet); 00956 delete applet; 00957 } 00958 applet = containment = new Containment(0, 0, id); 00959 00960 if (loadingNull) { 00961 containment->setDrawWallpaper(false); 00962 } else { 00963 containment->setFailedToLaunch(false); 00964 } 00965 00966 // we want to provide something and don't care about the failure to launch 00967 containment->setFormFactor(Plasma::Planar); 00968 } 00969 00970 // if this is a new containment, we need to ensure that there are no stale 00971 // configuration data around 00972 if (id == 0) { 00973 KConfigGroup conf(q->config(), "Containments"); 00974 conf = KConfigGroup(&conf, QString::number(containment->id())); 00975 conf.deleteGroup(); 00976 } 00977 00978 applet->d->isContainment = true; 00979 containment->setPos(containment->d->preferredPos(q)); 00980 q->addItem(containment); 00981 applet->d->setIsContainment(true, true); 00982 containments.append(containment); 00983 00984 if (!delayedInit) { 00985 containment->init(); 00986 KConfigGroup cg = containment->config(); 00987 containment->restore(cg); 00988 containment->updateConstraints(Plasma::StartupCompletedConstraint); 00989 containment->save(cg); 00990 q->requestConfigSync(); 00991 containment->flushPendingConstraintsEvents(); 00992 } 00993 00994 QObject::connect(containment, SIGNAL(destroyed(QObject*)), 00995 q, SLOT(containmentDestroyed(QObject*))); 00996 QObject::connect(containment, SIGNAL(configNeedsSaving()), 00997 q, SLOT(requestConfigSync())); 00998 QObject::connect(containment, SIGNAL(releaseVisualFocus()), 00999 q, SIGNAL(releaseVisualFocus())); 01000 QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)), 01001 q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*))); 01002 01003 if (!delayedInit) { 01004 emit q->containmentAdded(containment); 01005 } 01006 01007 return containment; 01008 } 01009 01010 QList<Plasma::Containment *> CoronaPrivate::importLayout(const KConfigBase &conf, bool mergeConfig) 01011 { 01012 if (const KConfigGroup *group = dynamic_cast<const KConfigGroup *>(&conf)) { 01013 if (!group->isValid()) { 01014 return QList<Containment *>(); 01015 } 01016 } 01017 01018 QList<Plasma::Containment *> newContainments; 01019 QSet<uint> containmentsIds; 01020 01021 foreach (Containment *containment, containments) { 01022 containmentsIds.insert(containment->id()); 01023 } 01024 01025 KConfigGroup containmentsGroup(&conf, "Containments"); 01026 01027 foreach (const QString &group, containmentsGroup.groupList()) { 01028 KConfigGroup containmentConfig(&containmentsGroup, group); 01029 01030 if (containmentConfig.entryMap().isEmpty()) { 01031 continue; 01032 } 01033 01034 uint cid = group.toUInt(); 01035 if (containmentsIds.contains(cid)) { 01036 cid = ++AppletPrivate::s_maxAppletId; 01037 } else if (cid > AppletPrivate::s_maxAppletId) { 01038 AppletPrivate::s_maxAppletId = cid; 01039 } 01040 01041 if (mergeConfig) { 01042 KConfigGroup realConf(q->config(), "Containments"); 01043 realConf = KConfigGroup(&realConf, QString::number(cid)); 01044 // in case something was there before us 01045 realConf.deleteGroup(); 01046 containmentConfig.copyTo(&realConf); 01047 } 01048 01049 //kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group; 01050 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Adding Containment" << containmentConfig.readEntry("plugin", QString()); 01051 Containment *c = addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(), cid, true); 01052 if (!c) { 01053 continue; 01054 } 01055 01056 newContainments.append(c); 01057 containmentsIds.insert(c->id()); 01058 01059 c->init(); 01060 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Init Containment" << c->pluginName(); 01061 c->restore(containmentConfig); 01062 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Restored Containment" << c->pluginName(); 01063 } 01064 01065 foreach (Containment *containment, newContainments) { 01066 containment->updateConstraints(Plasma::StartupCompletedConstraint); 01067 containment->d->initApplets(); 01068 emit q->containmentAdded(containment); 01069 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment" << containment->name(); 01070 } 01071 01072 return newContainments; 01073 } 01074 01075 } // namespace Plasma 01076 01077 #include "corona.moc" 01078
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:35 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:35 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.