~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/desktop/shell/desktopcorona.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   Copyright 2008 Aaron Seigo <aseigo@kde.org>
 
3
 *
 
4
 *   This program is free software; you can redistribute it and/or modify
 
5
 *   it under the terms of the GNU Library General Public License as
 
6
 *   published by the Free Software Foundation; either version 2, or
 
7
 *   (at your option) any later version.
 
8
 *
 
9
 *   This program is distributed in the hope that it will be useful,
 
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 *   GNU General Public License for more details
 
13
 *
 
14
 *   You should have received a copy of the GNU Library General Public
 
15
 *   License along with this program; if not, write to the
 
16
 *   Free Software Foundation, Inc.,
 
17
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
18
 */
 
19
 
 
20
#include "desktopcorona.h"
 
21
 
 
22
#include <QAction>
 
23
#include <QApplication>
 
24
#include <QDir>
 
25
#include <QGraphicsLayout>
 
26
#include <QTimer>
 
27
#include <QMenu>
 
28
#include <QSignalMapper>
 
29
 
 
30
#include <KAction>
 
31
#include <KDebug>
 
32
#include <KDialog>
 
33
#include <KGlobal>
 
34
#include <KGlobalSettings>
 
35
#include <KServiceTypeTrader>
 
36
#include <KStandardDirs>
 
37
#include <KSycoca>
 
38
#include <KWindowSystem>
 
39
 
 
40
#include <Plasma/AbstractToolBox>
 
41
#include <Plasma/Containment>
 
42
#include <plasma/containmentactionspluginsconfig.h>
 
43
#include <Plasma/Context>
 
44
#include <Plasma/DataEngineManager>
 
45
#include <Plasma/Package>
 
46
 
 
47
#include <kephal/screens.h>
 
48
 
 
49
#include <scripting/layouttemplatepackagestructure.h>
 
50
 
 
51
#include "activity.h"
 
52
#include "kactivitycontroller.h"
 
53
#include "kactivityinfo.h"
 
54
#include "panelview.h"
 
55
#include "plasmaapp.h"
 
56
#include "plasma-shell-desktop.h"
 
57
#include "scripting/desktopscriptengine.h"
 
58
 
 
59
static const QString s_panelTemplatesPath("plasma-layout-templates/panels/*");
 
60
 
 
61
DesktopCorona::DesktopCorona(QObject *parent)
 
62
    : Plasma::Corona(parent),
 
63
      m_addPanelAction(0),
 
64
      m_addPanelsMenu(0),
 
65
      m_activityController(new KActivityController(this))
 
66
{
 
67
    init();
 
68
}
 
69
 
 
70
DesktopCorona::~DesktopCorona()
 
71
{
 
72
    delete m_addPanelsMenu;
 
73
}
 
74
 
 
75
void DesktopCorona::init()
 
