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 #include <kactioncollection.h>
00039 #include <kaction.h>
00040 #include <kshortcutsdialog.h>
00041
00042 #include "containment.h"
00043 #include "view.h"
00044 #include "private/applet_p.h"
00045 #include "private/containment_p.h"
00046 #include "tooltipmanager.h"
00047
00048 using namespace Plasma;
00049
00050 namespace Plasma
00051 {
00052
00053
00054
00055 const int CONFIG_SYNC_TIMEOUT = 10000;
00056
00057 class CoronaPrivate
00058 {
00059 public:
00060 CoronaPrivate(Corona *corona)
00061 : q(corona),
00062 immutability(Mutable),
00063 mimetype("text/x-plasmoidservicename"),
00064 config(0),
00065 actions(corona)
00066 {
00067 if (KGlobal::hasMainComponent()) {
00068 configName = KGlobal::mainComponent().componentName() + "-appletsrc";
00069 } else {
00070 configName = "plasma-appletsrc";
00071 }
00072 }
00073
00074 ~CoronaPrivate()
00075 {
00076 qDeleteAll(containments);
00077 }
00078
00079 void init()
00080 {
00081 configSyncTimer.setSingleShot(true);
00082 QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig()));
00083
00084
00085 actions.setConfigGroup("Shortcuts");
00086
00087 KAction *lockAction = actions.addAction("lock widgets");
00088 lockAction->setText(i18n("Lock Widgets"));
00089 lockAction->setIcon(KIcon("object-locked"));
00090 QObject::connect(lockAction, SIGNAL(triggered(bool)),
00091 q, SLOT(toggleImmutability()));
00092 lockAction->setShortcut(KShortcut("alt+d, l"));
00093 lockAction->setShortcutContext(Qt::ApplicationShortcut);
00094
00095
00096
00097
00098 KAction *action = actions.addAction("configure shortcuts");
00099 action->setText(i18n("Shortcut Settings"));
00100 action->setIcon(KIcon("configure"));
00101 action->setAutoRepeat(false);
00102 QObject::connect(action, SIGNAL(triggered()),
00103 q, SLOT(showShortcutConfig()));
00104
00105 action->setShortcutContext(Qt::ApplicationShortcut);
00106
00107 actions.readSettings();
00108
00109
00110 KActionCollection *aActions = AppletPrivate::defaultActions(q);
00111 KActionCollection *cActions = AppletPrivate::defaultActions(q);
00112 ContainmentPrivate::addDefaultActions(cActions);
00113
00114 cActions->readSettings();
00115 aActions->readSettings();
00116
00117 shortcutsDlg.setModal(false);
00118 shortcutsDlg.addCollection(aActions);
00119 shortcutsDlg.addCollection(cActions);
00120
00121 QObject::connect(&shortcutsDlg, SIGNAL(saved()), q, SIGNAL(shortcutsChanged()));
00122 }
00123
00124 void showShortcutConfig()
00125 {
00126
00127 shortcutsDlg.configure();
00128 }
00129
00130 void toggleImmutability()
00131 {
00132 if (immutability == Mutable) {
00133 q->setImmutability(UserImmutable);
00134 } else {
00135 q->setImmutability(Mutable);
00136 }
00137 }
00138
00139 void saveLayout(KSharedConfigPtr cg) const
00140 {
00141 KConfigGroup containmentsGroup(cg, "Containments");
00142 foreach (const Containment *containment, containments) {
00143 QString cid = QString::number(containment->id());
00144 KConfigGroup containmentConfig(&containmentsGroup, cid);
00145 containment->save(containmentConfig);
00146 }
00147 }
00148
00149 void updateContainmentImmutability()
00150 {
00151 foreach (Containment *c, containments) {
00152
00153 c->updateConstraints(ImmutableConstraint);
00154 }
00155 }
00156
00157 void containmentDestroyed(QObject *obj)
00158 {
00159
00160
00161
00162
00163 Containment* containment = static_cast<Plasma::Containment*>(obj);
00164 int index = containments.indexOf(containment);
00165
00166 if (index > -1) {
00167 containments.removeAt(index);
00168 q->requestConfigSync();
00169 }
00170 }
00171
00172 void syncConfig()
00173 {
00174 q->config()->sync();
00175 emit q->configSynced();
00176 }
00177
00178 Containment *addContainment(const QString &name, const QVariantList &args,
00179 uint id, bool delayedInit)
00180 {
00181 QString pluginName = name;
00182 Containment *containment = 0;
00183 Applet *applet = 0;
00184
00185
00186
00187 if (pluginName.isEmpty()) {
00188
00189 pluginName = "desktop";
00190 }
00191
00192 if (pluginName != "null") {
00193 applet = Applet::load(pluginName, id, args);
00194 containment = dynamic_cast<Containment*>(applet);
00195 }
00196
00197 if (!containment) {
00198 kDebug() << "loading of containment" << name << "failed.";
00199
00200
00201
00202 delete applet;
00203 containment = new Containment(0, 0, id);
00204
00205 if (pluginName == "null") {
00206 containment->setDrawWallpaper(false);
00207 }
00208
00209
00210 containment->setFailedToLaunch(false);
00211 containment->setFormFactor(Plasma::Planar);
00212 }
00213
00214 static_cast<Applet*>(containment)->d->isContainment = true;
00215 q->addItem(containment);
00216 static_cast<Applet*>(containment)->d->setIsContainment(true, true);
00217 containments.append(containment);
00218
00219 if (!delayedInit) {
00220 containment->init();
00221 containment->updateConstraints(Plasma::StartupCompletedConstraint);
00222 KConfigGroup cg = containment->config();
00223 containment->save(cg);
00224 q->requestConfigSync();
00225 containment->flushPendingConstraintsEvents();
00226 }
00227
00228 QObject::connect(containment, SIGNAL(destroyed(QObject*)),
00229 q, SLOT(containmentDestroyed(QObject*)));
00230 QObject::connect(containment, SIGNAL(configNeedsSaving()),
00231 q, SLOT(requestConfigSync()));
00232 QObject::connect(containment, SIGNAL(releaseVisualFocus()),
00233 q, SIGNAL(releaseVisualFocus()));
00234 QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)),
00235 q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)));
00236
00237 if (!delayedInit) {
00238 emit q->containmentAdded(containment);
00239 }
00240
00241 return containment;
00242 }
00243
00244 void offscreenWidgetDestroyed(QObject *);
00245
00246 Corona *q;
00247 ImmutabilityType immutability;
00248 QString mimetype;
00249 QString configName;
00250 KSharedConfigPtr config;
00251 QTimer configSyncTimer;
00252 QList<Containment*> containments;
00253 QHash<uint, QGraphicsWidget*> offscreenWidgets;
00254 KActionCollection actions;
00255 KShortcutsDialog shortcutsDlg;
00256 };
00257
00258 Corona::Corona(QObject *parent)
00259 : QGraphicsScene(parent),
00260 d(new CoronaPrivate(this))
00261 {
00262 d->init();
00263 ToolTipManager::self()->m_corona = this;
00264
00265 }
00266
00267 Corona::~Corona()
00268 {
00269 KConfigGroup trans(KGlobal::config(), "PlasmaTransientsConfig");
00270 trans.deleteGroup();
00271
00272
00273
00274
00275 clearFocus();
00276 delete d;
00277 }
00278
00279 void Corona::setAppletMimeType(const QString &type)
00280 {
00281 d->mimetype = type;
00282 }
00283
00284 QString Corona::appletMimeType()
00285 {
00286 return d->mimetype;
00287 }
00288
00289 void Corona::saveLayout(const QString &configName) const
00290 {
00291 KSharedConfigPtr c;
00292
00293 if (configName.isEmpty() || configName == d->configName) {
00294 c = config();
00295 } else {
00296 c = KSharedConfig::openConfig(configName);
00297 }
00298
00299 d->saveLayout(c);
00300 }
00301
00302 void Corona::requestConfigSync()
00303 {
00304
00305
00306
00307
00308
00309
00310
00311 if (!d->configSyncTimer.isActive()) {
00312 d->configSyncTimer.start(CONFIG_SYNC_TIMEOUT);
00313 }
00314 }
00315
00316 void Corona::requireConfigSync()
00317 {
00318 d->syncConfig();
00319 }
00320
00321 void Corona::initializeLayout(const QString &configName)
00322 {
00323 clearContainments();
00324 loadLayout(configName);
00325
00326 if (d->containments.isEmpty()) {
00327 loadDefaultLayout();
00328 if (!d->containments.isEmpty()) {
00329 requestConfigSync();
00330 }
00331 }
00332
00333 if (config()->isImmutable()) {
00334 d->updateContainmentImmutability();
00335 }
00336
00337 KConfigGroup coronaConfig(config(), "General");
00338 setImmutability((ImmutabilityType)coronaConfig.readEntry("immutability", (int)Mutable));
00339 }
00340
00341 void Corona::loadLayout(const QString &configName)
00342 {
00343 KSharedConfigPtr c;
00344
00345 if (configName.isEmpty() || configName == d->configName) {
00346 c = config();
00347 } else {
00348 c = KSharedConfig::openConfig(configName);
00349 }
00350
00351 KConfigGroup containments(c, "Containments");
00352
00353 foreach (const QString &group, containments.groupList()) {
00354 KConfigGroup containmentConfig(&containments, group);
00355
00356 if (containmentConfig.entryMap().isEmpty()) {
00357 continue;
00358 }
00359
00360 int cid = group.toUInt();
00361
00362 Containment *c = d->addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(),
00363 cid, true);
00364 if (!c) {
00365 continue;
00366 }
00367
00368 c->init();
00369 c->restore(containmentConfig);
00370 }
00371
00372 foreach (Containment *containment, d->containments) {
00373 QString cid = QString::number(containment->id());
00374 KConfigGroup containmentConfig(&containments, cid);
00375
00376 foreach (Applet *applet, containment->applets()) {
00377 applet->init();
00378
00379 applet->flushPendingConstraintsEvents();
00380 }
00381
00382 containment->updateConstraints(Plasma::StartupCompletedConstraint);
00383 containment->flushPendingConstraintsEvents();
00384 emit containmentAdded(containment);
00385 }
00386 }
00387
00388 Containment *Corona::containmentForScreen(int screen, int desktop) const
00389 {
00390 foreach (Containment *containment, d->containments) {
00391 if (containment->screen() == screen &&
00392 (desktop < 0 || containment->desktop() == desktop) &&
00393 (containment->containmentType() == Containment::DesktopContainment ||
00394 containment->containmentType() >= Containment::CustomContainment)) {
00395 return containment;
00396 }
00397 }
00398
00399 return 0;
00400 }
00401
00402 QList<Containment*> Corona::containments() const
00403 {
00404 return d->containments;
00405 }
00406
00407 void Corona::clearContainments()
00408 {
00409 foreach (Containment *containment, d->containments) {
00410 containment->clearApplets();
00411 }
00412 }
00413
00414 KSharedConfigPtr Corona::config() const
00415 {
00416 if (!d->config) {
00417 d->config = KSharedConfig::openConfig(d->configName);
00418 }
00419
00420 return d->config;
00421 }
00422
00423 Containment *Corona::addContainment(const QString &name, const QVariantList &args)
00424 {
00425 return d->addContainment(name, args, 0, false);
00426 }
00427
00428 Containment *Corona::addContainmentDelayed(const QString &name, const QVariantList &args)
00429 {
00430 return d->addContainment(name, args, 0, true);
00431 }
00432
00433 void Corona::addOffscreenWidget(QGraphicsWidget *widget)
00434 {
00435 if (d->offscreenWidgets.values().contains(widget)) {
00436 kDebug() << "widget is already an offscreen widget!";
00437 return;
00438 }
00439
00440 widget->setParentItem(0);
00441
00442
00443
00444 int i = 0;
00445 while (d->offscreenWidgets.contains(i)) {
00446 i++;
00447 }
00448
00449 d->offscreenWidgets[i] = widget;
00450 widget->setPos((-i - 1) * QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
00451 kDebug() << "adding offscreen widget at slot " << i;
00452
00453 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(offscreenWidgetDestroyed(QObject*)));
00454 }
00455
00456 void Corona::removeOffscreenWidget(QGraphicsWidget *widget)
00457 {
00458 QMutableHashIterator<uint, QGraphicsWidget *> it(d->offscreenWidgets);
00459
00460 while (it.hasNext()) {
00461 if (it.next().value() == widget) {
00462 it.remove();
00463 return;
00464 }
00465 }
00466 }
00467
00468 QList <QGraphicsWidget *> Corona::offscreenWidgets() const
00469 {
00470 return d->offscreenWidgets.values();
00471 }
00472
00473 void CoronaPrivate::offscreenWidgetDestroyed(QObject *o)
00474 {
00475
00476
00477
00478 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(o);
00479 q->removeOffscreenWidget(widget);
00480 }
00481
00482 int Corona::numScreens() const
00483 {
00484 return 1;
00485 }
00486
00487 QRect Corona::screenGeometry(int id) const
00488 {
00489 Q_UNUSED(id);
00490 if (views().isEmpty()) {
00491 return sceneRect().toRect();
00492 } else {
00493 QGraphicsView *v = views()[0];
00494 QRect r = sceneRect().toRect();
00495 r.moveTo(v->mapToGlobal(v->pos()));
00496 return r;
00497 }
00498 }
00499
00500 QRegion Corona::availableScreenRegion(int id) const
00501 {
00502 return QRegion(screenGeometry(id));
00503 }
00504
00505 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s)
00506 {
00507 QGraphicsView *v = viewFor(item);
00508
00509 if (!v) {
00510 return QPoint(0, 0);
00511 }
00512
00513 QPoint pos;
00514 QTransform sceneTransform = item->sceneTransform();
00515
00516
00517 if (sceneTransform.isRotating()) {
00518 qreal angle = acos(sceneTransform.m11());
00519 QTransform newTransform;
00520 QPointF center = item->sceneBoundingRect().center();
00521
00522 newTransform.translate(center.x(), center.y());
00523 newTransform.rotateRadians(-angle);
00524 newTransform.translate(-center.x(), -center.y());
00525 pos = v->mapFromScene(newTransform.inverted().map(item->scenePos()));
00526 } else {
00527 pos = v->mapFromScene(item->scenePos());
00528 }
00529
00530 pos = v->mapToGlobal(pos);
00531
00532 Plasma::View *pv = dynamic_cast<Plasma::View *>(v);
00533
00534 Plasma::Location loc = Floating;
00535 if (pv && pv->containment()) {
00536 loc = pv->containment()->location();
00537 }
00538
00539 switch (loc) {
00540 case BottomEdge:
00541 case TopEdge: {
00542
00543
00544
00545 if (pos.x() + s.width() > v->geometry().right()) {
00546 pos.setX(v->geometry().right() - s.width());
00547 } else {
00548 pos.setX(qMax(pos.x(), v->geometry().left()));
00549 }
00550 break;
00551 }
00552 case LeftEdge:
00553 case RightEdge: {
00554
00555 if (pos.y() + s.height() > v->geometry().bottom()) {
00556 pos.setY(v->geometry().bottom() - s.height());
00557 } else {
00558 pos.setY(qMax(pos.y(), v->geometry().top()));
00559 }
00560 break;
00561 }
00562 default:
00563 break;
00564 }
00565 switch (loc) {
00566 case BottomEdge:
00567 pos.setY(v->geometry().y() - s.height());
00568 break;
00569 case TopEdge:
00570 pos.setY(v->geometry().bottom());
00571 break;
00572 case LeftEdge:
00573 pos.setX(v->geometry().right());
00574 break;
00575 case RightEdge:
00576 pos.setX(v->geometry().x() - s.width());
00577 break;
00578 default:
00579 if (pos.y() - s.height() > 0) {
00580 pos = QPoint(pos.x(), pos.y() - s.height());
00581 } else {
00582 pos = QPoint(pos.x(), pos.y() + (int)item->boundingRect().size().height());
00583 }
00584 }
00585
00586
00587 QRect screenRect =
00588 screenGeometry((pv && pv->containment()) ? pv->containment()->screen() : -1);
00589
00590
00591 if (loc != LeftEdge && pos.rx() + s.width() > screenRect.right()) {
00592 pos.rx() -= ((pos.rx() + s.width()) - screenRect.right());
00593 }
00594
00595 if (loc != TopEdge && pos.ry() + s.height() > screenRect.bottom()) {
00596 pos.ry() -= ((pos.ry() + s.height()) - screenRect.bottom());
00597 }
00598
00599 pos.rx() = qMax(0, pos.rx());
00600 return pos;
00601 }
00602
00603
00604
00605 void Corona::loadDefaultLayout()
00606 {
00607 }
00608
00609 void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00610 {
00611 QGraphicsScene::dragEnterEvent(event);
00612 }
00613
00614 void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
00615 {
00616 QGraphicsScene::dragLeaveEvent(event);
00617 }
00618
00619 void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00620 {
00621 QGraphicsScene::dragMoveEvent(event);
00622 }
00623
00624 ImmutabilityType Corona::immutability() const
00625 {
00626 return d->immutability;
00627 }
00628
00629 void Corona::setImmutability(const ImmutabilityType immutable)
00630 {
00631 if (d->immutability == immutable ||
00632 d->immutability == SystemImmutable) {
00633 return;
00634 }
00635
00636 kDebug() << "setting immutability to" << immutable;
00637 d->immutability = immutable;
00638 d->updateContainmentImmutability();
00639
00640 emit immutabilityChanged(immutable);
00641
00642
00643 QAction *action = d->actions.action("lock widgets");
00644 if (action) {
00645 if (d->immutability == SystemImmutable) {
00646 action->setEnabled(false);
00647 action->setVisible(false);
00648 } else {
00649 bool unlocked = d->immutability == Mutable;
00650 action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets"));
00651 action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked"));
00652 }
00653 }
00654
00655 KConfigGroup cg(config(), "General");
00656
00657
00658
00659 cg.writeEntry("immutability", (int)d->immutability);
00660 requestConfigSync();
00661 }
00662
00663 QList<Plasma::Location> Corona::freeEdges(int screen) const
00664 {
00665 QList<Plasma::Location> freeEdges;
00666 freeEdges << Plasma::TopEdge << Plasma::BottomEdge
00667 << Plasma::LeftEdge << Plasma::RightEdge;
00668
00669 foreach (Containment *containment, containments()) {
00670 if (containment->screen() == screen &&
00671 freeEdges.contains(containment->location())) {
00672 freeEdges.removeAll(containment->location());
00673 }
00674 }
00675
00676 return freeEdges;
00677 }
00678
00679 QAction *Corona::action(QString name) const
00680 {
00681 return d->actions.action(name);
00682 }
00683
00684 void Corona::addAction(QString name, QAction *action)
00685 {
00686 d->actions.addAction(name, action);
00687 }
00688
00689 KAction* Corona::addAction(QString name)
00690 {
00691 return d->actions.addAction(name);
00692 }
00693
00694 QList<QAction*> Corona::actions() const
00695 {
00696 return d->actions.actions();
00697 }
00698
00699 void Corona::enableAction(const QString &name, bool enable)
00700 {
00701 QAction *action = d->actions.action(name);
00702 if (action) {
00703 action->setEnabled(enable);
00704 action->setVisible(enable);
00705 }
00706 }
00707
00708 void Corona::updateShortcuts()
00709 {
00710 d->actions.readSettings();
00711 d->shortcutsDlg.addCollection(&d->actions);
00712 }
00713
00714 void Corona::addShortcuts(KActionCollection *newShortcuts)
00715 {
00716 d->shortcutsDlg.addCollection(newShortcuts);
00717 }
00718
00719 }
00720
00721 #include "corona.moc"
00722