• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • 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);
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 

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

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal