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 | |
34 | using namespace Plasma; |
35 | |
36 | namespace Plasma |
37 | { |
38 | |
39 | class ViewPrivate |
40 | { |
41 | public: |
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 | |
136 | int ViewPrivate::s_maxViewId(0); |
137 | QSet<int> ViewPrivate::viewIds; |
138 | |
139 | View::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 | |
152 | View::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 | |
165 | View::~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 | |
173 | void 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 | |
207 | int View::screen() const |
208 | { |
209 | return d->lastScreen; |
210 | } |
211 | |
212 | int View::desktop() const |
213 | { |
214 | if (d->containment) { |
215 | return d->containment->desktop(); |
216 | } |
217 | |
218 | return d->lastDesktop; |
219 | } |
220 | |
221 | int View::effectiveDesktop() const |
222 | { |
223 | int desk = desktop(); |
224 | return desk > -1 ? desk : KWindowSystem::currentDesktop(); |
225 | } |
226 | |
227 | void 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 | |
303 | Containment *View::containment() const |
304 | { |
305 | return d->containment; |
306 | } |
307 | |
308 | Containment *View::swapContainment(const QString &name, const QVariantList &args) |
309 | { |
310 | return swapContainment(d->containment, name, args); |
311 | } |
312 | |
313 | Containment *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 | |
370 | KConfigGroup View::config() const |
371 | { |
372 | KConfigGroup views(KGlobal::config(), "PlasmaViews" ); |
373 | return KConfigGroup(&views, QString::number(d->viewId)); |
374 | } |
375 | |
376 | void 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 | |
387 | int View::id() const |
388 | { |
389 | return d->viewId; |
390 | } |
391 | |
392 | void View::setWallpaperEnabled(bool draw) |
393 | { |
394 | d->drawWallpaper = draw; |
395 | } |
396 | |
397 | bool View::isWallpaperEnabled() const |
398 | { |
399 | return d->drawWallpaper; |
400 | } |
401 | |
402 | void View::setTrackContainmentChanges(bool trackChanges) |
403 | { |
404 | d->trackChanges = trackChanges; |
405 | } |
406 | |
407 | bool View::trackContainmentChanges() |
408 | { |
409 | return d->trackChanges; |
410 | } |
411 | |
412 | View * 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 | |