2
* Copyright 2010 Chani Armitage <chani@kde.org>
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.
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
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.
20
#include "desktopcorona.h"
21
#include "plasma-shell-desktop.h"
22
#include "kactivitycontroller.h"
23
#include "activitymanager/kidenticongenerator.h"
32
#include <KStandardDirs>
33
#include <KWindowSystem>
35
#include <Plasma/Containment>
36
#include <Plasma/Context>
37
#include <Plasma/Corona>
39
#include <kactivityconsumer.h>
41
#include "plasmaapp.h"
45
Activity::Activity(const QString &id, QObject *parent)
49
m_info(new KActivityInfo(id, this)),
50
m_activityConsumer(new KActivityConsumer(this)),
53
m_name = m_info->name();
54
m_icon = m_info->icon();
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()));
62
connect(m_activityConsumer, SIGNAL(currentActivityChanged(QString)), this, SLOT(checkIfCurrent()));
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);
74
//kDebug() << m_containments.size();
81
void Activity::activityChanged()
83
setName(m_info->name());
84
setIcon(m_info->icon());
87
void Activity::activityStateChanged(KActivityInfo::State state)
93
QString Activity::id()
98
QString Activity::name()
103
QPixmap Activity::pixmap(const QSize &size)
105
if (m_info->isValid() && !m_info->icon().isEmpty()) {
106
return KIcon(m_info->icon()).pixmap(size);
108
return KIdenticonGenerator::self()->generatePixmap(size.width(), m_id);
112
bool Activity::isCurrent()
115
//TODO maybe plasmaapp should cache the current activity to reduce dbus calls?
118
void Activity::checkIfCurrent()
120
const bool current = m_id == m_activityConsumer->currentActivity();
121
if (current != m_current) {
123
emit currentStatusChanged();
127
KActivityInfo::State Activity::state()
129
return m_info->state();
132
void Activity::remove()
134
KActivityController().removeActivity(m_id);
137
void Activity::removed()
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) {
147
const QString name = "activities/" + m_id;
148
QFile::remove(KStandardDirs::locateLocal("appdata", name));
151
Plasma::Containment* Activity::containmentForScreen(int screen, int desktop)
153
Plasma::Containment *containment = m_containments.value(QPair<int,int>(screen, desktop));
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)) {
166
containment->setScreen(screen, desktop);
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);
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");
182
//we don't want to steal contaiments from other activities
184
// we failed to even get the default; we're screwed.
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);
197
// possibly a plugin failure, let's go for the default
198
containment = PlasmaApp::self()->corona()->containmentForScreen(screen, desktop, "default");
202
containment->setScreen(screen, desktop);
208
insertContainment(containment, screen, desktop);
209
PlasmaApp::self()->corona()->requestConfigSync();
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);
220
void Activity::activate()
222
KActivityController().setCurrentActivity(m_id);
225
void Activity::ensureActive()
227
if (m_containments.isEmpty()) {
234
void Activity::checkScreens()
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;
240
for (int screen = 0; screen < numScreens; ++screen) {
241
if (numDesktops > 0) {
242
for (int desktop = 0; desktop < numDesktops; ++desktop) {
243
containmentForScreen(screen, desktop);
246
containmentForScreen(screen, -1);
251
void Activity::setName(const QString &name)
253
if (m_name == name) {
258
KActivityController().setActivityName(m_id, name);
261
foreach (Plasma::Containment *c, m_containments) {
262
c->context()->setCurrentActivity(name);
266
void Activity::setIcon(const QString &icon)
268
if (m_icon == icon) {
274
KActivityController().setActivityIcon(m_id, icon);
278
void Activity::updateActivityName(Plasma::Context *context)
280
if (context->currentActivityId() != m_id) {
281
kDebug() << "can't happen!";
284
setName(context->currentActivity());
287
void Activity::save(KConfig &external)
289
foreach (const QString &group, external.groupList()) {
290
KConfigGroup cg(&external, group);
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");
299
foreach (Plasma::Containment *c, m_containments) {
301
KConfigGroup group(&dest, QString::number(c->id()));
302
c->config().copyTo(&group);
308
void Activity::close()
310
KActivityController().stopActivity(m_id);
313
void Activity::closed()
315
const QString name = "activities/" + m_id;
316
KConfig external(name, KConfig::SimpleConfig, "appdata");
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());
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();
329
void Activity::replaceContainment(Plasma::Containment* containment)
331
insertContainment(containment, true);
334
void Activity::insertContainment(Plasma::Containment* cont, bool force)
336
int screen = cont->lastScreen();
337
int desktop = cont->lastDesktop();
339
kDebug() << screen << desktop;
341
//the migration can't set lastScreen, so maybe we need to assign the containment here
342
kDebug() << "found a lost one";
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());
354
insertContainment(cont, screen, desktop);
357
void Activity::insertContainment(Plasma::Containment* containment, int screen, int desktop)
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);
366
m_containments.insert(QPair<int,int>(screen, desktop), containment);
367
connect(containment, SIGNAL(destroyed(QObject *)), this, SLOT(containmentDestroyed(QObject *)));
370
void Activity::containmentDestroyed(QObject *object)
372
//safe here because we are not accessing it
373
Plasma::Containment *deletedCont = static_cast<Plasma::Containment *>(object);
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());
385
void Activity::open()
387
KActivityController().startActivity(m_id);
390
void Activity::opened()
392
if (!m_containments.isEmpty()) {
393
kDebug() << "already open!";
397
QString fileName = "activities/";
399
KConfig external(fileName, KConfig::SimpleConfig, "appdata");
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);
409
KConfigGroup configs(&external, "Containments");
410
configs.deleteGroup();
412
if (m_containments.isEmpty()) {
413
//TODO check if we need more for screens/desktops
414
kDebug() << "open failed (bad file?). creating new containment";
418
PlasmaApp::self()->corona()->requireConfigSync();
422
void Activity::setDefaultPlugin(const QString &plugin)
425
//FIXME save&restore this setting
428
const KActivityInfo * Activity::info() const
433
#include "activity.moc"
435
// vim: sw=4 sts=4 et tw=100