76
{
 
77
    setPreferredToolBoxPlugin(Plasma::Containment::DesktopContainment, "org.kde.desktoptoolbox");
 
78
    setPreferredToolBoxPlugin(Plasma::Containment::CustomContainment, "org.kde.desktoptoolbox");
 
79
    setPreferredToolBoxPlugin(Plasma::Containment::PanelContainment, "org.kde.paneltoolbox");
 
80
    setPreferredToolBoxPlugin(Plasma::Containment::CustomPanelContainment, "org.kde.paneltoolbox");
 
81
 
 
82
    kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "DesktopCorona init start" << "(line:" << __LINE__ << ")";
 
83
    Kephal::Screens *screens = Kephal::Screens::self();
 
84
    connect(screens, SIGNAL(screenAdded(Kephal::Screen *)), SLOT(screenAdded(Kephal::Screen *)));
 
85
    connect(KWindowSystem::self(), SIGNAL(workAreaChanged()), this, SIGNAL(availableScreenRegionChanged()));
 
86
 
 
87
    Plasma::ContainmentActionsPluginsConfig desktopPlugins;
 
88
    desktopPlugins.addPlugin(Qt::NoModifier, Qt::Vertical, "switchdesktop");
 
89
    desktopPlugins.addPlugin(Qt::NoModifier, Qt::MidButton, "paste");
 
90
    desktopPlugins.addPlugin(Qt::NoModifier, Qt::RightButton, "contextmenu");
 
91
    Plasma::ContainmentActionsPluginsConfig panelPlugins;
 
92
    panelPlugins.addPlugin(Qt::NoModifier, Qt::RightButton, "contextmenu");
 
93
 
 
94
    setContainmentActionsDefaults(Plasma::Containment::DesktopContainment, desktopPlugins);
 
95
    setContainmentActionsDefaults(Plasma::Containment::CustomContainment, desktopPlugins);
 
96
    setContainmentActionsDefaults(Plasma::Containment::PanelContainment, panelPlugins);
 
97
    setContainmentActionsDefaults(Plasma::Containment::CustomPanelContainment, panelPlugins);
 
98
 
 
99
    checkAddPanelAction();
 
100
 
 
101
    //why do these actions belong to plasmaapp?
 
102
    //because it makes the keyboard shortcuts work.
 
103
    KAction *action = new KAction(PlasmaApp::self());
 
104
    action->setText(i18n("Next Activity"));
 
105
    action->setObjectName( QLatin1String("Next Activity" )); // NO I18N
 
106
    action->setGlobalShortcut(KShortcut(Qt::META + Qt::Key_Tab));
 
107
    connect(action, SIGNAL(triggered()), this, SLOT(activateNextActivity()));
 
108
 
 
109
    action = new KAction(PlasmaApp::self());
 
110
    action->setText(i18n("Previous Activity"));
 
111
    action->setObjectName( QLatin1String("Previous Activity" )); // NO I18N
 
112
    action->setGlobalShortcut(KShortcut(Qt::META + Qt::SHIFT + Qt::Key_Tab));
 
113
    connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousActivity()));
 
114
 
 
115
    connect(this, SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)),
 
116
            this, SLOT(updateImmutability(Plasma::ImmutabilityType)));
 
117
    connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), this, SLOT(checkAddPanelAction(QStringList)));
 
118
 
 
119
    connect(m_activityController, SIGNAL(currentActivityChanged(QString)), this, SLOT(currentActivityChanged(QString)));
 
120
    connect(m_activityController, SIGNAL(activityAdded(const QString &)), this, SLOT(activityAdded(const QString &)));
 
121
    connect(m_activityController, SIGNAL(activityRemoved(const QString &)), this, SLOT(activityRemoved(const QString &)));
 
122
 
 
123
    mapAnimation(Plasma::Animator::AppearAnimation, Plasma::Animator::ZoomAnimation);
 
124
    mapAnimation(Plasma::Animator::DisappearAnimation, Plasma::Animator::ZoomAnimation);
 
125
    kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "DesktopCorona init end" << "(line:" << __LINE__ << ")";
 
126
}
 
127
 
 
128
void DesktopCorona::checkAddPanelAction(const QStringList &sycocaChanges)
 
129
{
 
130
    if (!sycocaChanges.isEmpty() && !sycocaChanges.contains("services")) {
 
131
        return;
 
132
    }
 
133
 
 
134
    delete m_addPanelAction;
 
135
    m_addPanelAction = 0;
 
136
 
 
137
    delete m_addPanelsMenu;
 
138
    m_addPanelsMenu = 0;
 
139
 
 
140
    KPluginInfo::List panelContainmentPlugins = Plasma::Containment::listContainmentsOfType("panel");
 
141
    const QString constraint = QString("[X-Plasma-Shell] == '%1' and 'panel' ~in [X-Plasma-ContainmentCategories]")
 
142
                                      .arg(KGlobal::mainComponent().componentName());
 
143
    KService::List templates = KServiceTypeTrader::self()->query("Plasma/LayoutTemplate", constraint);
 
144
 
 
145
    if (panelContainmentPlugins.count() + templates.count() == 1) {
 
146
        m_addPanelAction = new QAction(i18n("Add Panel"), this);
 
147
        m_addPanelAction->setData(Plasma::AbstractToolBox::AddTool);
 
148
        connect(m_addPanelAction, SIGNAL(triggered(bool)), this, SLOT(addPanel()));
 
149
    } else if (!panelContainmentPlugins.isEmpty()) {
 
150
        m_addPanelsMenu = new QMenu;
 
151
        m_addPanelAction = m_addPanelsMenu->menuAction();
 
152
        m_addPanelAction->setText(i18n("Add Panel"));
 
153
        m_addPanelAction->setData(Plasma::AbstractToolBox::AddTool);
 
154
        kDebug() << "populateAddPanelsMenu" << panelContainmentPlugins.count();
 
155
        connect(m_addPanelsMenu, SIGNAL(aboutToShow()), this, SLOT(populateAddPanelsMenu()));
 
156
        connect(m_addPanelsMenu, SIGNAL(triggered(QAction*)), this, SLOT(addPanel(QAction*)));
 
157
    }
 
158
 
 
159
    if (m_addPanelAction) {
 
160
        m_addPanelAction->setIcon(KIcon("list-add"));
 
161
        addAction("add panel", m_addPanelAction);
 
162
    }
 
163
}
 
