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

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 Wed May 2 2012 17:36:02 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal