00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00050
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
00094 c->updateConstraints(ImmutableConstraint);
00095 }
00096 }
00097
00098 void containmentDestroyed(QObject *obj)
00099 {
00100
00101
00102
00103
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
00127
00128 if (pluginName.isEmpty()) {
00129
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
00142
00143 delete applet;
00144 containment = new Containment(0, 0, id);
00145
00146 if (pluginName == "null") {
00147 containment->setDrawWallpaper(false);
00148 }
00149
00150
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
00203 }
00204
00205 Corona::~Corona()
00206 {
00207
00208
00209
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
00240
00241
00242
00243
00244
00245
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
00297 Containment *c = d->addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(),
00298 cid, true);
00299 if (!c) {
00300 continue;
00301 }
00302
00303
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
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
00379
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
00407
00408
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
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
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
00492 QRect screenRect =
00493 screenGeometry((pv && pv->containment()) ? pv->containment()->screen() : -1);
00494
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
00548
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 }
00570
00571 #include "corona.moc"
00572