164
 
 
165
void DesktopCorona::updateImmutability(Plasma::ImmutabilityType immutability)
 
166
{
 
167
    if (m_addPanelAction) {
 
168
        m_addPanelAction->setEnabled(immutability == Plasma::Mutable);
 
169
    }
 
170
}
 
171
 
 
172
void DesktopCorona::checkScreens(bool signalWhenExists)
 
173
{
 
174
    // quick sanity check to ensure we have containments for each screen
 
175
    int num = numScreens();
 
176
    for (int i = 0; i < num; ++i) {
 
177
        checkScreen(i, signalWhenExists);
 
178
    }
 
179
}
 
180
 
 
181
void DesktopCorona::checkScreen(int screen, bool signalWhenExists)
 
182
{
 
183
    // signalWhenExists is there to allow PlasmaApp to know when to create views
 
184
    // it does this only on containment addition, but in the case of a screen being
 
185
    // added and the containment already existing for that screen, no signal is emitted
 
186
    // and so PlasmaApp does not know that it needs to create a view for it. to avoid
 
187
    // taking care of that case in PlasmaApp (which would duplicate some of the code below,
 
188
    // DesktopCorona will, when signalWhenExists is true, emit a containmentAdded signal
 
189
    // even if the containment actually existed prior to this method being called.
 
190
    //
 
191
    //note: hte signal actually triggers view creation only for panels, atm.
 
192
    //desktop views are created in response to containment's screenChanged signal instead, which is
 
193
    //buggy (sometimes the containment thinks it's already on the screen, so no view is created)
 
194
 
 
195
    Activity *currentActivity = activity(m_activityController->currentActivity());
 
196
    //ensure the desktop(s) have a containment and view
 
197
    if (AppSettings::perVirtualDesktopViews()) {
 
198
        int numDesktops = KWindowSystem::numberOfDesktops();
 
199
 
 
200
        for (int j = 0; j < numDesktops; ++j) {
 
201
            checkDesktop(currentActivity, signalWhenExists, screen, j);
 
202
        }
 
203
    } else {
 
204
        checkDesktop(currentActivity, signalWhenExists, screen);
 
205
    }
 
206
 
 
207
    //ensure the panels get views too
 
208
    if (signalWhenExists) {
 
209
        foreach (Plasma::Containment * c, containments()) {
 
210
            if (c->screen() != screen) {
 
211
                continue;
 
212
            }
 
213
 
 
214
            Plasma::Containment::Type t = c->containmentType();
 
215
            if (t == Plasma::Containment::PanelContainment ||
 
216
                t == Plasma::Containment::CustomPanelContainment) {
 
217
                emit containmentAdded(c);
 
218
            }
 
219
        }
 
220
    }
 
221
}
 
222
 
 
223
void DesktopCorona::checkDesktop(Activity *activity, bool signalWhenExists, int screen, int desktop)
 
224
{
 
225
    Plasma::Containment *c = activity->containmentForScreen(screen, desktop);
 
226
 
 
227
    if (!c) {
 
228
        return;
 
229
    }
 
230
 
 
231
    c->setScreen(screen, desktop);
 
232
    c->flushPendingConstraintsEvents();
 
233
    requestConfigSync();
 
234
 
 
235
    if (signalWhenExists) {
 
236
        emit containmentAdded(c);
 
237
    }
 
238
}
 
239
 
 
240
int DesktopCorona::numScreens() const
 
