• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.11.5 API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • plasma
corona.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2007 Matt Broadstone <mbroadst@gmail.com>
3  * Copyright 2007-2011 Aaron Seigo <aseigo@kde.org>
4  * Copyright 2007 Riccardo Iaconelli <riccardo@kde.org>
5  * Copyright (c) 2009 Chani Armitage <chani@kde.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Library General Public License as
9  * published by the Free Software Foundation; either version 2, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #include "corona.h"
24 #include "private/corona_p.h"
25 
26 #include <QApplication>
27 #include <QDesktopWidget>
28 #include <QGraphicsView>
29 #include <QGraphicsSceneDragDropEvent>
30 #include <QGraphicsGridLayout>
31 #include <QMimeData>
32 #include <QPainter>
33 #include <QTimer>
34 
35 #include <cmath>
36 
37 #include <kaction.h>
38 #include <kdebug.h>
39 #include <kglobal.h>
40 #include <klocale.h>
41 #include <kmimetype.h>
42 #include <kshortcutsdialog.h>
43 #include <kwindowsystem.h>
44 
45 #include "animator.h"
46 #include "abstracttoolbox.h"
47 #include "containment.h"
48 #include "containmentactionspluginsconfig.h"
49 #include "view.h"
50 #include "private/animator_p.h"
51 #include "private/applet_p.h"
52 #include "private/containment_p.h"
53 #include "tooltipmanager.h"
54 #include "abstractdialogmanager.h"
55 
56 using namespace Plasma;
57 
58 namespace Plasma
59 {
60 
61 bool CoronaPrivate::s_positioningContainments = false;
62 
63 Corona::Corona(QObject *parent)
64  : QGraphicsScene(parent),
65  d(new CoronaPrivate(this))
66 {
67  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Corona ctor start";
68  d->init();
69  ToolTipManager::self()->m_corona = this;
70  //setViewport(new QGLWidget(QGLFormat(QGL::StencilBuffer | QGL::AlphaChannel)));
71 }
72 
73 Corona::~Corona()
74 {
75  KConfigGroup trans(KGlobal::config(), "PlasmaTransientsConfig");
76  trans.deleteGroup();
77 
78  // FIXME: Same fix as in Plasma::View - make sure that when the focused widget is
79  // destroyed we don't try to transfer it to something that's already been
80  // deleted.
81  clearFocus();
82  delete d;
83 }
84 
85 void Corona::setAppletMimeType(const QString &type)
86 {
87  d->mimetype = type;
88 }
89 
90 QString Corona::appletMimeType()
91 {
92  return d->mimetype;
93 }
94 
95 void Corona::setDefaultContainmentPlugin(const QString &name)
96 {
97  // we could check if it is in:
98  // Containment::listContainments().contains(name) ||
99  // Containment::listContainments(QString(), KGlobal::mainComponent().componentName()).contains(name)
100  // but that seems like overkill
101  d->defaultContainmentPlugin = name;
102 }
103 
104 QString Corona::defaultContainmentPlugin() const
105 {
106  return d->defaultContainmentPlugin;
107 }
108 
109 void Corona::saveLayout(const QString &configName) const
110 {
111  KSharedConfigPtr c;
112 
113  if (configName.isEmpty() || configName == d->configName) {
114  c = config();
115  } else {
116  c = KSharedConfig::openConfig(configName, KConfig::SimpleConfig);
117  }
118 
119  d->saveLayout(c);
120 }
121 
122 void Corona::exportLayout(KConfigGroup &config, QList<Containment*> containments)
123 {
124  foreach (const QString &group, config.groupList()) {
125  KConfigGroup cg(&config, group);
126  cg.deleteGroup();
127  }
128 
129  //temporarily unlock so that removal works
130  ImmutabilityType oldImm = immutability();
131  d->immutability = Mutable;
132 
133  KConfigGroup dest(&config, "Containments");
134  KConfigGroup dummy;
135  foreach (Plasma::Containment *c, containments) {
136  c->save(dummy);
137  c->config().reparent(&dest);
138 
139  //ensure the containment is unlocked
140  //this is done directly because we have to bypass any SystemImmutable checks
141  c->Applet::d->immutability = Mutable;
142  foreach (Applet *a, c->applets()) {
143  a->d->immutability = Mutable;
144  }
145 
146  c->destroy(false);
147  }
148 
149  //restore immutability
150  d->immutability = oldImm;
151 
152  config.sync();
153 }
154 
155 void Corona::requestConfigSync()
156 {
157  // constant controlling how long between requesting a configuration sync
158  // and one happening should occur. currently 10 seconds
159  static const int CONFIG_SYNC_TIMEOUT = 10000;
160 
161  // TODO: should we check into our immutability before doing this?
162 
163  //NOTE: this is a pretty simplistic model: we simply save no more than CONFIG_SYNC_TIMEOUT
164  // after the first time this is called. not much of a heuristic for save points, but
165  // it should at least compress these activities a bit and provide a way for applet
166  // authors to ween themselves from the sync() disease. A more interesting/dynamic
167  // algorithm for determining when to actually sync() to disk might be better, though.
168  if (!d->configSyncTimer.isActive()) {
169  d->configSyncTimer.start(CONFIG_SYNC_TIMEOUT);
170  }
171 }
172 
173 void Corona::requireConfigSync()
174 {
175  d->syncConfig();
176 }
177 
178 void Corona::initializeLayout(const QString &configName)
179 {
180  clearContainments();
181  loadLayout(configName);
182 
183  if (d->containments.isEmpty()) {
184  loadDefaultLayout();
185  if (!d->containments.isEmpty()) {
186  requestConfigSync();
187  }
188  }
189 
190  if (config()->isImmutable()) {
191  setImmutability(SystemImmutable);
192  } else {
193  KConfigGroup coronaConfig(config(), "General");
194  setImmutability((ImmutabilityType)coronaConfig.readEntry("immutability", (int)Mutable));
195  }
196 }
197 
198 bool containmentSortByPosition(const Containment *c1, const Containment *c2)
199 {
200  return c1->id() < c2->id();
201 }
202 
203 void Corona::layoutContainments()
204 {
205  if (CoronaPrivate::s_positioningContainments) {
206  return;
207  }
208 
209  CoronaPrivate::s_positioningContainments = true;
210 
211  //TODO: we should avoid running this too often; consider compressing requests
212  // with a timer.
213  QList<Containment*> c = containments();
214  QMutableListIterator<Containment*> it(c);
215 
216  while (it.hasNext()) {
217  Containment *containment = it.next();
218  if (containment->containmentType() == Containment::PanelContainment ||
219  containment->containmentType() == Containment::CustomPanelContainment ||
220  offscreenWidgets().contains(containment)) {
221  // weed out all containments we don't care about at all
222  // e.g. Panels and ourself
223  it.remove();
224  continue;
225  }
226  }
227 
228  qSort(c.begin(), c.end(), containmentSortByPosition);
229 
230  if (c.isEmpty()) {
231  CoronaPrivate::s_positioningContainments = false;
232  return;
233  }
234 
235  int column = 0;
236  int x = 0;
237  int y = 0;
238  int rowHeight = 0;
239 
240  it.toFront();
241  while (it.hasNext()) {
242  Containment *containment = it.next();
243  containment->setPos(x, y);
244  //kDebug() << ++count << "setting to" << x << y;
245 
246  int height = containment->size().height();
247  if (height > rowHeight) {
248  rowHeight = height;
249  }
250 
251  ++column;
252 
253  if (column == CONTAINMENT_COLUMNS) {
254  column = 0;
255  x = 0;
256  y += rowHeight + INTER_CONTAINMENT_MARGIN + TOOLBOX_MARGIN;
257  rowHeight = 0;
258  } else {
259  x += containment->size().width() + INTER_CONTAINMENT_MARGIN;
260  }
261  //kDebug() << "column: " << column << "; x " << x << "; y" << y << "; width was"
262  // << containment->size().width();
263  }
264 
265  CoronaPrivate::s_positioningContainments = false;
266 }
267 
268 
269 void Corona::loadLayout(const QString &configName)
270 {
271  if (!configName.isEmpty() && configName != d->configName) {
272  // if we have a new config name passed in, then use that as the config file for this Corona
273  d->config = 0;
274  d->configName = configName;
275  }
276 
277  KSharedConfigPtr conf = config();
278  d->importLayout(*conf, false);
279 }
280 
281 QList<Plasma::Containment *> Corona::importLayout(const KConfigGroup &conf)
282 {
283  return d->importLayout(conf, true);
284 }
285 
286 #ifndef KDE_NO_DEPRECATED
287 QList<Plasma::Containment *> Corona::importLayout(const KConfigBase &conf)
288 {
289  return d->importLayout(conf, true);
290 }
291 #endif
292 
293 Containment *Corona::containmentForScreen(int screen, int desktop) const
294 {
295  foreach (Containment *containment, d->containments) {
296  if (containment->screen() == screen &&
297  (desktop < 0 || containment->desktop() == desktop) &&
298  (containment->containmentType() == Containment::DesktopContainment ||
299  containment->containmentType() == Containment::CustomContainment)) {
300  return containment;
301  }
302  }
303 
304  return 0;
305 }
306 
307 Containment *Corona::containmentForScreen(int screen, int desktop,
308  const QString &defaultPluginIfNonExistent, const QVariantList &defaultArgs)
309 {
310  Containment *containment = containmentForScreen(screen, desktop);
311  if (!containment && !defaultPluginIfNonExistent.isEmpty()) {
312  // screen requests are allowed to bypass immutability
313  if (screen >= 0 && screen < numScreens() &&
314  desktop >= -1 && desktop < KWindowSystem::numberOfDesktops()) {
315  containment = d->addContainment(defaultPluginIfNonExistent, defaultArgs, 0, false);
316  if (containment) {
317  containment->setScreen(screen, desktop);
318  }
319  }
320  }
321 
322  return containment;
323 }
324 
325 QList<Containment*> Corona::containments() const
326 {
327  return d->containments;
328 }
329 
330 void Corona::clearContainments()
331 {
332  foreach (Containment *containment, d->containments) {
333  containment->clearApplets();
334  }
335 }
336 
337 KSharedConfigPtr Corona::config() const
338 {
339  if (!d->config) {
340  d->config = KSharedConfig::openConfig(d->configName, KConfig::SimpleConfig);
341  }
342 
343  return d->config;
344 }
345 
346 Containment *Corona::addContainment(const QString &name, const QVariantList &args)
347 {
348  if (d->immutability == Mutable) {
349  return d->addContainment(name, args, 0, false);
350  }
351 
352  return 0;
353 }
354 
355 Containment *Corona::addContainmentDelayed(const QString &name, const QVariantList &args)
356 {
357  if (d->immutability == Mutable) {
358  return d->addContainment(name, args, 0, true);
359  }
360 
361  return 0;
362 }
363 
364 void Corona::mapAnimation(Animator::Animation from, Animator::Animation to)
365 {
366  AnimatorPrivate::mapAnimation(from, to);
367 }
368 
369 void Corona::mapAnimation(Animator::Animation from, const QString &to)
370 {
371  AnimatorPrivate::mapAnimation(from, to);
372 }
373 
374 void Corona::addOffscreenWidget(QGraphicsWidget *widget)
375 {
376  foreach (QGraphicsWidget *w, d->offscreenWidgets) {
377  if (w == widget) {
378  kDebug() << "widget is already an offscreen widget!";
379  return;
380  }
381  }
382 
383  //search for an empty spot in the topleft quadrant of the scene. each 'slot' is QWIDGETSIZE_MAX
384  //x QWIDGETSIZE_MAX, so we're guaranteed to never have to move widgets once they're placed here.
385  int i = 0;
386  while (d->offscreenWidgets.contains(i)) {
387  i++;
388  }
389 
390  d->offscreenWidgets[i] = widget;
391 #if defined(arm) || defined(__arm__)
392  widget->setPos((-i - 1) * 2000, -2000);
393 #else
394  widget->setPos((-i - 1) * QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
395 #endif
396 
397  QGraphicsWidget *pw = widget->parentWidget();
398  widget->setParentItem(0);
399  if (pw) {
400  widget->setParent(pw);
401  }
402 
403  //kDebug() << "adding offscreen widget at slot " << i;
404  if (!widget->scene()) {
405  addItem(widget);
406  }
407 
408  connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(offscreenWidgetDestroyed(QObject*)));
409 }
410 
411 void Corona::removeOffscreenWidget(QGraphicsWidget *widget)
412 {
413  QMutableHashIterator<uint, QGraphicsWidget *> it(d->offscreenWidgets);
414 
415  while (it.hasNext()) {
416  if (it.next().value() == widget) {
417  it.remove();
418  return;
419  }
420  }
421 }
422 
423 QList <QGraphicsWidget *> Corona::offscreenWidgets() const
424 {
425  return d->offscreenWidgets.values();
426 }
427 
428 void CoronaPrivate::offscreenWidgetDestroyed(QObject *o)
429 {
430  // at this point, it's just a QObject, not a QGraphicsWidget, but we still need
431  // a pointer of the appropriate type.
432  // WARNING: DO NOT USE THE WIDGET POINTER FOR ANYTHING OTHER THAN POINTER COMPARISONS
433  QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(o);
434  q->removeOffscreenWidget(widget);
435 }
436 
437 int Corona::numScreens() const
438 {
439  return 1;
440 }
441 
442 QRect Corona::screenGeometry(int id) const
443 {
444  Q_UNUSED(id);
445  QGraphicsView *v = views().value(0);
446  if (v) {
447  QRect r = sceneRect().toRect();
448  r.moveTo(v->mapToGlobal(QPoint(0, 0)));
449  return r;
450  }
451 
452  return sceneRect().toRect();
453 }
454 
455 QRegion Corona::availableScreenRegion(int id) const
456 {
457  return QRegion(screenGeometry(id));
458 }
459 
460 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s)
461 {
462  return popupPosition(item, s, Qt::AlignLeft);
463 }
464 
465 QPoint Corona::popupPosition(const QGraphicsItem *item, const QSize &s, Qt::AlignmentFlag alignment)
466 {
467  // TODO: merge both methods (also these in Applet) into one (with optional alignment) when we can break compatibility
468  // TODO: add support for more flags in the future?
469 
470  const QGraphicsItem *actualItem = item;
471 
472  const QGraphicsView *v = viewFor(item);
473 
474  if (!v) {
475  return QPoint(0, 0);
476  }
477 
478  //its own view could be hidden, for instance if item is in an hidden Dialog
479  //try to position it using the parent applet as the item
480  if (!v->isVisible()) {
481  actualItem = item->parentItem();
482  if (!actualItem) {
483  const QGraphicsWidget *widget = qgraphicsitem_cast<const QGraphicsWidget*>(item);
484  if (widget) {
485  actualItem = qobject_cast<QGraphicsItem*>(widget->parent());
486  }
487  }
488 
489  //kDebug() << actualItem;
490 
491  if (actualItem) {
492  v = viewFor(actualItem);
493  if (!v) {
494  return QPoint(0, 0);
495  }
496  }
497  }
498 
499  if (!actualItem) {
500  actualItem = item;
501  }
502 
503  QPoint pos;
504  QTransform sceneTransform = actualItem->sceneTransform();
505 
506  //swap direction if necessary
507  if (QApplication::isRightToLeft() && alignment != Qt::AlignCenter) {
508  if (alignment == Qt::AlignRight) {
509  alignment = Qt::AlignLeft;
510  } else {
511  alignment = Qt::AlignRight;
512  }
513  }
514 
515  //if the applet is rotated the popup position has to be un-transformed
516  if (sceneTransform.isRotating()) {
517  qreal angle = acos(sceneTransform.m11());
518  QTransform newTransform;
519  QPointF center = actualItem->sceneBoundingRect().center();
520 
521  newTransform.translate(center.x(), center.y());
522  newTransform.rotateRadians(-angle);
523  newTransform.translate(-center.x(), -center.y());
524  pos = v->mapFromScene(newTransform.inverted().map(actualItem->scenePos()));
525  } else {
526  pos = v->mapFromScene(actualItem->scenePos());
527  }
528 
529  pos = v->mapToGlobal(pos);
530  //kDebug() << "==> position is" << actualItem->scenePos() << v->mapFromScene(actualItem->scenePos()) << pos;
531  const Plasma::View *pv = qobject_cast<const Plasma::View *>(v);
532 
533  Plasma::Location loc = Floating;
534  if (pv && pv->containment()) {
535  loc = pv->containment()->location();
536  }
537 
538  switch (loc) {
539  case BottomEdge:
540  case TopEdge: {
541  if (alignment == Qt::AlignCenter) {
542  pos.setX(pos.x() + actualItem->boundingRect().width()/2 - s.width()/2);
543  } else if (alignment == Qt::AlignRight) {
544  pos.setX(pos.x() + actualItem->boundingRect().width() - s.width());
545  }
546 
547  if (pos.x() + s.width() > v->geometry().x() + v->geometry().width()) {
548  pos.setX((v->geometry().x() + v->geometry().width()) - s.width());
549  } else {
550  pos.setX(qMax(pos.x(), v->geometry().left()));
551  }
552  break;
553  }
554  case LeftEdge:
555  case RightEdge: {
556  if (alignment == Qt::AlignCenter) {
557  pos.setY(pos.y() + actualItem->boundingRect().height()/2 - s.height()/2);
558  } else if (alignment == Qt::AlignRight) {
559  pos.setY(pos.y() + actualItem->boundingRect().height() - s.height());
560  }
561 
562  if (pos.y() + s.height() > v->geometry().y() + v->geometry().height()) {
563  pos.setY((v->geometry().y() + v->geometry().height()) - s.height());
564  } else {
565  pos.setY(qMax(pos.y(), v->geometry().top()));
566  }
567  break;
568  }
569  default:
570  if (alignment == Qt::AlignCenter) {
571  pos.setX(pos.x() + actualItem->boundingRect().width()/2 - s.width()/2);
572  } else if (alignment == Qt::AlignRight) {
573  pos.setX(pos.x() + actualItem->boundingRect().width() - s.width());
574  }
575  break;
576  }
577 
578 
579  //are we out of screen?
580  int screen = ((pv && pv->containment()) ? pv->containment()->screen() : -1);
581  if (screen == -1) {
582  if (pv) {
583  screen = pv->screen();
584  } else {
585  // fall back to asking the actual system what screen the view is on
586  // in the case we are dealing with a non-PlasmaView QGraphicsView
587  screen = QApplication::desktop()->screenNumber(v);
588  }
589  }
590 
591  QRect screenRect = screenGeometry(screen);
592 
593  switch (loc) {
594  case BottomEdge:
595  pos.setY(v->geometry().y() - s.height());
596  break;
597  case TopEdge:
598  pos.setY(v->geometry().y() + v->geometry().height());
599  break;
600  case LeftEdge:
601  pos.setX(v->geometry().x() + v->geometry().width());
602  break;
603  case RightEdge:
604  pos.setX(v->geometry().x() - s.width());
605  break;
606  default:
607  if (pos.y() - s.height() > screenRect.top()) {
608  pos.ry() = pos.y() - s.height();
609  } else {
610  pos.ry() = pos.y() + (int)actualItem->boundingRect().size().height() + 1;
611  }
612  }
613 
614  //kDebug() << "==> rect for" << screen << "is" << screenRect;
615 
616  if (loc != LeftEdge && pos.x() + s.width() > screenRect.x() + screenRect.width()) {
617  pos.rx() -= ((pos.x() + s.width()) - (screenRect.x() + screenRect.width()));
618  }
619 
620  if (loc != TopEdge && pos.y() + s.height() > screenRect.y() + screenRect.height()) {
621  pos.ry() -= ((pos.y() + s.height()) - (screenRect.y() + screenRect.height()));
622  }
623 
624  pos.rx() = qMax(0, pos.x());
625  pos.ry() = qMax(0, pos.y());
626  return pos;
627 }
628 
629 void Corona::loadDefaultLayout()
630 {
631 }
632 
633 void Corona::setPreferredToolBoxPlugin(const Containment::Type type, const QString &plugin)
634 {
635  d->toolBoxPlugins[type] = plugin;
636  //TODO: react to plugin changes on the fly? still don't see the use case (maybe for laptops that become tablets?)
637 }
638 
639 QString Corona::preferredToolBoxPlugin(const Containment::Type type) const
640 {
641  return d->toolBoxPlugins.value(type);
642 }
643 
644 void Corona::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
645 {
646  QGraphicsScene::dragEnterEvent(event);
647 }
648 
649 void Corona::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
650 {
651  QGraphicsScene::dragLeaveEvent(event);
652 }
653 
654 void Corona::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
655 {
656  QGraphicsScene::dragMoveEvent(event);
657 }
658 
659 ImmutabilityType Corona::immutability() const
660 {
661  return d->immutability;
662 }
663 
664 void Corona::setImmutability(const ImmutabilityType immutable)
665 {
666  if (d->immutability == immutable || d->immutability == SystemImmutable) {
667  return;
668  }
669 
670  kDebug() << "setting immutability to" << immutable;
671  d->immutability = immutable;
672  d->updateContainmentImmutability();
673  //tell non-containments that might care (like plasmaapp or a custom corona)
674  emit immutabilityChanged(immutable);
675 
676  //update our actions
677  QAction *action = d->actions.action("lock widgets");
678  if (action) {
679  if (d->immutability == SystemImmutable) {
680  action->setEnabled(false);
681  action->setVisible(false);
682  } else {
683  bool unlocked = d->immutability == Mutable;
684  action->setText(unlocked ? i18n("Lock Widgets") : i18n("Unlock Widgets"));
685  action->setIcon(KIcon(unlocked ? "object-locked" : "object-unlocked"));
686  action->setEnabled(true);
687  action->setVisible(true);
688  }
689  }
690 
691  if (d->immutability != SystemImmutable) {
692  KConfigGroup cg(config(), "General");
693 
694  // we call the dptr member directly for locked since isImmutable()
695  // also checks kiosk and parent containers
696  cg.writeEntry("immutability", (int)d->immutability);
697  requestConfigSync();
698  }
699 }
700 
701 QList<Plasma::Location> Corona::freeEdges(int screen) const
702 {
703  QList<Plasma::Location> freeEdges;
704  freeEdges << Plasma::TopEdge << Plasma::BottomEdge
705  << Plasma::LeftEdge << Plasma::RightEdge;
706 
707  foreach (Containment *containment, containments()) {
708  if (containment->screen() == screen &&
709  freeEdges.contains(containment->location())) {
710  freeEdges.removeAll(containment->location());
711  }
712  }
713 
714  return freeEdges;
715 }
716 
717 QAction *Corona::action(QString name) const
718 {
719  return d->actions.action(name);
720 }
721 
722 void Corona::addAction(QString name, QAction *action)
723 {
724  d->actions.addAction(name, action);
725 }
726 
727 KAction* Corona::addAction(QString name)
728 {
729  return d->actions.addAction(name);
730 }
731 
732 QList<QAction*> Corona::actions() const
733 {
734  return d->actions.actions();
735 }
736 
737 void Corona::enableAction(const QString &name, bool enable)
738 {
739  QAction *action = d->actions.action(name);
740  if (action) {
741  action->setEnabled(enable);
742  action->setVisible(enable);
743  }
744 }
745 
746 void Corona::updateShortcuts()
747 {
748  QMutableListIterator<QWeakPointer<KActionCollection> > it(d->actionCollections);
749  while (it.hasNext()) {
750  it.next();
751  KActionCollection *collection = it.value().data();
752  if (!collection) {
753  // get rid of KActionCollections that have been deleted behind our backs
754  it.remove();
755  continue;
756  }
757 
758  collection->readSettings();
759  if (d->shortcutsDlg) {
760  d->shortcutsDlg.data()->addCollection(collection);
761  }
762  }
763 }
764 
765 void Corona::addShortcuts(KActionCollection *newShortcuts)
766 {
767  d->actionCollections << newShortcuts;
768  if (d->shortcutsDlg) {
769  d->shortcutsDlg.data()->addCollection(newShortcuts);
770  }
771 }
772 
773 void Corona::setContainmentActionsDefaults(Containment::Type containmentType, const ContainmentActionsPluginsConfig &config)
774 {
775  d->containmentActionsDefaults.insert(containmentType, config);
776 }
777 
778 ContainmentActionsPluginsConfig Corona::containmentActionsDefaults(Containment::Type containmentType)
779 {
780  return d->containmentActionsDefaults.value(containmentType);
781 }
782 
783 void Corona::setDialogManager(AbstractDialogManager *dialogManager)
784 {
785  d->dialogManager = dialogManager;
786 }
787 
788 AbstractDialogManager *Corona::dialogManager()
789 {
790  return d->dialogManager.data();
791 }
792 
793 CoronaPrivate::CoronaPrivate(Corona *corona)
794  : q(corona),
795  immutability(Mutable),
796  mimetype("text/x-plasmoidservicename"),
797  defaultContainmentPlugin("desktop"),
798  config(0),
799  actions(corona)
800 {
801  if (KGlobal::hasMainComponent()) {
802  configName = KGlobal::mainComponent().componentName() + "-appletsrc";
803  } else {
804  configName = "plasma-appletsrc";
805  }
806 }
807 
808 CoronaPrivate::~CoronaPrivate()
809 {
810  qDeleteAll(containments);
811 }
812 
813 void CoronaPrivate::init()
814 {
815  q->setStickyFocus(true);
816  configSyncTimer.setSingleShot(true);
817  QObject::connect(&configSyncTimer, SIGNAL(timeout()), q, SLOT(syncConfig()));
818 
819  //some common actions
820  actions.setConfigGroup("Shortcuts");
821 
822  KAction *lockAction = actions.addAction("lock widgets");
823  QObject::connect(lockAction, SIGNAL(triggered(bool)), q, SLOT(toggleImmutability()));
824  lockAction->setText(i18n("Lock Widgets"));
825  lockAction->setAutoRepeat(true);
826  lockAction->setIcon(KIcon("object-locked"));
827  lockAction->setData(AbstractToolBox::ControlTool);
828  lockAction->setShortcut(KShortcut("alt+d, l"));
829  lockAction->setShortcutContext(Qt::ApplicationShortcut);
830 
831  //FIXME this doesn't really belong here. desktop KCM maybe?
832  //but should the shortcuts be per-app or really-global?
833  //I don't know how to make kactioncollections use plasmarc
834  KAction *action = actions.addAction("configure shortcuts");
835  QObject::connect(action, SIGNAL(triggered()), q, SLOT(showShortcutConfig()));
836  action->setText(i18n("Shortcut Settings"));
837  action->setIcon(KIcon("configure-shortcuts"));
838  action->setAutoRepeat(false);
839  action->setData(AbstractToolBox::ConfigureTool);
840  //action->setShortcut(KShortcut("ctrl+h"));
841  action->setShortcutContext(Qt::ApplicationShortcut);
842 
843  //fake containment/applet actions
844  KActionCollection *containmentActions = AppletPrivate::defaultActions(q); //containment has to start with applet stuff
845  ContainmentPrivate::addDefaultActions(containmentActions); //now it's really containment
846  actionCollections << &actions << AppletPrivate::defaultActions(q) << containmentActions;
847  q->updateShortcuts();
848 }
849 
850 void CoronaPrivate::showShortcutConfig()
851 {
852  //show a kshortcutsdialog with the actions
853  KShortcutsDialog *dlg = shortcutsDlg.data();
854  if (!dlg) {
855  dlg = new KShortcutsDialog();
856  dlg->setModal(false);
857  dlg->setAttribute(Qt::WA_DeleteOnClose, true);
858  QObject::connect(dlg, SIGNAL(saved()), q, SIGNAL(shortcutsChanged()));
859 
860  dlg->addCollection(&actions);
861  QMutableListIterator<QWeakPointer<KActionCollection> > it(actionCollections);
862  while (it.hasNext()) {
863  it.next();
864  KActionCollection *collection = it.value().data();
865  if (!collection) {
866  // get rid of KActionCollections that have been deleted behind our backs
867  it.remove();
868  continue;
869  }
870 
871  dlg->addCollection(collection);
872  }
873  }
874 
875  KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop());
876  dlg->configure();
877  dlg->raise();
878 }
879 
880 void CoronaPrivate::toggleImmutability()
881 {
882  if (immutability == Mutable) {
883  q->setImmutability(UserImmutable);
884  } else {
885  q->setImmutability(Mutable);
886  }
887 }
888 
889 void CoronaPrivate::saveLayout(KSharedConfigPtr cg) const
890 {
891  KConfigGroup containmentsGroup(cg, "Containments");
892  foreach (const Containment *containment, containments) {
893  QString cid = QString::number(containment->id());
894  KConfigGroup containmentConfig(&containmentsGroup, cid);
895  containment->save(containmentConfig);
896  }
897 }
898 
899 void CoronaPrivate::updateContainmentImmutability()
900 {
901  foreach (Containment *c, containments) {
902  // we need to tell each containment that immutability has been altered
903  c->updateConstraints(ImmutableConstraint);
904  }
905 }
906 
907 void CoronaPrivate::containmentDestroyed(QObject *obj)
908 {
909  // we do a static_cast here since it really isn't an Containment by this
910  // point anymore since we are in the qobject dtor. we don't actually
911  // try and do anything with it, we just need the value of the pointer
912  // so this unsafe looking code is actually just fine.
913  Containment* containment = static_cast<Plasma::Containment*>(obj);
914  int index = containments.indexOf(containment);
915 
916  if (index > -1) {
917  containments.removeAt(index);
918  q->requestConfigSync();
919  }
920  }
921 
922 void CoronaPrivate::syncConfig()
923 {
924  q->config()->sync();
925  emit q->configSynced();
926 }
927 
928 Containment *CoronaPrivate::addContainment(const QString &name, const QVariantList &args, uint id, bool delayedInit)
929 {
930  QString pluginName = name;
931  Containment *containment = 0;
932  Applet *applet = 0;
933 
934  //kDebug() << "Loading" << name << args << id;
935 
936  if (pluginName.isEmpty() || pluginName == "default") {
937  // default to the desktop containment
938  pluginName = defaultContainmentPlugin;
939  }
940 
941  bool loadingNull = pluginName == "null";
942  if (!loadingNull) {
943  applet = Applet::load(pluginName, id, args);
944  containment = dynamic_cast<Containment*>(applet);
945  }
946 
947  if (!containment) {
948  if (!loadingNull) {
949  kDebug() << "loading of containment" << name << "failed.";
950  }
951 
952  // in case we got a non-Containment from Applet::loadApplet or
953  // a null containment was requested
954  if (applet) {
955  // the applet probably doesn't know what's hit it, so let's pretend it can be
956  // initialized to make assumptions in the applet's dtor safer
957  q->addItem(applet);
958  applet->init();
959  q->removeItem(applet);
960  delete applet;
961  }
962  applet = containment = new Containment(0, 0, id);
963 
964  if (loadingNull) {
965  containment->setDrawWallpaper(false);
966  } else {
967  containment->setFailedToLaunch(false);
968  }
969 
970  // we want to provide something and don't care about the failure to launch
971  containment->setFormFactor(Plasma::Planar);
972  }
973 
974  // if this is a new containment, we need to ensure that there are no stale
975  // configuration data around
976  if (id == 0) {
977  KConfigGroup conf(q->config(), "Containments");
978  conf = KConfigGroup(&conf, QString::number(containment->id()));
979  conf.deleteGroup();
980  }
981 
982  applet->d->isContainment = true;
983  containment->setPos(containment->d->preferredPos(q));
984  q->addItem(containment);
985  applet->d->setIsContainment(true, true);
986  containments.append(containment);
987 
988  if (!delayedInit) {
989  containment->init();
990  KConfigGroup cg = containment->config();
991  containment->restore(cg);
992  containment->updateConstraints(Plasma::StartupCompletedConstraint);
993  containment->save(cg);
994  q->requestConfigSync();
995  containment->flushPendingConstraintsEvents();
996  }
997 
998  QObject::connect(containment, SIGNAL(destroyed(QObject*)),
999  q, SLOT(containmentDestroyed(QObject*)));
1000  QObject::connect(containment, SIGNAL(configNeedsSaving()),
1001  q, SLOT(requestConfigSync()));
1002  QObject::connect(containment, SIGNAL(releaseVisualFocus()),
1003  q, SIGNAL(releaseVisualFocus()));
1004  QObject::connect(containment, SIGNAL(screenChanged(int,int,Plasma::Containment*)),
1005  q, SIGNAL(screenOwnerChanged(int,int,Plasma::Containment*)));
1006 
1007  if (!delayedInit) {
1008  emit q->containmentAdded(containment);
1009  }
1010 
1011  return containment;
1012 }
1013 
1014 QList<Plasma::Containment *> CoronaPrivate::importLayout(const KConfigBase &conf, bool mergeConfig)
1015 {
1016  if (const KConfigGroup *group = dynamic_cast<const KConfigGroup *>(&conf)) {
1017  if (!group->isValid()) {
1018  return QList<Containment *>();
1019  }
1020  }
1021 
1022  QList<Plasma::Containment *> newContainments;
1023  QSet<uint> containmentsIds;
1024 
1025  foreach (Containment *containment, containments) {
1026  containmentsIds.insert(containment->id());
1027  }
1028 
1029  KConfigGroup containmentsGroup(&conf, "Containments");
1030 
1031  foreach (const QString &group, containmentsGroup.groupList()) {
1032  KConfigGroup containmentConfig(&containmentsGroup, group);
1033 
1034  if (containmentConfig.entryMap().isEmpty()) {
1035  continue;
1036  }
1037 
1038  uint cid = group.toUInt();
1039  if (containmentsIds.contains(cid)) {
1040  cid = ++AppletPrivate::s_maxAppletId;
1041  } else if (cid > AppletPrivate::s_maxAppletId) {
1042  AppletPrivate::s_maxAppletId = cid;
1043  }
1044 
1045  if (mergeConfig) {
1046  KConfigGroup realConf(q->config(), "Containments");
1047  realConf = KConfigGroup(&realConf, QString::number(cid));
1048  // in case something was there before us
1049  realConf.deleteGroup();
1050  containmentConfig.copyTo(&realConf);
1051  }
1052 
1053  //kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group;
1054  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Adding Containment" << containmentConfig.readEntry("plugin", QString());
1055  Containment *c = addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(), cid, true);
1056  if (!c) {
1057  continue;
1058  }
1059 
1060  newContainments.append(c);
1061  containmentsIds.insert(c->id());
1062 
1063  c->init();
1064  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Init Containment" << c->pluginName();
1065  c->restore(containmentConfig);
1066  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Restored Containment" << c->pluginName();
1067  }
1068 
1069  foreach (Containment *containment, newContainments) {
1070  containment->updateConstraints(Plasma::StartupCompletedConstraint);
1071  containment->d->initApplets();
1072  emit q->containmentAdded(containment);
1073  kDebug() << "!!{} STARTUP TIME" << QTime().msecsTo(QTime::currentTime()) << "Containment" << containment->name();
1074  }
1075 
1076  return newContainments;
1077 }
1078 
1079 } // namespace Plasma
1080 
1081 #include "corona.moc"
1082 
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Thu Sep 25 2014 04:19:20 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.11.5 API Reference

Skip menu "kdelibs-4.11.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal