• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

corona.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2007 Matt Broadstone <mbroadst@gmail.com>
00003  *   Copyright 2007 Aaron Seigo <aseigo@kde.org>
00004  *   Copyright 2007 Riccardo Iaconelli <riccardo@kde.org>
00005  *
00006  *   This program is free software; you can redistribute it and/or modify
00007  *   it under the terms of the GNU Library General Public License as
00008  *   published by the Free Software Foundation; either version 2, or
00009  *   (at your option) any later version.
00010  *
00011  *   This program is distributed in the hope that it will be useful,
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *   GNU General Public License for more details
00015  *
00016  *   You should have received a copy of the GNU Library General Public
00017  *   License along with this program; if not, write to the
00018  *   Free Software Foundation, Inc.,
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020  */
00021 
00022 #include "corona.h"
00023 
00024 #include <QApplication>
00025 #include <QGraphicsView>
00026 #include <QGraphicsSceneDragDropEvent>
00027 #include <QGraphicsGridLayout>
00028 #include <QMimeData>
00029 #include <QPainter>
00030 #include <QTimer>
00031 
00032 #include <cmath>
00033 
00034 #include <kdebug.h>
00035 #include <kglobal.h>
00036 #include <klocale.h>
00037 #include <kmimetype.h>
00038 
00039 #include "containment.h"
00040 #include "view.h"
00041 #include "private/applet_p.h"
00042 #include "tooltipmanager.h"
00043 
00044 using namespace Plasma;
00045 
00046 namespace Plasma
00047 {
00048 
00049 // constant controlling how long between requesting a configuration sync
00050 // and one happening should occur. currently 10 seconds
00051 const int CONFIG_SYNC_TIMEOUT = 10000;
00052 
00053 class CoronaPrivate
00054 {
00055 public:
00056     CoronaPrivate(Corona *corona)
00057         : q(corona),
00058           immutability(Mutable),
00059           mimetype("text/x-plasmoidservicename"),
00060           config(0)
00061     {
00062         if (KGlobal::hasMainComponent()) {
00063             configName = KGlobal::mainComponent().componentName() + "-appletsrc";
00064         } else {
00065             configName = "plasma-appletsrc";
00066         }
00067     }
00068 
00069     ~CoronaPrivate()
00070     {
00071         qDeleteAll(containments);
00072     }
00073 
00074     void init()
00075     {
00076         configSyncTimer.setSingleShot(true);
00077         QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig()));
00078     }
00079 
00080     void saveLayout(KSharedConfigPtr cg) const
00081     {
00082         KConfigGroup containmentsGroup(cg, "Containments");
00083         foreach (const Containment *containment, containments) {
00084             QString cid = QString::number(containment->id());
00085             KConfigGroup containmentConfig(&containmentsGroup, cid);
00086             containment->save(containmentConfig);
00087         }
00088     }
00089 
00090     void updateContainmentImmutability()
00091     {
00092         foreach (Containment *c, containments) {
00093             // we need to tell each containment that immutability has been altered
00094             c->updateConstraints(ImmutableConstraint);
00095         }
00096     }
00097 
00098     void containmentDestroyed(QObject *obj)
00099     {
00100         // we do a static_cast here since it really isn't an Containment by this
00101         // point anymore since we are in the qobject dtor. we don't actually
00102         // try and do anything with it, we just need the value of the pointer
00103         // so this unsafe looking code is actually just fine.
00104         Containment* containment = static_cast<Plasma::Containment*>(obj);
00105         int index = containments.indexOf(containment);
00106 
00107         if (index > -1) {
00108             containments.removeAt(index);
00109             q->requestConfigSync();
00110         }
00111     }
00112 
00113     void syncConfig()
00114     {
00115         q->config()->sync();
00116         emit q->configSynced();
00117     }
00118 
00119     Containment *addContainment(const QString &name, const QVariantList &args,
00120                                 uint id, bool delayedInit)
00121     {
00122         QString pluginName = name;
00123         Containment *containment = 0;
00124         Applet *applet = 0;
00125 
00126         //kDebug() << "Loading" << name << args << id;
00127 
00128         if (pluginName.isEmpty()) {
00129             // default to the desktop containment
00130             pluginName = "desktop";
00131         }
00132 
00133         if (pluginName != "null") {
00134             applet = Applet::load(pluginName, id, args);
00135             containment = dynamic_cast<Containment*>(applet);
00136         }
00137 
00138         if (!containment) {
00139             kDebug() << "loading of containment" << name << "failed.";
00140 
00141             // in case we got a non-Containment from Applet::loadApplet or
00142             // a null containment was requested
00143             delete applet;
00144             containment = new Containment(0, 0, id);
00145 
00146             if (pluginName == "null") {
00147                 containment->setDrawWallpaper(false);
00148             }
00149 
00150             // we want to provide something and don't care about the failure to launch
00151             containment->setFailedToLaunch(false);
00152             containment->setFormFactor(Plasma::Planar);
00153         }
00154 
00155         static_cast<Applet*>(containment)->d->setIsContainment(true);
00156         containments.append(containment);
00157         q->addItem(containment);
00158 
00159         if (!delayedInit) {
00160             containment->init();
00161             containment->updateConstraints(Plasma::StartupCompletedConstraint);
00162             KConfigGroup cg = containment->config();
00163             containment->save(cg);
00164             q->requestConfigSync();
00165             containment->flushPendingConstraintsEvents();
00166         }
00167 
00168         QObject::connect(containment, SIGNAL(destroyed(QObject*)),
00169                          q, SLOT(containmentDestroyed(QObject*)));
00170         QObject::connect(containment, SIGNAL(configNeedsSaving()),
00171                          q, SLOT(requestConfigSync()));
00172         QObject::connect(containment, SIGNAL(releaseVisualFocus()),
00173                          q, SIGNAL(releaseVisualFocus()));
00174         QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)),
00175                          q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)));
00176 
00177         if (!delayedInit) {
00178             emit q->containmentAdded(containment);
00179         }
00180 
00181         return containment;
00182     }
00183 
00184     void offscreenWidgetDestroyed(QObject *);
00185 
00186     Corona *q;
00187     ImmutabilityType immutability;
00188     QString mimetype;
00189     QString configName;
00190     KSharedConfigPtr config;
00191     QTimer configSyncTimer;
00192     QList<Containment*> containments;
00193     QHash<uint, QGraphicsWidget*> offscreenWidgets;
00194 };
00195 
00196 Corona::Corona(QObject *parent)
00197     : QGraphicsScene(parent),
00198       d(new CoronaPrivate(this))
00199 {
00200     d->init();
00201     ToolTipManager::self()->m_corona = this;
00202     //setViewport(new QGLWidget(QGLFormat(QGL::StencilBuffer | QGL::AlphaChannel)));
00203 }
00204 
00205 Corona::~Corona()
00206 {
00207     // FIXME: Same fix as in Plasma::View - make sure that when the focused widget is
00208     //        destroyed we don't try to transfer it to something that's already been
00209     //        deleted.
00210     clearFocus();
00211     delete d;
00212 }
00213 
00214 void Corona::setAppletMimeType(const QString &type)
00215 {
00216     d->mimetype = type;
00217 }
00218 
00219 QString Corona::appletMimeType()
00220 {
00221     return d->mimetype;
00222 }
00223 
00224 void Corona::saveLayout(const QString &configName) const
00225 {
00226     KSharedConfigPtr c;
00227 
00228     if (configName.isEmpty() || configName == d->configName) {
00229         c = config();
00230     } else {
00231         c = KSharedConfig::openConfig(configName);
00232     }
00233 
00234     d->saveLayout(c);
00235 }
00236 
00237 void Corona::requestConfigSync()
00238 {
00239     // TODO: should we check into our immutability before doing this?
00240 
00241     //NOTE: this is a pretty simplistic model: we simply save no more than CONFIG_SYNC_TIMEOUT
00242     //      after the first time this is called. not much of a heuristic for save points, but
00243     //      it should at least compress these activities a bit and provide a way for applet
00244     //      authors to ween themselves from the sync() disease. A more interesting/dynamic
00245     //      algorithm for determining when to actually sync() to disk might be better, though.
00246     if (!d->configSyncTimer.isActive()) {
00247         d->configSyncTimer.start(CONFIG_SYNC_TIMEOUT);
00248     }
00249 }
00250 
00251 void Corona::requireConfigSync()
00252 {
00253     d->syncConfig();
00254 }
00255 
00256 void Corona::initializeLayout(const QString &configName)
00257 {
00258     clearContainments();
00259     loadLayout(configName);
00260 
00261     if (d->containments.isEmpty()) {
00262         loadDefaultLayout();
00263         if (!d->containments.isEmpty()) {
00264             requestConfigSync();
00265         }
00266     }
00267 
00268     if (config()->isImmutable()) {
00269         d->updateContainmentImmutability();
00270     }
00271 
00272     KConfigGroup coronaConfig(config(), "General");
00273     setImmutability((ImmutabilityType)coronaConfig.readEntry("immutability", (int)Mutable));
00274 }
00275 
00276 void Corona::loadLayout(const QString &configName)
00277 {
00278     KSharedConfigPtr c;
00279 
00280     if (configName.isEmpty() || configName == d->configName) {
00281         c = config();
00282     } else {
00283         c = KSharedConfig::openConfig(configName);
00284     }
00285 
00286     KConfigGroup containments(config(), "Containments");
00287 
00288     foreach (const QString &group, containments.groupList()) {
00289         KConfigGroup containmentConfig(&containments, group);
00290 
00291         if (containmentConfig.entryMap().isEmpty()) {
00292             continue;
00293         }
00294 
00295         int cid = group.toUInt();
00296         //kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group;
00297         Containment *c = d->addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(),
00298                                            cid, true);
00299         if (!c) {
00300             continue;
00301         }
00302 
00303         //addItem(c);
00304         c->init();
00305         c->restore(containmentConfig);
00306     }
00307 
00308     foreach (Containment *containment, d->containments) {
00309         QString cid = QString::number(containment->id());
00310         KConfigGroup containmentConfig(&containments, cid);
00311 
00312         foreach (Applet *applet, containment->applets()) {
00313             applet->init();
00314             // We have to flush the applet constraints manually
00315             applet->flushPendingConstraintsEvents();
00316         }
00317 
00318         containment->updateConstraints(Plasma::StartupCompletedConstraint);
00319         containment->flushPendingConstraintsEvents();
00320         emit containmentAdded(containment);
00321     }
00322 }
00323 
00324 Containment *Corona::containmentForScreen(int screen, int desktop) const
00325 {
00326     foreach (Containment *containment, d->containments) {
00327         if (containment->screen() == screen &&
00328             (desktop < 0 || containment->desktop() == desktop) &&
00329             (containment->containmentType() == Containment::DesktopContainment ||
00330              containment->containmentType() >= Containment::CustomContainment)) {
00331             return containment;
00332         }
00333     }
00334 
00335     return 0;
00336 }
00337 
00338 QList<Containment*> Corona::containments() const
00339 {
00340     return d->containments;
00341 }
00342 
00343 void Corona::clearContainments()
00344 {
00345     foreach (Containment *containment, d->containments) {
00346         containment->clearApplets();
00347     }
00348 }
00349 
00350 KSharedConfigPtr Corona::config() const
00351 {
00352     if (!d->config) {
00353         d->config = KSharedConfig::openConfig(d->configName);
00354     }
00355 
00356     return d->config;
00357 }
00358 
00359 Containment *Corona::addContainment(const QString &name, const QVariantList &args)
00360 {
00361     return d->addContainment(name, args, 0, false);
00362 }
00363 
00364 Containment *Corona::addContainmentDelayed(const QString &name, const QVariantList &args)
00365 {
00366     return d->addContainment(name, args, 0, true);
00367 }
00368 
00369 void Corona::addOffscreenWidget(QGraphicsWidget *widget)
00370 {
00371     if (d->offscreenWidgets.values().contains(widget)) {
00372         kDebug() << "widget is already an offscreen widget!";
00373         return;
00374     }
00375 
00376     widget->setParentItem(0);
00377 
00378     //search for an empty spot in the topleft quadrant of the scene. each 'slot' is QWIDGETSIZE_MAX
00379     //x QWIDGETSIZE_MAX, so we're guaranteed to never have to move widgets once they're placed here.
00380     int i = 0;
00381     while (d->offscreenWidgets.contains(i)) {
00382         i++;
00383     }
00384 
00385     d->offscreenWidgets[i] = widget;
00386     widget->setPos((-i - 1) * QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
00387     kDebug() << "adding offscreen widget at slot " << i;
00388 
00389     connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(offscreenWidgetDestroyed(QObject*)));
00390 }
00391 
00392 void Corona::removeOffscreenWidget(QGraphicsWidget *widget)
00393 {
00394     QMutableHashIterator<uint, QGraphicsWidget *> it(d->offscreenWidgets);
00395 
00396     while (it.hasNext()) {
00397         if (it.next().value() == widget) {
00398             it.remove();
00399             return;
00400         }
00401     }
00402 }
00403 
00404 void CoronaPrivate::offscreenWidgetDestroyed(QObject *o)
00405 {
00406     // at this point, it's just a QObject, not a QGraphicsWidget, but we still need
00407     // a pointer of the appropriate type.
00408     // WARNING: DO NOT USE THE WIDGET POINTER FOR ANYTHING OTHER THAN POINTER COMPARISONS
00409     QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(o);
00410     q->removeOffscreenWidget(widget);
00411 }
00412 
00413 int Corona::numScreens() const
00414 {
00415     return 1;
00416 }
00417 
00418 QRect Corona::screenGeometry(int id) const
00419 {
00420     Q_UNUSED(id);
00421     if (views().isEmpty()) {
00422         return sceneRect().toRect();
00423     } else {
00424         QGraphicsView *v = views()[0];
00425         QRect r = sceneRect().toRect();
00426         r.moveTo(v->mapToGlobal(v->pos()));
00427         return r;
00428     }
00429 }
00430 
00431 QRegion Corona::availableScreenRegion(int id) const
00432 {
00433     return QRegion(screenGeometry(id));
00434 }
00435 
00436 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s)
00437 {
00438     QGraphicsView *v = viewFor(item);
00439 
00440     if (!v) {
00441         return QPoint(0, 0);
00442     }
00443 
00444     QPoint pos;
00445     QTransform sceneTransform = item->sceneTransform();
00446 
00447     //if the applet is rotated the popup position has to be un-transformed
00448     if (sceneTransform.isRotating()) {
00449         qreal angle = acos(sceneTransform.m11());
00450         QTransform newTransform;
00451         QPointF center = item->sceneBoundingRect().center();
00452 
00453         newTransform.translate(center.x(), center.y());
00454         newTransform.rotateRadians(-angle);
00455         newTransform.translate(-center.x(), -center.y());
00456         pos = v->mapFromScene(newTransform.inverted().map(item->scenePos()));
00457     } else {
00458         pos = v->mapFromScene(item->scenePos());
00459     }
00460 
00461     pos = v->mapToGlobal(pos);
00462     //kDebug() << "==> position is" << item->scenePos() << v->mapFromScene(item->scenePos()) << pos;
00463     Plasma::View *pv = dynamic_cast<Plasma::View *>(v);
00464 
00465     Plasma::Location loc = Floating;
00466     if (pv && pv->containment()) {
00467         loc = pv->containment()->location();
00468     }
00469 
00470     switch (loc) {
00471     case BottomEdge:
00472         pos = QPoint(pos.x(), pos.y() - s.height());
00473         break;
00474     case TopEdge:
00475         pos = QPoint(pos.x(), pos.y() + (int)item->boundingRect().size().height());
00476         break;
00477     case LeftEdge:
00478         pos = QPoint(pos.x() + (int)item->boundingRect().size().width(), pos.y());
00479         break;
00480     case RightEdge:
00481         pos = QPoint(pos.x() - s.width(), pos.y());
00482         break;
00483     default:
00484         if (pos.y() - s.height() > 0) {
00485              pos = QPoint(pos.x(), pos.y() - s.height());
00486         } else {
00487              pos = QPoint(pos.x(), pos.y() + (int)item->boundingRect().size().height());
00488         }
00489     }
00490 
00491     //are we out of screen?
00492     QRect screenRect =
00493         screenGeometry((pv && pv->containment()) ? pv->containment()->screen() : -1);
00494     //kDebug() << "==> rect for" << (pv ? pv->containment()->screen() : -1) << "is" << screenRect;
00495 
00496     if (loc != LeftEdge && pos.rx() + s.width() > screenRect.right()) {
00497         pos.rx() -= ((pos.rx() + s.width()) - screenRect.right());
00498     }
00499 
00500     if (loc != TopEdge && pos.ry() + s.height() > screenRect.bottom()) {
00501         pos.ry() -= ((pos.ry() + s.height()) - screenRect.bottom());
00502     }
00503 
00504     pos.rx() = qMax(0, pos.rx());
00505     return pos;
00506 }
00507 
00508 
00509 
00510 void Corona::loadDefaultLayout()
00511 {
00512 }
00513 
00514 void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00515 {
00516     QGraphicsScene::dragEnterEvent(event);
00517 }
00518 
00519 void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
00520 {
00521     QGraphicsScene::dragLeaveEvent(event);
00522 }
00523 
00524 void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00525 {
00526     QGraphicsScene::dragMoveEvent(event);
00527 }
00528 
00529 ImmutabilityType Corona::immutability() const
00530 {
00531     return d->immutability;
00532 }
00533 
00534 void Corona::setImmutability(const ImmutabilityType immutable)
00535 {
00536     if (d->immutability == immutable ||
00537         d->immutability == SystemImmutable) {
00538         return;
00539     }
00540 
00541     kDebug() << "setting immutability to" << immutable;
00542     d->immutability = immutable;
00543     d->updateContainmentImmutability();
00544 
00545     KConfigGroup cg(config(), "General");
00546 
00547     // we call the dptr member directly for locked since isImmutable()
00548     // also checks kiosk and parent containers
00549     cg.writeEntry("immutability", (int)d->immutability);
00550     requestConfigSync();
00551 }
00552 
00553 QList<Plasma::Location> Corona::freeEdges(int screen) const
00554 {
00555     QList<Plasma::Location> freeEdges;
00556     freeEdges << Plasma::TopEdge << Plasma::BottomEdge
00557               << Plasma::LeftEdge << Plasma::RightEdge;
00558 
00559     foreach (Containment *containment, containments()) {
00560         if (containment->screen() == screen && 
00561             freeEdges.contains(containment->location())) {
00562             freeEdges.removeAll(containment->location());
00563         }
00564     }
00565 
00566     return freeEdges;
00567 }
00568 
00569 } // namespace Plasma
00570 
00571 #include "corona.moc"
00572 

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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