241
{
 
242
#ifdef Q_WS_X11
 
243
    if (KGlobalSettings::isMultiHead()) {
 
244
        // with multihead, we "lie" and say that there is only one screen
 
245
        return 1;
 
246
    }
 
247
#endif
 
248
 
 
249
    return Kephal::ScreenUtils::numScreens();
 
250
}
 
251
 
 
252
QRect DesktopCorona::screenGeometry(int id) const
 
253
{
 
254
#ifdef Q_WS_X11
 
255
    if (KGlobalSettings::isMultiHead()) {
 
256
        // with multihead, we "lie" and say that screen 0 is the default screen, in fact, we pretend
 
257
        // we have only one screen at all
 
258
        Display *dpy = XOpenDisplay(NULL);
 
259
        if (dpy) {
 
260
            id = DefaultScreen(dpy);
 
261
            XCloseDisplay(dpy);
 
262
        }
 
263
    }
 
264
#endif
 
265
 
 
266
    return Kephal::ScreenUtils::screenGeometry(id);
 
267
}
 
268
 
 
269
QRegion DesktopCorona::availableScreenRegion(int id) const
 
270
{
 
271
#ifdef Q_WS_X11
 
272
    if (KGlobalSettings::isMultiHead()) {
 
273
        // with multihead, we "lie" and say that screen 0 is the default screen, in fact, we pretend
 
274
        // we have only one screen at all
 
275
        Display *dpy = XOpenDisplay(NULL);
 
276
        if (dpy) {
 
277
            id = DefaultScreen(dpy);
 
278
            XCloseDisplay(dpy);
 
279
        }
 
280
    }
 
281
#endif
 
282
 
 
283
    if (id < 0) {
 
284
        id = Kephal::ScreenUtils::primaryScreenId();
 
285
    }
 
286
 
 
287
    QRegion r(screenGeometry(id));
 
288
    foreach (PanelView *view, PlasmaApp::self()->panelViews()) {
 
289
        if (view->screen() == id && view->visibilityMode() == PanelView::NormalPanel) {
 
290
            r = r.subtracted(view->geometry());
 
291
        }
 
292
    }
 
293
 
 
294
    return r;
 
295
}
 
296
 
 
297
QRect DesktopCorona::availableScreenRect(int id) const
 
298
{
 
299
    if (id < 0) {
 
300
        id = Kephal::ScreenUtils::primaryScreenId();
 
301
    }
 
302
 
 
303
    QRect r(screenGeometry(id));
 
304
 
 
305
    foreach (PanelView *view, PlasmaApp::self()->panelViews()) {
 
306
        if (view->screen() == id && view->visibilityMode() == PanelView::NormalPanel) {
 
307
            QRect v = view->geometry();
 
308
            switch (view->location()) {
 
309
                case Plasma::TopEdge:
 
310
                    if (v.bottom() > r.top()) {
 
311
                        r.setTop(v.bottom());
 
312
                    }
 
313
                    break;
 
314
 
 
315
                case Plasma::BottomEdge:
 
316
                    if (v.top() < r.bottom()) {
 
317
                        r.setBottom(v.top());
 
318
                    }
 
319
                    break;
 
320
 
 
321
                case Plasma::LeftEdge:
 
322
                    if (v.right() > r.left()) {
 
323
                        r.setLeft(v.right());
 
324
                    }
 
325
                    break;
 
326
 
 
327
                case Plasma::RightEdge:
 
328
                    if (v.left() < r.right()) {
 
329
                        r.setRight(v.left());
 
330
                    }
 
331
                    break;
 
332
 
 
333
                default:
 
334
                    break;
 
335
            }
 
336
        }
 
337
    }
 
338
 
 
339
    return r;
 
340
}
 
341
 
 
342
int DesktopCorona::screenId(const QPoint &pos) const
 
343
{
 
344
#ifdef Q_WS_X11
 
345
    if (KGlobalSettings::isMultiHead()) {
 
346
        // with multihead, we "lie" and say that there is only one screen
 
347
        return 0;
 
348
    }
 
349
#endif
 
350
 
 
351
    return Kephal::ScreenUtils::screenId(pos);
 
352
}
 
353
 
 
354
void DesktopCorona::processUpdateScripts()
 
355
{
 
356
    evaluateScripts(WorkspaceScripting::ScriptEngine::pendingUpdateScripts());
 
357
}
 
358
 
 
359
void DesktopCorona::evaluateScripts(const QStringList &scripts)
 
