1/*
2 * Copyright 2007 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 "view.h"
21
22#include <QTimer>
23
24#include <kdebug.h>
25#include <kglobal.h>
26#include <kwindowsystem.h>
27#include <kactioncollection.h>
28
29#include "corona.h"
30#include "containment.h"
31#include "private/containment_p.h"
32#include "wallpaper.h"
33
34using namespace Plasma;
35
36namespace Plasma
37{
38
39class ViewPrivate
40{
41public:
42 ViewPrivate(View *view, int uniqueId)
43 : q(view),
44 containment(0),
45 viewId(0),
46 lastScreen(-1),
47 lastDesktop(-2),
48 drawWallpaper(true),
49 trackChanges(true),
50 init(false)
51 {
52 if (uniqueId > 0 && !viewIds.contains(uniqueId)) {
53 s_maxViewId = uniqueId;
54 viewId = uniqueId;
55 }
56
57 if (viewId == 0) {
58 // we didn't get a sane value assigned to us, so lets
59 // grab the next available id
60 viewId = ++s_maxViewId;
61 }
62 viewIds.insert(viewId);
63 }
64
65 ~ViewPrivate()
66 {
67 }
68
69 void privateInit()
70 {
71 q->setContainment(containment);
72 init = true;
73 }
74
75 void updateSceneRect()
76 {
77 if (!containment || !trackChanges) {
78 return;
79 }
80
81 kDebug() << "!!!!!!!!!!!!!!!!! setting the scene rect to"
82 << containment->sceneBoundingRect()
83 << "associated screen is" << containment->screen();
84
85 emit q->sceneRectAboutToChange();
86 if (q->transform().isIdentity()) { //we're not zoomed out
87 q->setSceneRect(containment->sceneBoundingRect());
88 } else {
89 //kDebug() << "trying to show the containment nicely";
90 q->ensureVisible(containment->sceneBoundingRect());
91 //q->centerOn(containment);
92 }
93 emit q->sceneRectChanged();
94 }
95
96 void containmentDestroyed()
97 {
98 containment = 0;
99 emit q->lostContainment();
100 }
101
102 void containmentScreenChanged(int wasScreen, int newScreen, Plasma::Containment *containment)
103 {
104 Q_UNUSED(wasScreen)
105 lastScreen = newScreen;
106 lastDesktop = containment->desktop();
107 }
108
109 void initGraphicsView()
110 {
111 q->setFrameShape(QFrame::NoFrame);
112 q->setAttribute(Qt::WA_TranslucentBackground);
113 q->setAutoFillBackground(true);
114 q->setDragMode(QGraphicsView::NoDrag);
115 q->setInteractive(true);
116 q->setAcceptDrops(true);
117 q->setAlignment(Qt::AlignLeft | Qt::AlignTop);
118 q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
119 q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
120 }
121
122 static int s_maxViewId;
123 //ugly but the only reliable way to do collision detection of ids
124 static QSet<int> viewIds;
125
126 Plasma::View *q;
127 Plasma::Containment *containment;
128 int viewId;
129 int lastScreen;
130 int lastDesktop;
131 bool drawWallpaper : 1;
132 bool trackChanges : 1;
133 bool init : 1;
134};
135
136int ViewPrivate::s_maxViewId(0);
137QSet<int> ViewPrivate::viewIds;
138
139View::View(Containment *containment, QWidget *parent)
140 : QGraphicsView(parent),
141 d(new ViewPrivate(this, 0))
142{
143 d->initGraphicsView();
144
145 if (containment) {
146 setScene(containment->scene());
147 d->containment = containment;
148 QTimer::singleShot(0, this, SLOT(privateInit()));
149 }
150}
151
152View::View(Containment *containment, int viewId, QWidget *parent)
153 : QGraphicsView(parent),
154 d(new ViewPrivate(this, viewId))
155{
156 d->initGraphicsView();
157
158 if (containment) {
159 setScene(containment->scene());
160 d->containment = containment;
161 QTimer::singleShot(0, this, SLOT(privateInit()));
162 }
163}
164
165View::~View()
166{
167 delete d;
168 // FIXME FIX a focus crash but i wasn't able to reproduce in a simple test case for Qt guys
169 // NB: this is also done in Corona
170 clearFocus();
171}
172
173void View::setScreen(int screen, int desktop)
174{
175 if (screen < 0) {
176 return;
177 }
178
179 // handle desktop views
180 // -1 == All desktops
181 if (desktop < -1 || desktop > KWindowSystem::numberOfDesktops() - 1) {
182 desktop = -1;
183 }
184
185 d->lastScreen = screen;
186 d->lastDesktop = desktop;
187
188 // handle views that are working with panel containment types
189 if (d->containment &&
190 (d->containment->containmentType() == Containment::PanelContainment ||
191 d->containment->containmentType() == Containment::CustomPanelContainment)) {
192 d->containment->setScreen(screen, desktop);
193 return;
194 }
195
196 Plasma::Corona *corona = qobject_cast<Plasma::Corona*>(scene());
197 if (corona) {
198 Containment *containment = corona->containmentForScreen(screen, desktop);
199
200 if (containment) {
201 d->containment = 0; //so that we don't end up on the old containment's screen
202 setContainment(containment);
203 }
204 }
205}
206
207int View::screen() const
208{
209 return d->lastScreen;
210}
211
212int View::desktop() const
213{
214 if (d->containment) {
215 return d->containment->desktop();
216 }
217
218 return d->lastDesktop;
219}
220
221int View::effectiveDesktop() const
222{
223 int desk = desktop();
224 return desk > -1 ? desk : KWindowSystem::currentDesktop();
225}
226
227void View::setContainment(Plasma::Containment *containment)
228{
229 if (d->init && containment == d->containment) {
230 return;
231 }
232
233 if (d->containment) {
234 disconnect(d->containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed()));
235 disconnect(d->containment, SIGNAL(geometryChanged()), this, SLOT(updateSceneRect()));
236 disconnect(d->containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)), this, SLOT(containmentScreenChanged(int,int,Plasma::Containment*)));
237 d->containment->removeAssociatedWidget(this);
238 }
239
240 if (!containment) {
241 d->containment = 0;
242 return;
243 }
244
245 Containment *oldContainment = d->containment;
246
247 int screen = d->lastScreen;
248 int desktop = d->lastDesktop;
249 if (oldContainment && oldContainment != containment) {
250 screen = oldContainment->screen();
251 desktop = oldContainment->desktop();
252 }
253
254 if (scene() != containment->scene()) {
255 setScene(containment->scene());
256 }
257
258 d->containment = containment;
259
260 //add keyboard-shortcut actions
261 d->containment->addAssociatedWidget(this);
262
263 int otherScreen = containment->screen();
264 int otherDesktop = containment->desktop();
265
266 if (screen > -1) {
267 d->lastScreen = screen;
268 d->lastDesktop = desktop;
269 //kDebug() << "set screen from setContainment due to old containment";
270 if (oldContainment && oldContainment != containment) {
271 oldContainment->setScreen(-1, -1);
272 }
273
274 if (screen != containment->screen() || desktop != containment->desktop()) {
275 containment->setScreen(screen, desktop);
276 }
277 } else {
278 d->lastScreen = otherScreen;
279 d->lastDesktop = otherDesktop;
280 }
281
282 if (oldContainment && oldContainment != containment && otherScreen > -1 &&
283 (!containment || otherScreen != containment->screen() || otherDesktop != containment->desktop())) {
284 // assign the old containment the old screen/desktop
285 //kDebug() << "set screen from setContainment" << screen << otherScreen << desktop << otherDesktop;
286 oldContainment->setScreen(otherScreen, otherDesktop);
287 }
288
289
290 /*
291 if (oldContainment) {
292 kDebug() << "old" << (QObject*)oldContainment << screen << oldContainment->screen()
293 << "new" << (QObject*)containment << otherScreen << containment->screen();
294 }
295 */
296
297 d->updateSceneRect();
298 connect(containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed()));
299 connect(containment, SIGNAL(geometryChanged()), this, SLOT(updateSceneRect()));
300 connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)), this, SLOT(containmentScreenChanged(int,int,Plasma::Containment*)));
301}
302
303Containment *View::containment() const
304{
305 return d->containment;
306}
307
308Containment *View::swapContainment(const QString &name, const QVariantList &args)
309{
310 return swapContainment(d->containment, name, args);
311}
312
313Containment *View::swapContainment(Plasma::Containment *existing, const QString &name, const QVariantList &args)
314{
315 if (!existing) {
316 return 0;
317 }
318
319 Containment *old = existing;
320 Plasma::Corona *corona = old->corona();
321 Plasma::Containment *c = corona->addContainmentDelayed(name, args);
322 if (c) {
323 const bool fail = c->pluginName() != name;
324 if (fail) {
325 delete c;
326 return old;
327 }
328
329 c->init();
330
331 KConfigGroup oldConfig = old->config();
332 KConfigGroup newConfig = c->config();
333
334 // ensure that the old containments configuration is up to date
335 old->save(oldConfig);
336
337 // Copy configuration to new containment
338 oldConfig.copyTo(&newConfig);
339
340 if (name != old->pluginName()) {
341 // we want to delete the form factor config key, as that may change from
342 // containment to containment. see https://bugs.kde.org/show_bug.cgi?id=253485
343 newConfig.deleteEntry("formfactor");
344 }
345
346 if (old == d->containment) {
347 // set our containment to the new one, if the the old containment was us
348 setContainment(c);
349 }
350
351 // load the configuration of the old containment into the new one
352 c->restore(newConfig);
353 c->updateConstraints(Plasma::StartupCompletedConstraint);
354 c->d->initApplets();
355 emit corona->containmentAdded(c);
356
357 // destroy the old one
358 old->destroy(false);
359
360 // and now save the config
361 c->save(newConfig);
362 corona->requestConfigSync();
363
364 return c;
365 }
366
367 return old;
368}
369
370KConfigGroup View::config() const
371{
372 KConfigGroup views(KGlobal::config(), "PlasmaViews");
373 return KConfigGroup(&views, QString::number(d->viewId));
374}
375
376void View::configNeedsSaving() const
377{
378 Plasma::Corona *corona = qobject_cast<Plasma::Corona*>(scene());
379 if (corona) {
380 corona->requestConfigSync();
381 } else {
382 KGlobal::config()->sync();
383 }
384}
385
386
387int View::id() const
388{
389 return d->viewId;
390}
391
392void View::setWallpaperEnabled(bool draw)
393{
394 d->drawWallpaper = draw;
395}
396
397bool View::isWallpaperEnabled() const
398{
399 return d->drawWallpaper;
400}
401
402void View::setTrackContainmentChanges(bool trackChanges)
403{
404 d->trackChanges = trackChanges;
405}
406
407bool View::trackContainmentChanges()
408{
409 return d->trackChanges;
410}
411
412View * View::topLevelViewAt(const QPoint & pos)
413{
414 QWidget *w = QApplication::topLevelAt(pos);
415 if (w) {
416 Plasma::View *v = qobject_cast<Plasma::View *>(w);
417 return v;
418 } else {
419 return 0;
420 }
421}
422
423} // namespace Plasma
424
425#include "view.moc"
426
427