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 * 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 00025 #include <QApplication> 00026 #include <QDesktopWidget> 00027 #include <QGraphicsView> 00028 #include <QGraphicsSceneDragDropEvent> 00029 #include <QGraphicsGridLayout> 00030 #include <QMimeData> 00031 #include <QPainter> 00032 #include <QTimer> 00033 00034 #include <cmath> 00035 00036 #include <kaction.h> 00037 #include <kactioncollection.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 // constant controlling how long between requesting a configuration sync 00062 // and one happening should occur. currently 10 seconds 00063 const int CONFIG_SYNC_TIMEOUT = 10000; 00064 00065 class CoronaPrivate 00066 { 00067 public: 00068 CoronaPrivate(Corona *corona) 00069 : q(corona), 00070 immutability(Mutable), 00071 mimetype("text/x-plasmoidservicename"), 00072 config(0), 00073 actions(corona) 00074 { 00075 if (KGlobal::hasMainComponent()) { 00076 configName = KGlobal::mainComponent().componentName() + "-appletsrc"; 00077 } else { 00078 configName = "plasma-appletsrc"; 00079 } 00080 } 00081 00082 ~CoronaPrivate() 00083 { 00084 qDeleteAll(containments); 00085 } 00086 00087 void init() 00088 { 00089 q->setStickyFocus(true); 00090 configSyncTimer.setSingleShot(true); 00091 QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig())); 00092 00093 //some common actions 00094 actions.setConfigGroup("Shortcuts"); 00095 00096 KAction *lockAction = actions.addAction("lock widgets"); 00097 QObject::connect(lockAction, SIGNAL(triggered(bool)), q, SLOT(toggleImmutability())); 00098 lockAction->setText(i18n("Lock Widgets")); 00099 lockAction->setAutoRepeat(true); 00100 lockAction->setIcon(KIcon("object-locked")); 00101 lockAction->setData(AbstractToolBox::ControlTool); 00102 lockAction->setShortcut(KShortcut("alt+d, l")); 00103 lockAction->setShortcutContext(Qt::ApplicationShortcut); 00104 00105 //FIXME this doesn't really belong here. desktop KCM maybe? 00106 //but should the shortcuts be per-app or really-global? 00107 //I don't know how to make kactioncollections use plasmarc 00108 KAction *action = actions.addAction("configure shortcuts"); 00109 QObject::connect(action, SIGNAL(triggered()), q, SLOT(showShortcutConfig())); 00110 action->setText(i18n("Shortcut Settings")); 00111 action->setIcon(KIcon("configure-shortcuts")); 00112 action->setAutoRepeat(false); 00113 action->setData(AbstractToolBox::ConfigureTool); 00114 //action->setShortcut(KShortcut("ctrl+h")); 00115 action->setShortcutContext(Qt::ApplicationShortcut); 00116 00117 actions.readSettings(); 00118 00119 //fake containment/applet actions 00120 KActionCollection *aActions = AppletPrivate::defaultActions(q); 00121 KActionCollection *cActions = AppletPrivate::defaultActions(q); //containment has to start with applet stuff 00122 ContainmentPrivate::addDefaultActions(cActions); //now it's really containment 00123 //grab the current stuff 00124 cActions->readSettings(); 00125 aActions->readSettings(); 00126 00127 shortcutsDlg.setModal(false); 00128 shortcutsDlg.addCollection(aActions); 00129 shortcutsDlg.addCollection(cActions); 00130 00131 QObject::connect(&shortcutsDlg, SIGNAL(saved()), q, SIGNAL(shortcutsChanged())); 00132 } 00133 00134 void showShortcutConfig() 00135 { 00136 //show a kshortcutsdialog with the actions 00137 shortcutsDlg.configure(); 00138 } 00139 00140 void toggleImmutability() 00141 { 00142 if (immutability == Mutable) { 00143 q->setImmutability(UserImmutable); 00144 } else { 00145 q->setImmutability(Mutable); 00146 } 00147 } 00148 00149 void saveLayout(KSharedConfigPtr cg) const 00150 { 00151 KConfigGroup containmentsGroup(cg, "Containments"); 00152 foreach (const Containment *containment, containments) { 00153 QString cid = QString::number(containment->id()); 00154 KConfigGroup containmentConfig(&containmentsGroup, cid); 00155 containment->save(containmentConfig); 00156 } 00157 } 00158 00159 void updateContainmentImmutability() 00160 { 00161 foreach (Containment *c, containments) { 00162 // we need to tell each containment that immutability has been altered 00163 c->updateConstraints(ImmutableConstraint); 00164 } 00165 } 00166 00167 void containmentDestroyed(QObject *obj) 00168 { 00169 // we do a static_cast here since it really isn't an Containment by this 00170 // point anymore since we are in the qobject dtor. we don't actually 00171 // try and do anything with it, we just need the value of the pointer 00172 // so this unsafe looking code is actually just fine. 00173 Containment* containment = static_cast<Plasma::Containment*>(obj); 00174 int index = containments.indexOf(containment); 00175 00176 if (index > -1) { 00177 containments.removeAt(index); 00178 q->requestConfigSync(); 00179 } 00180 } 00181 00182 void syncConfig() 00183 { 00184 q->config()->sync(); 00185 emit q->configSynced(); 00186 } 00187 00188 Containment *addContainment(const QString &name, const QVariantList &args, 00189 uint id, bool delayedInit) 00190 { 00191 QString pluginName = name; 00192 Containment *containment = 0; 00193 Applet *applet = 0; 00194 00195 //kDebug() << "Loading" << name << args << id; 00196 00197 if (pluginName.isEmpty() || pluginName == "default") { 00198 // default to the desktop containment 00199 pluginName = "desktop"; 00200 } 00201 00202 bool loadingNull = pluginName == "null"; 00203 if (!loadingNull) { 00204 applet = Applet::load(pluginName, id, args); 00205 containment = dynamic_cast<Containment*>(applet); 00206 } 00207 00208 if (!containment) { 00209 if (!loadingNull) { 00210 kDebug() << "loading of containment" << name << "failed."; 00211 } 00212 00213 // in case we got a non-Containment from Applet::loadApplet or 00214 // a null containment was requested 00215 if (applet) { 00216 // the applet probably doesn't know what's hit it, so let's pretend it can be 00217 // initialized to make assumptions in the applet's dtor safer 00218 q->addItem(applet); 00219 applet->init(); 00220 q->removeItem(applet); 00221 delete applet; 00222 } 00223 applet = containment = new Containment(0, 0, id); 00224 00225 if (loadingNull) { 00226 containment->setDrawWallpaper(false); 00227 } else { 00228 containment->setFailedToLaunch(false); 00229 } 00230 00231 // we want to provide something and don't care about the failure to launch 00232 containment->setFormFactor(Plasma::Planar); 00233 } 00234 00235 // if this is a new containment, we need to ensure that there are no stale 00236 // configuration data around 00237 if (id == 0) { 00238 KConfigGroup conf(q->config(), "Containments"); 00239 conf = KConfigGroup(&conf, QString::number(containment->id())); 00240 conf.deleteGroup(); 00241 } 00242 00243 applet->d->isContainment = true; 00244 containment->setPos(containment->d->preferredPos(q)); 00245 q->addItem(containment); 00246 applet->d->setIsContainment(true, true); 00247 containments.append(containment); 00248 00249 if (!delayedInit) { 00250 containment->init(); 00251 KConfigGroup cg = containment->config(); 00252 containment->restore(cg); 00253 containment->updateConstraints(Plasma::StartupCompletedConstraint); 00254 containment->save(cg); 00255 q->requestConfigSync(); 00256 containment->flushPendingConstraintsEvents(); 00257 } 00258 00259 QObject::connect(containment, SIGNAL(destroyed(QObject*)), 00260 q, SLOT(containmentDestroyed(QObject*))); 00261 QObject::connect(containment, SIGNAL(configNeedsSaving()), 00262 q, SLOT(requestConfigSync())); 00263 QObject::connect(containment, SIGNAL(releaseVisualFocus()), 00264 q, SIGNAL(releaseVisualFocus())); 00265 QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)), 00266 q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*))); 00267 00268 if (!delayedInit) { 00269 emit q->containmentAdded(containment); 00270 } 00271 00272 return containment; 00273 } 00274 00275 void offscreenWidgetDestroyed(QObject *); 00276 QList<Plasma::Containment *> importLayout(const KConfigBase &conf, bool mergeConfig); 00277 00278 static bool s_positioningContainments; 00279 00280 Corona *q; 00281 ImmutabilityType immutability; 00282 QString mimetype; 00283 QString configName; 00284 KSharedConfigPtr config; 00285 QTimer configSyncTimer; 00286 QList<Containment*> containments; 00287 QHash<uint, QGraphicsWidget*> offscreenWidgets; 00288 KActionCollection actions; 00289 KShortcutsDialog shortcutsDlg; 00290 QMap<Containment::Type, ContainmentActionsPluginsConfig> containmentActionsDefaults; 00291 QWeakPointer<AbstractDialogManager>dialogManager; 00292 QHash<Containment::Type, QString> toolBoxPlugins; 00293 }; 00294 00295 bool CoronaPrivate::s_positioningContainments = false; 00296 00297 Corona::Corona(QObject *parent) 00298 : QGraphicsScene(parent), 00299 d(new CoronaPrivate(this)) 00300 { 00301 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Corona ctor start"; 00302 d->init(); 00303 ToolTipManager::self()->m_corona = this; 00304 //setViewport(new QGLWidget(QGLFormat(QGL::StencilBuffer | QGL::AlphaChannel))); 00305 } 00306 00307 Corona::~Corona() 00308 { 00309 KConfigGroup trans(KGlobal::config(), "PlasmaTransientsConfig"); 00310 trans.deleteGroup(); 00311 00312 // FIXME: Same fix as in Plasma::View - make sure that when the focused widget is 00313 // destroyed we don't try to transfer it to something that's already been 00314 // deleted. 00315 clearFocus(); 00316 delete d; 00317 } 00318 00319 void Corona::setAppletMimeType(const QString &type) 00320 { 00321 d->mimetype = type; 00322 } 00323 00324 QString Corona::appletMimeType() 00325 { 00326 return d->mimetype; 00327 } 00328 00329 void Corona::saveLayout(const QString &configName) const 00330 { 00331 KSharedConfigPtr c; 00332 00333 if (configName.isEmpty() || configName == d->configName) { 00334 c = config(); 00335 } else { 00336 c = KSharedConfig::openConfig(configName); 00337 } 00338 00339 d->saveLayout(c); 00340 } 00341 00342 void Corona::exportLayout(KConfigGroup &config, QList<Containment*> containments) 00343 { 00344 foreach (const QString &group, config.groupList()) { 00345 KConfigGroup cg(&config, group); 00346 cg.deleteGroup(); 00347 } 00348 00349 //temporarily unlock so that removal works 00350 ImmutabilityType oldImm = immutability(); 00351 d->immutability = Mutable; 00352 00353 KConfigGroup dest(&config, "Containments"); 00354 KConfigGroup dummy; 00355 foreach (Plasma::Containment *c, containments) { 00356 c->save(dummy); 00357 c->config().reparent(&dest); 00358 00359 //ensure the containment is unlocked 00360 //this is done directly because we have to bypass any SystemImmutable checks 00361 c->Applet::d->immutability = Mutable; 00362 foreach (Applet *a, c->applets()) { 00363 a->d->immutability = Mutable; 00364 } 00365 00366 c->destroy(false); 00367 } 00368 00369 //restore immutability 00370 d->immutability = oldImm; 00371 00372 config.sync(); 00373 } 00374 00375 void Corona::requestConfigSync() 00376 { 00377 // TODO: should we check into our immutability before doing this? 00378 00379 //NOTE: this is a pretty simplistic model: we simply save no more than CONFIG_SYNC_TIMEOUT 00380 // after the first time this is called. not much of a heuristic for save points, but 00381 // it should at least compress these activities a bit and provide a way for applet 00382 // authors to ween themselves from the sync() disease. A more interesting/dynamic 00383 // algorithm for determining when to actually sync() to disk might be better, though. 00384 if (!d->configSyncTimer.isActive()) { 00385 d->configSyncTimer.start(CONFIG_SYNC_TIMEOUT); 00386 } 00387 } 00388 00389 void Corona::requireConfigSync() 00390 { 00391 d->syncConfig(); 00392 } 00393 00394 void Corona::initializeLayout(const QString &configName) 00395 { 00396 clearContainments(); 00397 loadLayout(configName); 00398 00399 if (d->containments.isEmpty()) { 00400 loadDefaultLayout(); 00401 if (!d->containments.isEmpty()) { 00402 requestConfigSync(); 00403 } 00404 } 00405 00406 if (config()->isImmutable()) { 00407 setImmutability(SystemImmutable); 00408 } else { 00409 KConfigGroup coronaConfig(config(), "General"); 00410 setImmutability((ImmutabilityType)coronaConfig.readEntry("immutability", (int)Mutable)); 00411 } 00412 } 00413 00414 bool containmentSortByPosition(const Containment *c1, const Containment *c2) 00415 { 00416 return c1->id() < c2->id(); 00417 } 00418 00419 void Corona::layoutContainments() 00420 { 00421 if (CoronaPrivate::s_positioningContainments) { 00422 return; 00423 } 00424 00425 CoronaPrivate::s_positioningContainments = true; 00426 00427 //TODO: we should avoid running this too often; consider compressing requests 00428 // with a timer. 00429 QList<Containment*> c = containments(); 00430 QMutableListIterator<Containment*> it(c); 00431 00432 while (it.hasNext()) { 00433 Containment *containment = it.next(); 00434 if (containment->containmentType() == Containment::PanelContainment || 00435 containment->containmentType() == Containment::CustomPanelContainment || 00436 offscreenWidgets().contains(containment)) { 00437 // weed out all containments we don't care about at all 00438 // e.g. Panels and ourself 00439 it.remove(); 00440 continue; 00441 } 00442 } 00443 00444 qSort(c.begin(), c.end(), containmentSortByPosition); 00445 00446 if (c.isEmpty()) { 00447 CoronaPrivate::s_positioningContainments = false; 00448 return; 00449 } 00450 00451 int column = 0; 00452 int x = 0; 00453 int y = 0; 00454 int rowHeight = 0; 00455 00456 it.toFront(); 00457 while (it.hasNext()) { 00458 Containment *containment = it.next(); 00459 containment->setPos(x, y); 00460 //kDebug() << ++count << "setting to" << x << y; 00461 00462 int height = containment->size().height(); 00463 if (height > rowHeight) { 00464 rowHeight = height; 00465 } 00466 00467 ++column; 00468 00469 if (column == CONTAINMENT_COLUMNS) { 00470 column = 0; 00471 x = 0; 00472 y += rowHeight + INTER_CONTAINMENT_MARGIN + TOOLBOX_MARGIN; 00473 rowHeight = 0; 00474 } else { 00475 x += containment->size().width() + INTER_CONTAINMENT_MARGIN; 00476 } 00477 //kDebug() << "column: " << column << "; x " << x << "; y" << y << "; width was" 00478 // << containment->size().width(); 00479 } 00480 00481 CoronaPrivate::s_positioningContainments = false; 00482 } 00483 00484 00485 void Corona::loadLayout(const QString &configName) 00486 { 00487 if (!configName.isEmpty() && configName != d->configName) { 00488 // if we have a new config name passed in, then use that as the config file for this Corona 00489 d->config = 0; 00490 d->configName = configName; 00491 } 00492 00493 KSharedConfigPtr conf = config(); 00494 d->importLayout(*conf, false); 00495 } 00496 00497 QList<Plasma::Containment *> Corona::importLayout(const KConfigGroup &conf) 00498 { 00499 return d->importLayout(conf, true); 00500 } 00501 00502 #ifndef KDE_NO_DEPRECATED 00503 QList<Plasma::Containment *> Corona::importLayout(const KConfigBase &conf) 00504 { 00505 return d->importLayout(conf, true); 00506 } 00507 #endif 00508 00509 QList<Plasma::Containment *> CoronaPrivate::importLayout(const KConfigBase &conf, bool mergeConfig) 00510 { 00511 if (const KConfigGroup *group = dynamic_cast<const KConfigGroup *>(&conf)) { 00512 if (!group->isValid()) { 00513 return QList<Containment *>(); 00514 } 00515 } 00516 00517 QList<Plasma::Containment *> newContainments; 00518 QSet<uint> containmentsIds; 00519 00520 foreach (Containment *containment, containments) { 00521 containmentsIds.insert(containment->id()); 00522 } 00523 00524 KConfigGroup containmentsGroup(&conf, "Containments"); 00525 00526 foreach (const QString &group, containmentsGroup.groupList()) { 00527 KConfigGroup containmentConfig(&containmentsGroup, group); 00528 00529 if (containmentConfig.entryMap().isEmpty()) { 00530 continue; 00531 } 00532 00533 uint cid = group.toUInt(); 00534 if (containmentsIds.contains(cid)) { 00535 cid = ++AppletPrivate::s_maxAppletId; 00536 } else if (cid > AppletPrivate::s_maxAppletId) { 00537 AppletPrivate::s_maxAppletId = cid; 00538 } 00539 00540 if (mergeConfig) { 00541 KConfigGroup realConf(q->config(), "Containments"); 00542 realConf = KConfigGroup(&realConf, QString::number(cid)); 00543 // in case something was there before us 00544 realConf.deleteGroup(); 00545 containmentConfig.copyTo(&realConf); 00546 } 00547 00548 //kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group; 00549 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Adding Containment" << containmentConfig.readEntry("plugin", QString()); 00550 Containment *c = addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(), cid, true); 00551 if (!c) { 00552 continue; 00553 } 00554 00555 newContainments.append(c); 00556 containmentsIds.insert(c->id()); 00557 00558 c->init(); 00559 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Init Containment" << c->pluginName(); 00560 c->restore(containmentConfig); 00561 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Restored Containment" << c->pluginName(); 00562 } 00563 00564 foreach (Containment *containment, newContainments) { 00565 containment->updateConstraints(Plasma::StartupCompletedConstraint); 00566 containment->d->initApplets(); 00567 emit q->containmentAdded(containment); 00568 kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment" << containment->name(); 00569 } 00570 00571 return newContainments; 00572 } 00573 00574 Containment *Corona::containmentForScreen(int screen, int desktop) const 00575 { 00576 foreach (Containment *containment, d->containments) { 00577 if (containment->screen() == screen && 00578 (desktop < 0 || containment->desktop() == desktop) && 00579 (containment->containmentType() == Containment::DesktopContainment || 00580 containment->containmentType() == Containment::CustomContainment)) { 00581 return containment; 00582 } 00583 } 00584 00585 return 0; 00586 } 00587 00588 Containment *Corona::containmentForScreen(int screen, int desktop, 00589 const QString &defaultPluginIfNonExistent, const QVariantList &defaultArgs) 00590 { 00591 Containment *containment = containmentForScreen(screen, desktop); 00592 if (!containment && !defaultPluginIfNonExistent.isEmpty()) { 00593 // screen requests are allowed to bypass immutability 00594 if (screen >= 0 && screen < numScreens() && 00595 desktop >= -1 && desktop < KWindowSystem::numberOfDesktops()) { 00596 containment = d->addContainment(defaultPluginIfNonExistent, defaultArgs, 0, false); 00597 if (containment) { 00598 containment->setScreen(screen, desktop); 00599 } 00600 } 00601 } 00602 00603 return containment; 00604 } 00605 QList<Containment*> Corona::containments() const 00606 { 00607 return d->containments; 00608 } 00609 00610 void Corona::clearContainments() 00611 { 00612 foreach (Containment *containment, d->containments) { 00613 containment->clearApplets(); 00614 } 00615 } 00616 00617 KSharedConfigPtr Corona::config() const 00618 { 00619 if (!d->config) { 00620 d->config = KSharedConfig::openConfig(d->configName); 00621 } 00622 00623 return d->config; 00624 } 00625 00626 Containment *Corona::addContainment(const QString &name, const QVariantList &args) 00627 { 00628 if (d->immutability == Mutable) { 00629 return d->addContainment(name, args, 0, false); 00630 } 00631 00632 return 0; 00633 } 00634 00635 Containment *Corona::addContainmentDelayed(const QString &name, const QVariantList &args) 00636 { 00637 if (d->immutability == Mutable) { 00638 return d->addContainment(name, args, 0, true); 00639 } 00640 00641 return 0; 00642 } 00643 00644 void Corona::mapAnimation(Animator::Animation from, Animator::Animation to) 00645 { 00646 AnimatorPrivate::mapAnimation(from, to); 00647 } 00648 00649 void Corona::mapAnimation(Animator::Animation from, const QString &to) 00650 { 00651 AnimatorPrivate::mapAnimation(from, to); 00652 } 00653 00654 void Corona::addOffscreenWidget(QGraphicsWidget *widget) 00655 { 00656 foreach (QGraphicsWidget *w, d->offscreenWidgets) { 00657 if (w == widget) { 00658 kDebug() << "widget is already an offscreen widget!"; 00659 return; 00660 } 00661 } 00662 00663 //search for an empty spot in the topleft quadrant of the scene. each 'slot' is QWIDGETSIZE_MAX 00664 //x QWIDGETSIZE_MAX, so we're guaranteed to never have to move widgets once they're placed here. 00665 int i = 0; 00666 while (d->offscreenWidgets.contains(i)) { 00667 i++; 00668 } 00669 00670 d->offscreenWidgets[i] = widget; 00671 widget->setPos((-i - 1) * QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX); 00672 00673 QGraphicsWidget *pw = widget->parentWidget(); 00674 widget->setParentItem(0); 00675 widget->setParent(pw); 00676 00677 //kDebug() << "adding offscreen widget at slot " << i; 00678 if (!widget->scene()) { 00679 addItem(widget); 00680 } 00681 00682 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(offscreenWidgetDestroyed(QObject*))); 00683 } 00684 00685 void Corona::removeOffscreenWidget(QGraphicsWidget *widget) 00686 { 00687 QMutableHashIterator<uint, QGraphicsWidget *> it(d->offscreenWidgets); 00688 00689 while (it.hasNext()) { 00690 if (it.next().value() == widget) { 00691 it.remove(); 00692 return; 00693 } 00694 } 00695 } 00696 00697 QList <QGraphicsWidget *> Corona::offscreenWidgets() const 00698 { 00699 return d->offscreenWidgets.values(); 00700 } 00701 00702 void CoronaPrivate::offscreenWidgetDestroyed(QObject *o) 00703 { 00704 // at this point, it's just a QObject, not a QGraphicsWidget, but we still need 00705 // a pointer of the appropriate type. 00706 // WARNING: DO NOT USE THE WIDGET POINTER FOR ANYTHING OTHER THAN POINTER COMPARISONS 00707 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(o); 00708 q->removeOffscreenWidget(widget); 00709 } 00710 00711 int Corona::numScreens() const 00712 { 00713 return 1; 00714 } 00715 00716 QRect Corona::screenGeometry(int id) const 00717 { 00718 Q_UNUSED(id); 00719 if (views().isEmpty()) { 00720 return sceneRect().toRect(); 00721 } else { 00722 QGraphicsView *v = views()[0]; 00723 QRect r = sceneRect().toRect(); 00724 r.moveTo(v->mapToGlobal(QPoint(0, 0))); 00725 return r; 00726 } 00727 } 00728 00729 QRegion Corona::availableScreenRegion(int id) const 00730 { 00731 return QRegion(screenGeometry(id)); 00732 } 00733 00734 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s) 00735 { 00736 return popupPosition(item, s, Qt::AlignLeft); 00737 } 00738 00739 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s, Qt::AlignmentFlag alignment) 00740 { 00741 // TODO: merge both methods (also these in Applet) into one (with optional alignment) when we can break compatibility 00742 // TODO: add support for more flags in the future? 00743 00744 QGraphicsView *v = viewFor(item); 00745 00746 if (!v) { 00747 return QPoint(0, 0); 00748 } 00749 00750 QPoint pos; 00751 QTransform sceneTransform = item->sceneTransform(); 00752 00753 //swap direction if necessary 00754 if (QApplication::isRightToLeft() && alignment != Qt::AlignCenter) { 00755 if (alignment == Qt::AlignRight) { 00756 alignment = Qt::AlignLeft; 00757 } else { 00758 alignment = Qt::AlignRight; 00759 } 00760 } 00761 00762 //if the applet is rotated the popup position has to be un-transformed 00763 if (sceneTransform.isRotating()) { 00764 qreal angle = acos(sceneTransform.m11()); 00765 QTransform newTransform; 00766 QPointF center = item->sceneBoundingRect().center(); 00767 00768 newTransform.translate(center.x(), center.y()); 00769 newTransform.rotateRadians(-angle); 00770 newTransform.translate(-center.x(), -center.y()); 00771 pos = v->mapFromScene(newTransform.inverted().map(item->scenePos())); 00772 } else { 00773 pos = v->mapFromScene(item->scenePos()); 00774 } 00775 00776 pos = v->mapToGlobal(pos); 00777 //kDebug() << "==> position is" << item->scenePos() << v->mapFromScene(item->scenePos()) << pos; 00778 Plasma::View *pv = dynamic_cast<Plasma::View *>(v); 00779 00780 Plasma::Location loc = Floating; 00781 if (pv && pv->containment()) { 00782 loc = pv->containment()->location(); 00783 } 00784 00785 switch (loc) { 00786 case BottomEdge: 00787 case TopEdge: { 00788 if (alignment == Qt::AlignCenter) { 00789 pos.setX(pos.x() + item->boundingRect().width()/2 - s.width()/2); 00790 } else if (alignment == Qt::AlignRight) { 00791 pos.setX(pos.x() + item->boundingRect().width() - s.width()); 00792 } 00793 00794 if (pos.x() + s.width() > v->geometry().right()) { 00795 pos.setX(v->geometry().right() - s.width()); 00796 } else { 00797 pos.setX(qMax(pos.x(), v->geometry().left())); 00798 } 00799 break; 00800 } 00801 case LeftEdge: 00802 case RightEdge: { 00803 if (alignment == Qt::AlignCenter) { 00804 pos.setY(pos.y() + item->boundingRect().height()/2 - s.height()/2); 00805 } else if (alignment == Qt::AlignRight) { 00806 pos.setY(pos.y() + item->boundingRect().height() - s.height()); 00807 } 00808 00809 if (pos.y() + s.height() > v->geometry().bottom()) { 00810 pos.setY(v->geometry().bottom() - s.height()); 00811 } else { 00812 pos.setY(qMax(pos.y(), v->geometry().top())); 00813 } 00814 break; 00815 } 00816 default: 00817 if (alignment == Qt::AlignCenter) { 00818 pos.setX(pos.x() + item->boundingRect().width()/2 - s.width()/2); 00819 } else if (alignment == Qt::AlignRight) { 00820 pos.setX(pos.x() + item->boundingRect().width() - s.width()); 00821 } 00822 break; 00823 } 00824 00825 switch (loc) { 00826 case BottomEdge: 00827 pos.setY(v->geometry().y() - s.height()); 00828 break; 00829 case TopEdge: 00830 pos.setY(v->geometry().bottom()); 00831 break; 00832 case LeftEdge: 00833 pos.setX(v->geometry().right()); 00834 break; 00835 case RightEdge: 00836 pos.setX(v->geometry().x() - s.width()); 00837 break; 00838 default: 00839 if (pos.y() - s.height() > 0) { 00840 pos.ry() = pos.y() - s.height(); 00841 } else { 00842 pos.ry() = pos.y() + (int)item->boundingRect().size().height() + 1; 00843 } 00844 } 00845 00846 //are we out of screen? 00847 int screen = ((pv && pv->containment()) ? pv->containment()->screen() : -1); 00848 if (screen == -1) { 00849 if (pv) { 00850 screen = pv->screen(); 00851 } else { 00852 // fall back to asking the actual system what screen the view is on 00853 // in the case we are dealing with a non-PlasmaView QGraphicsView 00854 screen = QApplication::desktop()->screenNumber(v); 00855 } 00856 } 00857 00858 QRect screenRect = screenGeometry(screen); 00859 //kDebug() << "==> rect for" << screen << "is" << screenRect; 00860 00861 if (loc != LeftEdge && pos.x() + s.width() > screenRect.right()) { 00862 pos.rx() -= ((pos.x() + s.width()) - screenRect.right()); 00863 } 00864 00865 if (loc != TopEdge && pos.y() + s.height() > screenRect.bottom()) { 00866 pos.ry() -= ((pos.y() + s.height()) - screenRect.bottom()); 00867 } 00868 00869 pos.rx() = qMax(0, pos.x()); 00870 return pos; 00871 } 00872 00873 void Corona::loadDefaultLayout() 00874 { 00875 } 00876 00877 void Corona::setPreferredToolBoxPlugin(const Containment::Type type, const QString &plugin) 00878 { 00879 d->toolBoxPlugins[type] = plugin; 00880 //TODO: react to plugin changes on the fly? still don't see the use case (maybe for laptops that become tablets?) 00881 } 00882 00883 QString Corona::preferredToolBoxPlugin(const Containment::Type type) const 00884 { 00885 return d->toolBoxPlugins.value(type); 00886 } 00887 00888 void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event) 00889 { 00890 QGraphicsScene::dragEnterEvent(event); 00891 } 00892 00893 void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) 00894 { 00895 QGraphicsScene::dragLeaveEvent(event); 00896 } 00897 00898 void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event) 00899 { 00900 QGraphicsScene::dragMoveEvent(event); 00901 } 00902 00903 ImmutabilityType Corona::immutability() const 00904 { 00905 return d->immutability; 00906 } 00907 00908 void Corona::setImmutability(const ImmutabilityType immutable) 00909 { 00910 if (d->immutability == immutable || d->immutability == SystemImmutable) { 00911 return; 00912 } 00913 00914 kDebug() << "setting immutability to" << immutable; 00915 d->immutability = immutable; 00916 d->updateContainmentImmutability(); 00917 //tell non-containments that might care (like plasmaapp or a custom corona) 00918 emit immutabilityChanged(immutable); 00919 00920 //update our actions 00921 QAction *action = d->actions.action("lock widgets"); 00922 if (action) { 00923 if (d->immutability == SystemImmutable) { 00924 action->setEnabled(false); 00925 action->setVisible(false); 00926 } else { 00927 bool unlocked = d->immutability == Mutable; 00928 action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets")); 00929 action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked")); 00930 action->setEnabled(true); 00931 action->setVisible(true); 00932 } 00933 } 00934 00935 if (d->immutability != SystemImmutable) { 00936 KConfigGroup cg(config(), "General"); 00937 00938 // we call the dptr member directly for locked since isImmutable() 00939 // also checks kiosk and parent containers 00940 cg.writeEntry("immutability", (int)d->immutability); 00941 requestConfigSync(); 00942 } 00943 } 00944 00945 QList<Plasma::Location> Corona::freeEdges(int screen) const 00946 { 00947 QList<Plasma::Location> freeEdges; 00948 freeEdges << Plasma::TopEdge << Plasma::BottomEdge 00949 << Plasma::LeftEdge << Plasma::RightEdge; 00950 00951 foreach (Containment *containment, containments()) { 00952 if (containment->screen() == screen && 00953 freeEdges.contains(containment->location())) { 00954 freeEdges.removeAll(containment->location()); 00955 } 00956 } 00957 00958 return freeEdges; 00959 } 00960 00961 QAction *Corona::action(QString name) const 00962 { 00963 return d->actions.action(name); 00964 } 00965 00966 void Corona::addAction(QString name, QAction *action) 00967 { 00968 d->actions.addAction(name, action); 00969 } 00970 00971 KAction* Corona::addAction(QString name) 00972 { 00973 return d->actions.addAction(name); 00974 } 00975 00976 QList<QAction*> Corona::actions() const 00977 { 00978 return d->actions.actions(); 00979 } 00980 00981 void Corona::enableAction(const QString &name, bool enable) 00982 { 00983 QAction *action = d->actions.action(name); 00984 if (action) { 00985 action->setEnabled(enable); 00986 action->setVisible(enable); 00987 } 00988 } 00989 00990 void Corona::updateShortcuts() 00991 { 00992 d->actions.readSettings(); 00993 d->shortcutsDlg.addCollection(&d->actions); 00994 } 00995 00996 void Corona::addShortcuts(KActionCollection *newShortcuts) 00997 { 00998 d->shortcutsDlg.addCollection(newShortcuts); 00999 } 01000 01001 void Corona::setContainmentActionsDefaults(Containment::Type containmentType, const ContainmentActionsPluginsConfig &config) 01002 { 01003 d->containmentActionsDefaults.insert(containmentType, config); 01004 } 01005 01006 ContainmentActionsPluginsConfig Corona::containmentActionsDefaults(Containment::Type containmentType) 01007 { 01008 return d->containmentActionsDefaults.value(containmentType); 01009 } 01010 01011 void Corona::setDialogManager(AbstractDialogManager *dialogManager) 01012 { 01013 d->dialogManager = dialogManager; 01014 } 01015 01016 AbstractDialogManager *Corona::dialogManager() 01017 { 01018 return d->dialogManager.data(); 01019 } 01020 01021 } // namespace Plasma 01022 01023 #include "corona.moc" 01024
KDE 4.6 API Reference