360
{
 
361
    foreach (const QString &script, scripts) {
 
362
        WorkspaceScripting::DesktopScriptEngine scriptEngine(this);
 
363
        connect(&scriptEngine, SIGNAL(printError(QString)), this, SLOT(printScriptError(QString)));
 
364
        connect(&scriptEngine, SIGNAL(print(QString)), this, SLOT(printScriptMessage(QString)));
 
365
        connect(&scriptEngine, SIGNAL(createPendingPanelViews()), PlasmaApp::self(), SLOT(createWaitingPanels()));
 
366
 
 
367
        QFile file(script);
 
368
        if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
 
369
            QString code = file.readAll();
 
370
            kDebug() << "evaluating startup script:" << script;
 
371
            scriptEngine.evaluateScript(code);
 
372
        }
 
373
    }
 
374
}
 
375
 
 
376
void DesktopCorona::printScriptError(const QString &error)
 
377
{
 
378
    kWarning() << "Startup script errror:" << error;
 
379
}
 
380
 
 
381
void DesktopCorona::printScriptMessage(const QString &error)
 
382
{
 
383
    kDebug() << "Startup script: " << error;
 
384
}
 
385
 
 
386
void DesktopCorona::loadDefaultLayout()
 
387
{
 
388
    evaluateScripts(WorkspaceScripting::ScriptEngine::defaultLayoutScripts());
 
389
    if (containments().isEmpty()) {
 
390
        QString defaultConfig = KStandardDirs::locate("appdata", "plasma-default-layoutrc");
 
391
        if (!defaultConfig.isEmpty()) {
 
392
            kDebug() << "attempting to load the default layout from:" << defaultConfig;
 
393
            loadLayout(defaultConfig);
 
394
            QTimer::singleShot(1000, this, SLOT(saveDefaultSetup()));
 
395
        }
 
396
    }
 
397
 
 
398
    QTimer::singleShot(1000, this, SLOT(saveDefaultSetup()));
 
399
}
 
400
 
 
401
void DesktopCorona::saveDefaultSetup()
 
402
{
 
403
    // a "null" KConfigGroup is used to force a save into the config file
 
404
    KConfigGroup invalidConfig;
 
405
 
 
406
    foreach (Plasma::Containment *containment, containments()) {
 
407
        containment->save(invalidConfig);
 
408
        foreach (Plasma::Applet* applet, containment->applets()) {
 
409
            applet->save(invalidConfig);
 
410
        }
 
411
    }
 
412
 
 
413
    requestConfigSync();
 
414
}
 
415
 
 
416
Plasma::Applet *DesktopCorona::loadDefaultApplet(const QString &pluginName, Plasma::Containment *c)
 
417
{
 
418
    QVariantList args;
 
419
    Plasma::Applet *applet = Plasma::Applet::load(pluginName, 0, args);
 
420
 
 
421
    if (applet) {
 
422
        c->addApplet(applet);
 
423
    }
 
424
 
 
425
    return applet;
 
426
}
 
427
 
 
428
void DesktopCorona::screenAdded(Kephal::Screen *s)
 
429
{
 
430
    kDebug() << s->id();
 
431
    checkScreen(s->id(), true);
 
432
}
 
433
 
 
434
void DesktopCorona::populateAddPanelsMenu()
 
