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); 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); 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().right()) { 00544 pos.setX(v->geometry().right() - 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().bottom()) { 00559 pos.setY(v->geometry().bottom() - 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().bottom()); 00595 break; 00596 case LeftEdge: 00597 pos.setX(v->geometry().right()); 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.right()) { 00613 pos.rx() -= ((pos.x() + s.width()) - screenRect.right()); 00614 } 00615 00616 if (loc != TopEdge && pos.y() + s.height() > screenRect.bottom()) { 00617 pos.ry() -= ((pos.y() + s.height()) - screenRect.bottom()); 00618 } 00619 00620 pos.rx() = qMax(0, pos.x()); 00621 return pos; 00622 } 00623 00624 void Corona::loadDefaultLayout() 00625 { 00626 } 00627 00628 void Corona::setPreferredToolBoxPlugin(const Containment::Type type, const QString &plugin) 00629 { 00630 d->toolBoxPlugins[type] = plugin; 00631 //TODO: react to plugin changes on the fly? still don't see the use case (maybe for laptops that become tablets?) 00632 } 00633 00634 QString Corona::preferredToolBoxPlugin(const Containment::Type type) const 00635 { 00636 return d->toolBoxPlugins.value(type); 00637 } 00638 00639 void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event) 00640 { 00641 QGraphicsScene::dragEnterEvent(event); 00642 } 00643 00644 void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) 00645 { 00646 QGraphicsScene::dragLeaveEvent(event); 00647 } 00648 00649 void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event) 00650 { 00651 QGraphicsScene::dragMoveEvent(event); 00652 } 00653 00654 ImmutabilityType Corona::immutability() const 00655 { 00656 return d->immutability; 00657 } 00658 00659 void Corona::setImmutability(const ImmutabilityType immutable) 00660 { 00661 if (d->immutability == immutable || d->immutability == SystemImmutable) { 00662 return; 00663 } 00664 00665 kDebug() << "setting immutability to" << immutable; 00666 d->immutability = immutable; 00667 d->updateContainmentImmutability(); 00668 //tell non-containments that might care (like plasmaapp or a custom corona) 00669 emit immutabilityChanged(immutable); 00670 00671 //update our actions 00672 QAction *action = d->actions.action("lock widgets"); 00673 if (action) { 00674 if (d->immutability == SystemImmutable) { 00675 action->setEnabled(false); 00676 action->setVisible(false); 00677 } else { 00678 bool unlocked = d->immutability == Mutable; 00679 action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets")); 00680 action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked")); 00681 action->setEnabled(true); 00682 action->setVisible(true); 00683 } 00684 } 00685 00686 if (d->immutability != SystemImmutable) { 00687 KConfigGroup cg(config(), "General"); 00688 00689 // we call the dptr member directly for locked since isImmutable() 00690 // also checks kiosk and parent containers 00691 cg.writeEntry("immutability", (int)d->immutability); 00692 requestConfigSync(); 00693 } 00694 } 00695 00696 QList<Plasma::Location> Corona::freeEdges(int screen) const 00697 { 00698 QList<Plasma::Location> freeEdges; 00699 freeEdges << Plasma::TopEdge << Plasma::BottomEdge 00700 << Plasma::LeftEdge << Plasma::RightEdge; 00701 00702 foreach (Containment *containment, containments()) { 00703 if (containment->screen() == screen && 00704 freeEdges.contains(containment->location())) { 00705 freeEdges.removeAll(containment->location()); 00706 } 00707 } 00708 00709 return freeEdges; 00710 } 00711 00712 QAction *Corona::action(QString name) const 00713 { 00714 return d->actions.action(name); 00715 } 00716 00717 void Corona::addAction(QString name, QAction *action) 00718 { 00719 d->actions.addAction(name, action); 00720 } 00721 00722 KAction* Corona::addAction(QString name) 00723 { 00724 return d->actions.addAction(name); 00725 } 00726 00727 QList<QAction*> Corona::actions() const 00728 { 00729 return d->actions.actions(); 00730 } 00731 00732 void Corona::enableAction(const QString &name, bool enable) 00733 { 00734 QAction *action = d->actions.action(name); 00735 if (action) { 00736 action->setEnabled(enable); 00737 action->setVisible(enable); 00738 } 00739 } 00740 00741 void Corona::updateShortcuts() 00742 { 00743 QMutableListIterator<QWeakPointer<KActionCollection> > it(d->actionCollections); 00744 while (it.hasNext()) { 00745 it.next(); 00746 KActionCollection *collection = it.value().data(); 00747 if (!collection) { 00748 // get rid of KActionCollections that have been deleted behind our backs 00749 it.remove(); 00750 continue; 00751 } 00752 00753 collection->readSettings(); 00754 if (d->shortcutsDlg) { 00755 d->shortcutsDlg.data()->addCollection(collection); 00756 } 00757 } 00758 } 00759 00760 void Corona::addShortcuts(KActionCollection *newShortcuts) 00761 { 00762 d->actionCollections << newShortcuts; 00763 if (d->shortcutsDlg) { 00764 d->shortcutsDlg.data()->addCollection(newShortcuts); 00765 } 00766 } 00767 00768 void Corona::setContainmentActionsDefaults(Containment::Type containmentType, const ContainmentActionsPluginsConfig &config) 00769 { 00770 d->containmentActionsDefaults.insert(containmentType, config); 00771 } 00772 00773 ContainmentActionsPluginsConfig Corona::containmentActionsDefaults(Containment::Type containmentType) 00774 { 00775 return d->containmentActionsDefaults.value(containmentType); 00776 } 00777 00778 void Corona::setDialogManager(AbstractDialogManager *dialogManager) 00779 { 00780 d->dialogManager = dialogManager; 00781 } 00782 00783 AbstractDialogManager *Corona::dialogManager() 00784 { 00785 return d->dialogManager.data(); 00786 } 00787 00788 CoronaPrivate::CoronaPrivate(Corona *corona) 00789 : q(corona), 00790 immutability(Mutable), 00791 mimetype("text/x-plasmoidservicename"), 00792 defaultContainmentPlugin("desktop"), 00793 config(0), 00794 actions(corona) 00795 { 00796 if (KGlobal::hasMainComponent()) { 00797 configName = KGlobal::mainComponent().componentName() + "-appletsrc"; 00798 } else { 00799 configName = "plasma-appletsrc"; 00800 } 00801 } 00802 00803 CoronaPrivate::~CoronaPrivate() 00804 { 00805 qDeleteAll(containments); 00806 } 00807 00808 void CoronaPrivate::init() 00809 { 00810 q->setStickyFocus(true); 00811 configSyncTimer.setSingleShot(true); 00812 QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig())); 00813 00814 //some common actions 00815 actions.setConfigGroup("Shortcuts"); 00816 00817 KAction *lockAction = actions.addAction("lock widgets"); 00818 QObject::connect(lockAction, SIGNAL(triggered(bool)), q, SLOT(toggleImmutability())); 00819 lockAction->setText(i18n("Lock Widgets")); 00820 lockAction->setAutoRepeat(true); 00821 lockAction->setIcon(KIcon("object-locked")); 00822 lockAction->setData(AbstractToolBox::ControlTool); 00823 lockAction->setShortcut(KShortcut("alt+d, l")); 00824 lockAction->setShortcutContext(Qt::ApplicationShortcut); 00825 00826 //FIXME this doesn't really belong here. desktop KCM maybe? 00827 //but should the shortcuts be per-app or really-global? 00828 //I don't know how to make kactioncollections use plasmarc 00829 KAction *action = actions.addAction("configure shortcuts"); 00830 QObject::connect(action, SIGNAL(triggered()), q, SLOT(showShortcutConfig())); 00831 action->setText(i18n("Shortcut Settings")); 00832 action->setIcon(KIcon("configure-shortcuts")); 00833 action->setAutoRepeat(false); 00834 action->setData(AbstractToolBox::ConfigureTool); 00835 //action->setShortcut(KShortcut("ctrl+h")); 00836 action->setShortcutContext(Qt::ApplicationShortcut); 00837 00838 //fake containment/applet actions 00839 KActionCollection *containmentActions = AppletPrivate::defaultActions(q); //containment has to start with applet stuff 00840 ContainmentPrivate::addDefaultActions(containmentActions); //now it's really containment 00841 actionCollections << &actions << AppletPrivate::defaultActions(q) << containmentActions; 00842 q->updateShortcuts(); 00843 } 00844 00845 void CoronaPrivate::showShortcutConfig() 00846 { 00847 //show a kshortcutsdialog with the actions 00848 KShortcutsDialog *dlg = shortcutsDlg.data(); 00849 if (!dlg) { 00850 dlg = new KShortcutsDialog(); 00851 dlg->setModal(false); 00852 dlg->setAttribute(Qt::WA_DeleteOnClose, true); 00853 QObject::connect(dlg, SIGNAL(saved()), q, SIGNAL(shortcutsChanged())); 00854 00855 dlg->addCollection(&actions); 00856 QMutableListIterator<QWeakPointer<KActionCollection> > it(actionCollections); 00857 while (it.hasNext()) { 00858 it.next(); 00859 KActionCollection *collection = it.value().data(); 00860 if (!collection) { 00861 // get rid of KActionCollections that have been deleted behind our backs 00862 it.remove(); 00863 continue; 00864 } 00865 00866 dlg->addCollection(collection); 00867 } 00868 } 00869 00870 KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop()); 00871 dlg->configure(); 00872 dlg->raise(); 00873 } 00874 00875 void CoronaPrivate::toggleImmutability() 00876 { 00877 if (immutability == Mutable) { 00878 q->setImmutability(UserImmutable); 00879 } else { 00880 q->setImmutability(Mutable); 00881 } 00882 } 00883 00884 void CoronaPrivate::saveLayout(KSharedConfigPtr cg) const 00885 { 00886 KConfigGroup containmentsGroup(cg, "Containments"); 00887 foreach (const Containment *containment, containments) { 00888 QString cid = QString::number(containment->id()); 00889 KConfigGroup containmentConfig(&containmentsGroup, cid); 00890 containment->save(containmentConfig); 00891 } 00892 } 00893 00894 void CoronaPrivate::updateContainmentImmutability() 00895 { 00896 foreach (Containment *c, containments) { 00897 // we need to tell each containment that immutability has been altered 00898 c->updateConstraints(ImmutableConstraint); 00899 } 00900 } 00901 00902 void CoronaPrivate::containmentDestroyed(QObject *obj) 00903 { 00904 // we do a static_cast here since it really isn't an Containment by this 00905 // point anymore since we are in the qobject dtor. we don't actually 00906 // try and do anything with it, we just need the value of the pointer 00907 // so this unsafe looking code is actually just fine. 00908 Containment* containment = static_cast<Plasma::Containment*>(obj); 00909 int index = containments.indexOf(containment); 00910 00911 if (index > -1) { 00912 containments.removeAt(index); 00913 q->requestConfigSync(); 00914 } 00915 } 00916 00917 void CoronaPrivate::syncConfig() 00918 { 00919 q->config()->sync(); 00920 emit q->configSynced(); 00921 } 00922 00923 Containment *CoronaPrivate::addContainment(const QString &name, const QVariantList &args, uint id, bool delayedInit) 00924 { 00925 QString pluginName = name; 00926 Containment *containment = 0; 00927 Applet *applet = 0; 00928 00929 //kDebug() << "Loading" << name << args << id; 00930 00931 if (pluginName.isEmpty() || pluginName == "default") { 00932 // default to the desktop containment 00933 pluginName = defaultContainmentPlugin; 00934 } 00935 00936 bool loadingNull = pluginName == "null"; 00937 if (!loadingNull) { 00938 applet = Applet::load(pluginName, id, args); 00939 containment = dynamic_cast<Containment*>(applet); 00940 } 00941 00942 if (!containment) { 00943 if (!loadingNull) { 00944 kDebug() << "loading of containment" << name << "failed."; 00945 } 00946 00947 // in case we got a non-Containment from Applet::loadApplet or 00948 // a null containment was requested 00949 if (applet) { 00950 // the applet probably doesn't know what's hit it, so let's pretend it can be 00951 // initialized to make assumptions in the applet's dtor safer 00952 q->addItem(applet); 00953 applet->init(); 00954 q->removeItem(applet); 00955 delete applet; 00956 } 00957 applet = containment = new Containment(0, 0, id); 00958 00959 if (loadingNull) { 00960 containment->setDrawWallpaper(false); 00961 } else { 00962 containment->setFailedToLaunch(false); 00963 } 00964 00965 // we want to provide something and don't care about the failure to launch 00966 containment->setFormFactor(Plasma::Planar); 00967 } 00968 00969 // if this is a new containment, we need to ensure that there are no stale 00970 // configuration data around 00971 if (id == 0) { 00972 KConfigGroup conf(q->config(), "Containments"); 00973 conf = KConfigGroup(&conf, QString::number(containment->id())); 00974 conf.deleteGroup(); 00975 } 00976 00977 applet->d->isContainment = true; 00978 containment->setPos(containment->d->preferredPos(q)); 00979 q->addItem(containment); 00980 applet->d->setIsContainment(true, true); 00981 containments.append(containment); 00982 00983 if (!delayedInit) { 00984 containment->init(); 00985 KConfigGroup cg = containment->config(); 00986 containment->restore(cg); 00987 containment->updateConstraints(Plasma::StartupCompletedConstraint); 00988 containment->save(cg); 00989 q->requestConfigSync(); 00990 containment->flushPendingConstraintsEvents(); 00991 } 00992 00993 QObject::connect(containment, SIGNAL(destroyed(QObject*)), 00994 q, SLOT(containmentDestroyed(QObject*))); 00995 QObject::connect(containment, SIGNAL(configNeedsSaving()), 00996 q, SLOT(requestConfigSync())); 00997 QObject::connect(containment, SIGNAL(releaseVisualFocus()), 00998 q, SIGNAL(releaseVisualFocus())); 00999 QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)), 01000 q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*))); 01001 01002 if (!delayedInit) { 01003 emit q->containmentAdded(containment); 01004 } 01005 01006 return containment; 01007 } 01008 01009 QList<Plasma::Containment *> CoronaPrivate::importLayout(const KConfigBase &conf, bool mergeConfig) 01010 { 01011 if (const KConfigGroup *group = dynamic_cast<const KConfigGroup *>(&conf)) { 01012 if (!group->isValid()) { 01013 return QList<Containment *>(); 01014 } 01015 } 01016 01017 QList<Plasma::Containment *> newContainments; 01018 QSet<uint> containmentsIds; 01019 01020 foreach (Containment *containment, containments) { 01021 containmentsIds.insert(containment->id()); 01022 } 01023 01024 KConfigGroup containmentsGroup(&conf, "Containments"); 01025 01026 foreach (const QString &group, containmentsGroup.groupList()) { 01027 KConfigGroup containmentConfig(&containmentsGroup, group); 01028 01029 if (containmentConfig.entryMap().isEmpty()) { 01030 continue; 01031 } 01032 01033 uint cid = group.toUInt(); 01034 if (containmentsIds.contains(cid)) { 01035 cid = ++AppletPrivate::s_maxAppletId; 01036 } else if (cid > AppletPrivate::s_maxAppletId) { 01037 AppletPrivate::s_maxAppletId = cid; 01038 } 01039 01040 if (mergeConfig) { 01041 KConfigGroup realConf(q->config(), "Containments"); 01042 realConf = KConfigGroup(&realConf, QString::number(cid)); 01043 // in case something was there before us 01044 realConf.deleteGroup(); 01045 containmentConfig.copyTo(&realConf); 01046 } 01047 01048 //kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group; 01049 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Adding Containment" << containmentConfig.readEntry("plugin", QString()); 01050 Containment *c = addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(), cid, true); 01051 if (!c) { 01052 continue; 01053 } 01054 01055 newContainments.append(c); 01056 containmentsIds.insert(c->id()); 01057 01058 c->init(); 01059 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Init Containment" << c->pluginName(); 01060 c->restore(containmentConfig); 01061 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Restored Containment" << c->pluginName(); 01062 } 01063 01064 foreach (Containment *containment, newContainments) { 01065 containment->updateConstraints(Plasma::StartupCompletedConstraint); 01066 containment->d->initApplets(); 01067 emit q->containmentAdded(containment); 01068 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment" << containment->name(); 01069 } 01070 01071 return newContainments; 01072 } 01073 01074 } // namespace Plasma 01075 01076 #include "corona.moc" 01077
KDE 4.7 API Reference