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

« back to all changes in this revision

Viewing changes to plasma/desktop/shell/activity.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 2010 Chani Armitage <chani@kde.org>
 
3
 *
 
4
 *   This program is free software; you can redistribute it and/or modify
 
5
 *   it under the terms of the GNU General Public License as
 
6
 *   published by the Free Software Foundation; either version 2,
 
7
 *   or (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
#include "plasma-shell-desktop.h"
 
22
#include "kactivitycontroller.h"
 
23
#include "activitymanager/kidenticongenerator.h"
 
24
 
 
25
#include <QPixmap>
 
26
#include <QString>
 
27
#include <QSize>
 
28
#include <QFile>
 
29
 
 
30
#include <KConfig>
 
31
#include <KIcon>
 
32
#include <KStandardDirs>
 
33
#include <KWindowSystem>
 
34
 
 
35
#include <Plasma/Containment>
 
36
#include <Plasma/Context>
 
37
#include <Plasma/Corona>
 
38
 
 
39
#include <kactivityconsumer.h>
 
40
 
 
41
#include "plasmaapp.h"
 
42
 
 
43
#include "activity.h"
 
44
 
 
45
Activity::Activity(const QString &id, QObject *parent)
 
46
    : QObject(parent),
 
47
      m_id(id),
 
48
      m_plugin("default"),
 
49
      m_info(new KActivityInfo(id, this)),
 
50
      m_activityConsumer(new KActivityConsumer(this)),
 
51
      m_current(false)
 
52
{
 
53
    m_name = m_info->name();
 
54
    m_icon = m_info->icon();
 
55
 
 
56
    connect(m_info, SIGNAL(infoChanged()), this, SLOT(activityChanged()));
 
57
    connect(m_info, SIGNAL(stateChanged(KActivityInfo::State)), this, SLOT(activityStateChanged(KActivityInfo::State)));
 
58
    connect(m_info, SIGNAL(started()), this, SLOT(opened()));
 
59
    connect(m_info, SIGNAL(stopped()), this, SLOT(closed()));
 
60
    connect(m_info, SIGNAL(removed()), this, SLOT(removed()));
 
61
 
 
62
    connect(m_activityConsumer, SIGNAL(currentActivityChanged(QString)), this, SLOT(checkIfCurrent()));
 
63
    checkIfCurrent();
 
64
 
 
65
    //find your containments
 
66
    foreach (Plasma::Containment *cont, PlasmaApp::self()->corona()->containments()) {
 
67
        if ((cont->containmentType() == Plasma::Containment::DesktopContainment ||
 
68
             cont->containmentType() == Plasma::Containment::CustomContainment) &&
 
69
            !PlasmaApp::self()->corona()->offscreenWidgets().contains(cont) && cont->context()->currentActivityId() == id) {
 
70
            insertContainment(cont);
 
71
        }
 
72
    }
 
73
 
 
74
    //kDebug() << m_containments.size();
 
75
}
 
76
 
 
77
Activity::~Activity()
 
78
{
 
79
}
 
80
 
 
81
void Activity::activityChanged()
 
82
{
 
83
    setName(m_info->name());
 
84
    setIcon(m_info->icon());
 
85
}
 
86
 
 
87
void Activity::activityStateChanged(KActivityInfo::State state)
 
88
{
 
89
    Q_UNUSED(state)
 
90
    emit stateChanged();
 
91
}
 
92
 
 
93
QString Activity::id()
 
94
{
 
95
    return m_id;
 
96
}
 
97
 
 
98
QString Activity::name()
 
99
{
 
100
    return m_name;
 
101
}
 
102
 
 
103
QPixmap Activity::pixmap(const QSize &size)
 
104
{
 
105
    if (m_info->isValid() && !m_info->icon().isEmpty()) {
 
106
        return KIcon(m_info->icon()).pixmap(size);
 
107
    } else {
 
108
        return KIdenticonGenerator::self()->generatePixmap(size.width(), m_id);
 
109
    }
 
110
}
 
111
 
 
112
bool Activity::isCurrent()
 
113
{
 
114
    return m_current;
 
115
    //TODO maybe plasmaapp should cache the current activity to reduce dbus calls?
 
116
}
 
117
 
 
118
void Activity::checkIfCurrent()
 
119
{
 
120
    const bool current = m_id == m_activityConsumer->currentActivity();
 
121
    if (current != m_current) {
 
122
        m_current = current;
 
123
        emit currentStatusChanged();
 
124
    }
 
125
}
 
126
 
 
127
KActivityInfo::State Activity::state()
 
128
{
 
129
    return m_info->state();
 
130
}
 
131
 
 
132
void Activity::remove()
 
133
{
 
134
    KActivityController().removeActivity(m_id);
 
135
}
 
136
 
 
137
void Activity::removed()
 
138
{
 
139
    if (! m_containments.isEmpty()) {
 
140
        //FIXME only PlasmaApp::self()->corona() has authority to remove properly
 
141
        kDebug() << "!!!!! if your widgets are locked you've hit a BUG now";
 
142
        foreach (Plasma::Containment *c, m_containments) {
 
143
            c->destroy(false);
 
144
        }
 
145
    }
 
146
 
 
147
    const QString name = "activities/" + m_id;
 
148
    QFile::remove(KStandardDirs::locateLocal("appdata", name));
 
149
}
 
150
 
 
151
Plasma::Containment* Activity::containmentForScreen(int screen, int desktop)
 
152
{
 
153
    Plasma::Containment *containment = m_containments.value(QPair<int,int>(screen, desktop));
 
154
 
 
155
    if (!containment) {
 
156
        kDebug() << "adding containment for" << screen << desktop;
 
157
        // first look to see if there are any unnasigned containments that are candidates for
 
158
        // being sucked into this Activity
 
159
        foreach (Plasma::Containment *c, PlasmaApp::self()->corona()->containments()) {
 
160
            if ((c->containmentType() == Plasma::Containment::DesktopContainment ||
 
161
                c->containmentType() == Plasma::Containment::CustomContainment) &&
 
162
                c->context()->currentActivityId().isEmpty() &&
 
163
                !PlasmaApp::self()->corona()->offscreenWidgets().contains(c) &&
 
164
                m_containments.key(c, QPair<int,int>(-2,-2)) == QPair<int,int>(-2,-2)) {
 
165
                containment = c;
 
166
                containment->setScreen(screen, desktop);
 
167
                break;
 
168
            }
 
169
        }
 
170
 
 
171
        if (!containment) {
 
172
            // we ask for the containment for the screen with a default plugin, because
 
173
            // this allows the corona to either grab one for us that already exists matching
 
174
            // screen and desktop, or create a new one. this also works regardless of immutability
 
175
            containment = PlasmaApp::self()->corona()->containmentForScreen(screen, desktop, m_plugin);
 
176
 
 
177
            if (!containment || !containment->context()->currentActivityId().isEmpty()) {
 
178
                // possibly a plugin failure, let's go for the default
 
179
                containment = PlasmaApp::self()->corona()->containmentForScreen(screen, desktop, "default");
 
180
            }
 
181
 
 
182
            //we don't want to steal contaiments from other activities
 
183
            if (!containment) {
 
184
                // we failed to even get the default; we're screwed.
 
185
                Q_ASSERT(false);
 
186
                return 0;
 
187
            }
 
188
 
 
189
            if (!containment->context()->currentActivityId().isEmpty() &&
 
190
                containment->context()->currentActivityId() != m_id) {
 
191
                // we got a containment, but it belongs to some other activity; let's unassign it
 
192
                // from a screen and grab a new one
 
193
                containment->setScreen(-1);
 
194
                containment = PlasmaApp::self()->corona()->containmentForScreen(screen, desktop, m_plugin);
 
195
 
 
196
                if (!containment) {
 
197
                    // possibly a plugin failure, let's go for the default
 
198
                    containment = PlasmaApp::self()->corona()->containmentForScreen(screen, desktop, "default");
 
199
                }
 
200
 
 
201
                if (containment) {
 
202
                    containment->setScreen(screen, desktop);
 
203
                }
 
204
            }
 
205
        }
 
206
 
 
207
        if (containment) {
 
208
            insertContainment(containment, screen, desktop);
 
209
            PlasmaApp::self()->corona()->requestConfigSync();
 
210
        }
 
211
    } else if (containment->screen() != screen || containment->desktop() != desktop) {
 
212
        // ensure the containment _also_ knows which screen we think it is on;
 
213
        // can happen when swapping between activities without stopping them first
 
214
        containment->setScreen(screen, desktop);
 
215
    }
 
216
 
 
217
    return containment;
 
218
}
 
219
 
 
220
void Activity::activate()
 
221
{
 
222
    KActivityController().setCurrentActivity(m_id);
 
223
}
 
224
 
 
225
void Activity::ensureActive()
 
226
{
 
227
    if (m_containments.isEmpty()) {
 
228
        opened();
 
229
    }
 
230
 
 
231
    checkScreens();
 
232
}
 
233
 
 
234
void Activity::checkScreens()
 
235
{
 
236
    //ensure there's a containment for every screen & desktop.
 
237
    int numScreens = PlasmaApp::self()->corona()->numScreens();
 
238
    int numDesktops = AppSettings::perVirtualDesktopViews() ? KWindowSystem::numberOfDesktops() : 0;
 
239
 
 
240
    for (int screen = 0; screen < numScreens; ++screen) {
 
241
        if (numDesktops > 0) {
 
242
            for (int desktop = 0; desktop < numDesktops; ++desktop) {
 
243
                containmentForScreen(screen, desktop);
 
244
            }
 
245
        } else {
 
246
            containmentForScreen(screen, -1);
 
247
        }
 
248
    }
 
249
}
 
250
 
 
251
void Activity::setName(const QString &name)
 
252
{
 
253
    if (m_name == name) {
 
254
        return;
 
255
    }
 
256
 
 
257
    m_name = name;
 
258
    KActivityController().setActivityName(m_id, name);
 
259
    emit infoChanged();
 
260
 
 
261
    foreach (Plasma::Containment *c, m_containments) {
 
262
        c->context()->setCurrentActivity(name);
 
263
    }
 
264
}
 
265
 
 
266
void Activity::setIcon(const QString &icon)
 
267
{
 
268
    if (m_icon == icon) {
 
269
        return;
 
270
    }
 
271
 
 
272
    m_icon = icon;
 
273
 
 
274
    KActivityController().setActivityIcon(m_id, icon);
 
275
    emit infoChanged();
 
276
}
 
277
 
 
278
void Activity::updateActivityName(Plasma::Context *context)
 
279
{
 
280
    if (context->currentActivityId() != m_id) {
 
281
        kDebug() << "can't happen!";
 
282
        return;
 
283
    }
 
284
    setName(context->currentActivity());
 
285
}
 
286
 
 
287
void Activity::save(KConfig &external)
 
288
{
 
289
    foreach (const QString &group, external.groupList()) {
 
290
        KConfigGroup cg(&external, group);
 
291
        cg.deleteGroup();
 
292
    }
 
293
 
 
294
    //TODO: multi-screen saving/restoring, where each screen can be
 
295
    // independently restored: put each screen's containments into a
 
296
    // different group, e.g. [Screens][0][Containments], [Screens][1][Containments], etc
 
297
    KConfigGroup dest(&external, "Containments");
 
298
    KConfigGroup dummy;
 
299
    foreach (Plasma::Containment *c, m_containments) {
 
300
        c->save(dummy);
 
301
        KConfigGroup group(&dest, QString::number(c->id()));
 
302
        c->config().copyTo(&group);
 
303
    }
 
304
 
 
305
    external.sync();
 
306
}
 
307
 
 
308
void Activity::close()
 
309
{
 
310
    KActivityController().stopActivity(m_id);
 
311
}
 
312
 
 
313
void Activity::closed()
 
314
{
 
315
    const QString name = "activities/" + m_id;
 
316
    KConfig external(name, KConfig::SimpleConfig, "appdata");
 
317
 
 
318
    //passing an empty string for the group name turns a kconfig into a kconfiggroup
 
319
    KConfigGroup group = external.group(QString());
 
320
    PlasmaApp::self()->corona()->exportLayout(group, m_containments.values());
 
321
 
 
322
    //hrm, shouldn't the containments' deleted signals have done this for us?
 
323
    if (!m_containments.isEmpty()) {
 
324
        kDebug() << "isn't close supposed to *remove* containments??";
 
325
        m_containments.clear();
 
326
    }
 
327
}
 
328
 
 
329
void Activity::replaceContainment(Plasma::Containment* containment)
 
330
{
 
331
    insertContainment(containment, true);
 
332
}
 
333
 
 
334
void Activity::insertContainment(Plasma::Containment* cont, bool force)
 
335
{
 
336
    int screen = cont->lastScreen();
 
337
    int desktop = cont->lastDesktop();
 
338
 
 
339
    kDebug() << screen << desktop;
 
340
    if (screen == -1) {
 
341
        //the migration can't set lastScreen, so maybe we need to assign the containment here
 
342
        kDebug() << "found a lost one";
 
343
        screen = 0;
 
344
    }
 
345
 
 
346
    if (!force && m_containments.contains(QPair<int,int>(screen, desktop))) {
 
347
        //this almost certainly means someone has been meddling where they shouldn't
 
348
        //but we should protect them from harm anyways
 
349
        kDebug() << "@!@!@!@!@!@@@@rejecting containment!!!";
 
350
        cont->context()->setCurrentActivityId(QString());
 
351
        return;
 
352
    }
 
353
 
 
354
    insertContainment(cont, screen, desktop);
 
355
}
 
356
 
 
357
void Activity::insertContainment(Plasma::Containment* containment, int screen, int desktop)
 
358
{
 
359
    //ensure it's hooked up
 
360
    Plasma::Context *context = containment->context();
 
361
    context->setCurrentActivityId(m_id);
 
362
    context->setCurrentActivity(m_name);
 
363
    //hack to keep the name in sync while KActivity* are in kdebase
 
364
    connect(context, SIGNAL(activityChanged(Plasma::Context*)), this, SLOT(updateActivityName(Plasma::Context*)), Qt::UniqueConnection);
 
365
 
 
366
    m_containments.insert(QPair<int,int>(screen, desktop), containment);
 
367
    connect(containment, SIGNAL(destroyed(QObject *)), this, SLOT(containmentDestroyed(QObject *)));
 
368
}
 
369
 
 
370
void Activity::containmentDestroyed(QObject *object)
 
371
{
 
372
    //safe here because we are not accessing it
 
373
    Plasma::Containment *deletedCont = static_cast<Plasma::Containment *>(object);
 
374
 
 
375
    QHash<QPair<int,int>, Plasma::Containment*>::iterator i;
 
376
    for (i = m_containments.begin(); i != m_containments.end(); ++i) {
 
377
        Plasma::Containment *cont = i.value();
 
378
        if (cont == deletedCont) {
 
379
            m_containments.remove(i.key());
 
380
            break;
 
381
        }
 
382
    }
 
383
}
 
384
 
 
385
void Activity::open()
 
386
{
 
387
    KActivityController().startActivity(m_id);
 
388
}
 
389
 
 
390
void Activity::opened()
 
391
{
 
392
    if (!m_containments.isEmpty()) {
 
393
        kDebug() << "already open!";
 
394
        return;
 
395
    }
 
396
 
 
397
    QString fileName = "activities/";
 
398
    fileName += m_id;
 
399
    KConfig external(fileName, KConfig::SimpleConfig, "appdata");
 
400
 
 
401
    foreach (Plasma::Containment *newContainment, PlasmaApp::self()->corona()->importLayout(external.group(QByteArray()))) {
 
402
        insertContainment(newContainment);
 
403
        //ensure it's hooked up (if something odd happened we don't want orphan containments)
 
404
        Plasma::Context *context = newContainment->context();
 
405
        context->setCurrentActivityId(m_id);
 
406
        connect(context, SIGNAL(activityChanged(Plasma::Context*)), this, SLOT(updateActivityName(Plasma::Context*)), Qt::UniqueConnection);
 
407
    }
 
408
 
 
409
    KConfigGroup configs(&external, "Containments");
 
410
    configs.deleteGroup();
 
411
 
 
412
    if (m_containments.isEmpty()) {
 
413
        //TODO check if we need more for screens/desktops
 
414
        kDebug() << "open failed (bad file?). creating new containment";
 
415
        checkScreens();
 
416
    }
 
417
 
 
418
    PlasmaApp::self()->corona()->requireConfigSync();
 
419
    external.sync();
 
420
}
 
421
 
 
422
void Activity::setDefaultPlugin(const QString &plugin)
 
423
{
 
424
    m_plugin = plugin;
 
425
    //FIXME save&restore this setting
 
426
}
 
427
 
 
428
const KActivityInfo * Activity::info() const
 
429
{
 
430
    return m_info;
 
431
}
 
432
 
 
433
#include "activity.moc"
 
434
 
 
435
// vim: sw=4 sts=4 et tw=100