435
{
 
436
    m_addPanelsMenu->clear();
 
437
    const KPluginInfo emptyInfo;
 
438
 
 
439
    KPluginInfo::List panelContainmentPlugins = Plasma::Containment::listContainmentsOfType("panel");
 
440
    QMap<QString, QPair<KPluginInfo, KService::Ptr> > sorted;
 
441
    foreach (const KPluginInfo &plugin, panelContainmentPlugins) {
 
442
        //FIXME: a better way to filter out what is not wanted?
 
443
        if (!plugin.property("X-Plasma-ContainmentCategories").value<QStringList>().contains("netbook")) {
 
444
            sorted.insert(plugin.name(), qMakePair(plugin, KService::Ptr(0)));
 
445
        }
 
446
    }
 
447
 
 
448
    const QString constraint = QString("[X-Plasma-Shell] == '%1' and 'panel' in [X-Plasma-ContainmentCategories]")
 
449
                                      .arg(KGlobal::mainComponent().componentName());
 
450
    KService::List templates = KServiceTypeTrader::self()->query("Plasma/LayoutTemplate", constraint);
 
451
    foreach (const KService::Ptr &service, templates) {
 
452
        sorted.insert(service->name(), qMakePair(emptyInfo, service));
 
453
    }
 
454
 
 
455
    QMapIterator<QString, QPair<KPluginInfo, KService::Ptr> > it(sorted);
 
456
    Plasma::PackageStructure::Ptr templateStructure(new WorkspaceScripting::LayoutTemplatePackageStructure);
 
457
    while (it.hasNext()) {
 
458
        it.next();
 
459
        QPair<KPluginInfo, KService::Ptr> pair = it.value();
 
460
        if (pair.first.isValid()) {
 
461
            KPluginInfo plugin = pair.first;
 
462
            QAction *action = m_addPanelsMenu->addAction(plugin.name());
 
463
            if (!plugin.icon().isEmpty()) {
 
464
                action->setIcon(KIcon(plugin.icon()));
 
465
            }
 
466
 
 
467
            action->setData(plugin.pluginName());
 
468
        } else {
 
469
            //FIXME: proper names
 
470
            KPluginInfo info(pair.second);
 
471
            const QString path = KStandardDirs::locate("data", templateStructure->defaultPackageRoot() + '/' + info.pluginName() + '/');
 
472
            if (!path.isEmpty()) {
 
473
                Plasma::Package package(path, templateStructure);
 
474
                const QString scriptFile = package.filePath("mainscript");
 
475
                if (!scriptFile.isEmpty()) {
 
476
                    QAction *action = m_addPanelsMenu->addAction(info.name());
 
477
                    action->setData(QString::fromLatin1("plasma-desktop-template:%1").arg(scriptFile));
 
478
                }
 
479
            }
 
480
        }
 
481
    }
 
482
}
 
483
 
 
484
void DesktopCorona::addPanel()
 
485
{
 
486
    KPluginInfo::List panelPlugins = Plasma::Containment::listContainmentsOfType("panel");
 
487
 
 
488
    if (!panelPlugins.isEmpty()) {
 
489
        addPanel(panelPlugins.first().pluginName());
 
490
    }
 
491
}
 
492
 
 
493
void DesktopCorona::addPanel(QAction *action)
 
494
{
 
495
    const QString plugin = action->data().toString();
 
496
    if (plugin.startsWith("plasma-desktop-template:")) {
 
497
        evaluateScripts(QStringList() << plugin.right(plugin.length() - qstrlen("plasma-desktop-template:")));
 
498
    } else if (!plugin.isEmpty()) {
 
499
        addPanel(plugin);
 
500
    }
 
501
}
 
502
 
 
503
void DesktopCorona::addPanel(const QString &plugin)
 
504
{
 
505
    Plasma::Containment *panel = addContainment(plugin);
 
506
    if (!panel) {
 
507
        return;
 
508
    }
 
509
 
 
510
    panel->showConfigurationInterface();
 
511
 
 
512
    //Fall back to the cursor position since we don't know what is the originating containment
 
513
    const int screen = Kephal::ScreenUtils::screenId(QCursor::pos());
 
514
 
 
515
    panel->setScreen(screen);
 
516
 
 
517
    QList<Plasma::Location> freeEdges = DesktopCorona::freeEdges(screen);
 
518
    //kDebug() << freeEdges;
 
519
    Plasma::Location destination;
 
520
    if (freeEdges.contains(Plasma::TopEdge)) {
 
521
        destination = Plasma::TopEdge;
 
522
    } else if (freeEdges.contains(Plasma::BottomEdge)) {
 
523
        destination = Plasma::BottomEdge;
 
524
    } else if (freeEdges.contains(Plasma::LeftEdge)) {
 
525
        destination = Plasma::LeftEdge;
 
526
    } else if (freeEdges.contains(Plasma::RightEdge)) {
 
527
        destination = Plasma::RightEdge;
 
528
    } else destination = Plasma::TopEdge;
 
529
 
 
530
    panel->setLocation(destination);
 
531
 
 
532
    const QRect screenGeom = screenGeometry(screen);
 
533
    const QRegion availGeom = availableScreenRegion(screen);
 
534
    int minH = 10;
 
535
    int minW = 10;
 
536
    int w = 35;
 
537
    int h = 35;
 
538
 
 
539
    //FIXME: this should really step through the rects on the relevant screen edge to find
 
540
    //appropriate space
 
541
    if (destination == Plasma::LeftEdge) {
 
542
        QRect r = availGeom.intersected(QRect(screenGeom.x(), screenGeom.y(), w, screenGeom.height())).boundingRect();
 
543
        h = r.height();
 
544
        minW = 35;
 
545
        minH = h;
 
546
    } else if (destination == Plasma::RightEdge) {
 
547
        QRect r = availGeom.intersected(QRect(screenGeom.right() - w, screenGeom.y(), w, screenGeom.height())).boundingRect();
 
548
        h = r.height();
 
549
        minW = 35;
 
550
        minH = h;
 
551
    } else if (destination == Plasma::TopEdge) {
 
552
        QRect r = availGeom.intersected(QRect(screenGeom.x(), screenGeom.y(), screenGeom.width(), h)).boundingRect();
 
553
        w = r.width();
 
554
        minH = 35;
 
555
        minW = w;
 
556
    } else if (destination == Plasma::BottomEdge) {
 
557
        QRect r = availGeom.intersected(QRect(screenGeom.x(), screenGeom.bottom() - h, screenGeom.width(), h)).boundingRect();
 
558
        w = r.width();
 
559
        minH = 35;
 
560
        minW = w;
 
561
    }
 
562
 
 
563
    panel->setMinimumSize(minW, minH);
 
564
    panel->setMaximumSize(w, h);
 
565
    panel->resize(w, h);
 
566
}
 
567
 
 
568
void DesktopCorona::checkActivities()
 
569
{
 
570
    kDebug() << "containments to start with" << containments().count();
 
571
 
 
572
    KActivityConsumer::ServiceStatus status = m_activityController->serviceStatus();
 
573
    //kDebug() << "$%$%$#%$%$%Status:" << status;
 
574
    if (status == KActivityConsumer::NotRunning) {
 
575
        //panic and give up - better than causing a mess
 
576
        kDebug() << "No ActivityManager? Help, I've fallen and I can't get up!";
 
577
        return;
 
578
    }
 
579
 
 
580
    QStringList existingActivities = m_activityController->listActivities();
 
581
    foreach (const QString &id, existingActivities) {
 
582
        activityAdded(id);
 
583
    }
 
584
 
 
585
    QStringList newActivities;
 
586
    QString newCurrentActivity;
 
587
    //migration checks:
 
588
    //-containments with an invalid id are deleted.
 
589
    //-containments that claim they were on a screen are kept together, and are preferred if we
 
590
    //need to initialize the current activity.
 
591
    //-containments that don't know where they were or who they were with just get made into their
 
592
    //own activity.
 
593
    foreach (Plasma::Containment *cont, containments()) {
 
594
        if ((cont->containmentType() == Plasma::Containment::DesktopContainment ||
 
595
             cont->containmentType() == Plasma::Containment::CustomContainment) &&
 
596
            !offscreenWidgets().contains(cont)) {
 
597
            Plasma::Context *context = cont->context();
 
598
            QString oldId = context->currentActivityId();
 
599
            if (!oldId.isEmpty()) {
 
600
                if (existingActivities.contains(oldId)) {
 
601
                    continue; //it's already claimed
 
602
                }
 
603
                kDebug() << "invalid id" << oldId;
 
604
                //byebye
 
605
                cont->destroy(false);
 
606
                continue;
 
607
            }
 
608
            if (cont->screen() > -1) {
 
609
                //it belongs on the current activity
 
610
                if (!newCurrentActivity.isEmpty()) {
 
611
                    context->setCurrentActivityId(newCurrentActivity);
 
612
                    continue;
 
613
                }
 
614
            }
 
615
            //discourage blank names
 
616
            if (context->currentActivity().isEmpty()) {
 
617
                context->setCurrentActivity(i18nc("Default name for a new activity", "New Activity"));
 
618
            }
 
619
            //create a new activity for the containment
 
620
            QString id = m_activityController->addActivity(context->currentActivity());
 
621
            context->setCurrentActivityId(id);
 
622
            newActivities << id;
 
623
            if (cont->screen() > -1) {
 
624
                newCurrentActivity = id;
 
625
            }
 
626
            kDebug() << "migrated" << context->currentActivityId() << context->currentActivity();
 
627
        }
 
628
    }
 
629
 
 
630
    kDebug() << "migrated?" << !newActivities.isEmpty() << containments().count();
 
631
    if (!newActivities.isEmpty()) {
 
632
        requestConfigSync();
 
633
    }
 
634
 
 
635
    //init the newbies
 
636
    foreach (const QString &id, newActivities) {
 
637
        activityAdded(id);
 
638
    }
 
639
 
 
640
    //ensure the current activity is initialized
 
641
    if (m_activityController->currentActivity().isEmpty()) {
 
642
        kDebug() << "guessing at current activity";
 
643
        if (existingActivities.isEmpty()) {
 
644
            if (newCurrentActivity.isEmpty()) {
 
645
                if (newActivities.isEmpty()) {
 
646
                    kDebug() << "no activities!?! Bad activitymanager, no cookie!";
 
647
                    QString id = m_activityController->addActivity(i18nc("Default name for a new activity", "New Activity"));
 
648
                    activityAdded(id);
 
649
                    m_activityController->setCurrentActivity(id);
 
650
                    kDebug() << "created emergency activity" << id;
 
651
                } else {
 
652
                    m_activityController->setCurrentActivity(newActivities.first());
 
653
                }
 
654
            } else {
 
655
                m_activityController->setCurrentActivity(newCurrentActivity);
 
656
            }
 
657
        } else {
 
658
            m_activityController->setCurrentActivity(existingActivities.first());
 
659
        }
 
660
    }
 
661
}
 
662
 
 
663
void DesktopCorona::currentActivityChanged(const QString &newActivity)
 
664
{
 
665
    kDebug() << newActivity;
 
666
    Activity *act =activity(newActivity);
 
667
    if (act) {
 
668
        act->ensureActive();
 
669
    }
 
670
}
 
671
 
 
672
Activity* DesktopCorona::activity(const QString &id)
 
673
{
 
674
    if (!m_activities.contains(id)) {
 
675
        //the add signal comes late sometimes
 
676
        activityAdded(id);
 
677
    }
 
678
    return m_activities.value(id);
 
679
}
 
680
 
 
681
void DesktopCorona::activityAdded(const QString &id)
 
682
{
 
683
    //TODO more sanity checks
 
684
    if (m_activities.contains(id)) {
 
685
        kDebug() << "you're late." << id;
 
686
        return;
 
687
    }
 
688
 
 
689
    Activity *a = new Activity(id, this);
 
690
    if (a->isCurrent()) {
 
691
        a->ensureActive();
 
692
    }
 
693
    m_activities.insert(id, a);
 
694
}
 
695
 
 
696
void DesktopCorona::activityRemoved(const QString &id)
 
697
{
 
698
    Activity *a = m_activities.take(id);
 
699
    a->deleteLater();
 
700
}
 
701
 
 
702
void DesktopCorona::activateNextActivity()
 
703
{
 
704
    QStringList list = m_activityController->listActivities(KActivityInfo::Running);
 
705
    if (list.isEmpty()) {
 
706
        return;
 
707
    }
 
708
 
 
709
    //FIXME: if the current activity is in transition the "next" will be the first
 
710
    int start = list.indexOf(m_activityController->currentActivity());
 
711
    int i = (start + 1) % list.size();
 
712
 
 
713
    m_activityController->setCurrentActivity(list.at(i));
 
714
}
 
715
 
 
716
void DesktopCorona::activatePreviousActivity()
 
717
{
 
718
    QStringList list = m_activityController->listActivities(KActivityInfo::Running);
 
719
    if (list.isEmpty()) {
 
720
        return;
 
721
    }
 
722
 
 
723
    //FIXME: if the current activity is in transition the "previous" will be the last
 
724
    int start = list.indexOf(m_activityController->currentActivity());
 
725
    //fun fact: in c++, (-1 % foo) == -1
 
726
    int i = start - 1;
 
727
    if (i < 0) {
 
728
        i = list.size() - 1;
 
729
    }
 
730
 
 
731
    m_activityController->setCurrentActivity(list.at(i));
 
732
}
 
733
 
 
734
 
 
735
#include "desktopcorona.moc"
 
736