1 /*
2 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmaibox.org>
3 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "lattecorona.h"
9
10 // local
11 #include <coretypes.h>
12 #include "alternativeshelper.h"
13 #include "apptypes.h"
14 #include "lattedockadaptor.h"
15 #include "screenpool.h"
16 #include "data/generictable.h"
17 #include "data/layouticondata.h"
18 #include "declarativeimports/interfaces.h"
19 #include "indicator/factory.h"
20 #include "layout/abstractlayout.h"
21 #include "layout/centrallayout.h"
22 #include "layout/genericlayout.h"
23 #include "layouts/importer.h"
24 #include "layouts/manager.h"
25 #include "layouts/synchronizer.h"
26 #include "shortcuts/globalshortcuts.h"
27 #include "package/lattepackage.h"
28 #include "plasma/extended/backgroundcache.h"
29 #include "plasma/extended/backgroundtracker.h"
30 #include "plasma/extended/screengeometries.h"
31 #include "plasma/extended/screenpool.h"
32 #include "plasma/extended/theme.h"
33 #include "settings/universalsettings.h"
34 #include "templates/templatesmanager.h"
35 #include "view/view.h"
36 #include "view/settings/viewsettingsfactory.h"
37 #include "view/windowstracker/windowstracker.h"
38 #include "view/windowstracker/allscreenstracker.h"
39 #include "view/windowstracker/currentscreentracker.h"
40 #include "wm/abstractwindowinterface.h"
41 #include "wm/schemecolors.h"
42 #include "wm/waylandinterface.h"
43 #include "wm/xwindowinterface.h"
44 #include "wm/tracker/lastactivewindow.h"
45 #include "wm/tracker/schemes.h"
46 #include "wm/tracker/windowstracker.h"
47
48 // Qt
49 #include <QAction>
50 #include <QApplication>
51 #include <QScreen>
52 #include <QDBusConnection>
53 #include <QDebug>
54 #include <QDesktopWidget>
55 #include <QFile>
56 #include <QFontDatabase>
57 #include <QQmlContext>
58 #include <QProcess>
59
60 // Plasma
61 #include <Plasma>
62 #include <Plasma/Corona>
63 #include <Plasma/Containment>
64 #include <PlasmaQuick/ConfigView>
65
66 // KDE
67 #include <KActionCollection>
68 #include <KPluginMetaData>
69 #include <KGlobalAccel>
70 #include <KLocalizedString>
71 #include <KPackage/Package>
72 #include <KPackage/PackageLoader>
73 #include <KAboutData>
74 #include <KActivities/Consumer>
75 #include <KDeclarative/QmlObjectSharedEngine>
76 #include <KWindowSystem>
77 #include <KWayland/Client/connection_thread.h>
78 #include <KWayland/Client/registry.h>
79 #include <KWayland/Client/plasmashell.h>
80 #include <KWayland/Client/plasmawindowmanagement.h>
81
82 namespace Latte {
83
Corona(bool defaultLayoutOnStartup,QString layoutNameOnStartUp,QString addViewTemplateName,int userSetMemoryUsage,QObject * parent)84 Corona::Corona(bool defaultLayoutOnStartup, QString layoutNameOnStartUp, QString addViewTemplateName, int userSetMemoryUsage, QObject *parent)
85 : Plasma::Corona(parent),
86 m_defaultLayoutOnStartup(defaultLayoutOnStartup),
87 m_startupAddViewTemplateName(addViewTemplateName),
88 m_userSetMemoryUsage(userSetMemoryUsage),
89 m_layoutNameOnStartUp(layoutNameOnStartUp),
90 m_activitiesConsumer(new KActivities::Consumer(this)),
91 m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)),
92 m_indicatorFactory(new Indicator::Factory(this)),
93 m_universalSettings(new UniversalSettings(KSharedConfig::openConfig(), this)),
94 m_globalShortcuts(new GlobalShortcuts(this)),
95 m_plasmaScreenPool(new PlasmaExtended::ScreenPool(this)),
96 m_themeExtended(new PlasmaExtended::Theme(KSharedConfig::openConfig(), this)),
97 m_viewSettingsFactory(new ViewSettingsFactory(this)),
98 m_templatesManager(new Templates::Manager(this)),
99 m_layoutsManager(new Layouts::Manager(this)),
100 m_plasmaGeometries(new PlasmaExtended::ScreenGeometries(this)),
101 m_dialogShadows(new PanelShadows(this, QStringLiteral("dialogs/background")))
102 {
103 connect(qApp, &QApplication::aboutToQuit, this, &Corona::onAboutToQuit);
104
105 //! create the window manager
106 if (KWindowSystem::isPlatformWayland()) {
107 m_wm = new WindowSystem::WaylandInterface(this);
108 } else {
109 m_wm = new WindowSystem::XWindowInterface(this);
110 }
111
112 setupWaylandIntegration();
113
114 KPackage::Package package(new Latte::Package(this));
115
116 m_screenPool->load();
117
118 if (!package.isValid()) {
119 qWarning() << staticMetaObject.className()
120 << "the package" << package.metadata().rawData() << "is invalid!";
121 return;
122 } else {
123 qDebug() << staticMetaObject.className()
124 << "the package" << package.metadata().rawData() << "is valid!";
125 }
126
127 setKPackage(package);
128 //! universal settings / extendedtheme must be loaded after the package has been set
129 m_universalSettings->load();
130 m_themeExtended->load();
131
132 qmlRegisterTypes();
133
134 if (m_activitiesConsumer && (m_activitiesConsumer->serviceStatus() == KActivities::Consumer::Running)) {
135 load();
136 }
137
138 connect(m_activitiesConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load);
139
140 m_viewsScreenSyncTimer.setSingleShot(true);
141 m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval());
142 connect(&m_viewsScreenSyncTimer, &QTimer::timeout, this, &Corona::syncLatteViewsToScreens);
143 connect(m_universalSettings, &UniversalSettings::screenTrackerIntervalChanged, this, [this]() {
144 m_viewsScreenSyncTimer.setInterval(m_universalSettings->screenTrackerInterval());
145 });
146
147 //! Dbus adaptor initialization
148 new LatteDockAdaptor(this);
149 QDBusConnection dbus = QDBusConnection::sessionBus();
150 dbus.registerObject(QStringLiteral("/Latte"), this);
151 }
152
~Corona()153 Corona::~Corona()
154 {
155 /*m_inQuit = true;
156
157 //! BEGIN: Give the time to slide-out views when closing
158 m_layoutsManager->synchronizer()->hideAllViews();
159 m_viewSettingsFactory->deleteLater();
160
161 m_viewsScreenSyncTimer.stop();
162
163 if (m_layoutsManager->memoryUsage() == MemoryUsage::SingleLayout) {
164 cleanConfig();
165 }
166
167 qDebug() << "Latte Corona - unload: containments ...";
168 m_layoutsManager->unload();*/
169
170 m_plasmaGeometries->deleteLater();
171 m_wm->deleteLater();
172 m_dialogShadows->deleteLater();
173 m_globalShortcuts->deleteLater();
174 m_layoutsManager->deleteLater();
175 m_screenPool->deleteLater();
176 m_universalSettings->deleteLater();
177 m_plasmaScreenPool->deleteLater();
178 m_themeExtended->deleteLater();
179 m_indicatorFactory->deleteLater();
180
181 disconnect(m_activitiesConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load);
182 delete m_activitiesConsumer;
183
184 qDebug() << "Latte Corona - deleted...";
185
186 if (!m_importFullConfigurationFile.isEmpty()) {
187 //!NOTE: Restart latte to import the new configuration
188 QString importCommand = "latte-dock --import-full \"" + m_importFullConfigurationFile + "\"";
189 qDebug() << "Executing Import Full Configuration command : " << importCommand;
190
191 QProcess::startDetached(importCommand);
192 }
193 }
194
onAboutToQuit()195 void Corona::onAboutToQuit()
196 {
197 m_inQuit = true;
198
199 //! BEGIN: Give the time to slide-out views when closing
200 m_layoutsManager->synchronizer()->hideAllViews();
201 m_viewSettingsFactory->deleteLater();
202
203 m_viewsScreenSyncTimer.stop();
204
205 if (m_layoutsManager->memoryUsage() == MemoryUsage::SingleLayout) {
206 cleanConfig();
207 }
208
209 qDebug() << "Latte Corona - unload: containments ...";
210 m_layoutsManager->unload();
211 }
212
load()213 void Corona::load()
214 {
215 if (m_activitiesConsumer && (m_activitiesConsumer->serviceStatus() == KActivities::Consumer::Running) && m_activitiesStarting) {
216 m_activitiesStarting = false;
217
218 disconnect(m_activitiesConsumer, &KActivities::Consumer::serviceStatusChanged, this, &Corona::load);
219
220 m_templatesManager->init();
221 m_layoutsManager->init();
222
223 connect(this, &Corona::availableScreenRectChangedFrom, this, &Plasma::Corona::availableScreenRectChanged);
224 connect(this, &Corona::availableScreenRegionChangedFrom, this, &Plasma::Corona::availableScreenRegionChanged);
225 connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &Corona::primaryOutputChanged, Qt::UniqueConnection);
226 connect(m_screenPool, &ScreenPool::primaryPoolChanged, this, &Corona::screenCountChanged);
227
228 QString loadLayoutName = "";
229
230 if (m_userSetMemoryUsage != -1) {
231 MemoryUsage::LayoutsMemory usage = static_cast<MemoryUsage::LayoutsMemory>(m_userSetMemoryUsage);
232 m_universalSettings->setLayoutsMemoryUsage(usage);
233 }
234
235 if (!m_defaultLayoutOnStartup && m_layoutNameOnStartUp.isEmpty()) {
236 if (m_universalSettings->layoutsMemoryUsage() == MemoryUsage::MultipleLayouts) {
237 loadLayoutName = "";
238 } else {
239 loadLayoutName = m_universalSettings->singleModeLayoutName();
240
241 if (!m_layoutsManager->synchronizer()->layoutExists(loadLayoutName)) {
242 //! If chosen layout does not exist, force Default layout loading
243 QString defaultLayoutTemplateName = i18n(Templates::DEFAULTLAYOUTTEMPLATENAME);
244 loadLayoutName = defaultLayoutTemplateName;
245
246 if (!m_layoutsManager->synchronizer()->layoutExists(defaultLayoutTemplateName)) {
247 //! If Default layout does not exist at all, create it
248 QString path = m_templatesManager->newLayout("", defaultLayoutTemplateName);
249 m_layoutsManager->setOnAllActivities(Layout::AbstractLayout::layoutName(path));
250 }
251 }
252 }
253 } else if (m_defaultLayoutOnStartup) {
254 //! force loading a NEW default layout even though a default layout may already exists
255 QString newDefaultLayoutPath = m_templatesManager->newLayout("", i18n(Templates::DEFAULTLAYOUTTEMPLATENAME));
256 loadLayoutName = Layout::AbstractLayout::layoutName(newDefaultLayoutPath);
257 m_universalSettings->setLayoutsMemoryUsage(MemoryUsage::SingleLayout);
258 } else {
259 loadLayoutName = m_layoutNameOnStartUp;
260 m_universalSettings->setLayoutsMemoryUsage(MemoryUsage::SingleLayout);
261 }
262
263 m_layoutsManager->loadLayoutOnStartup(loadLayoutName);
264
265 //! load screens signals such screenGeometryChanged in order to support
266 //! plasmoid.screenGeometry properly
267 for (QScreen *screen : qGuiApp->screens()) {
268 addOutput(screen);
269 }
270
271 connect(m_layoutsManager->synchronizer(), &Layouts::Synchronizer::initializationFinished, [this]() {
272 if (!m_startupAddViewTemplateName.isEmpty()) {
273 //! user requested through cmd startup to add view from specific view template and we can add it after the startup
274 //! sequence has loaded all required layouts properly
275 addView(0, m_startupAddViewTemplateName);
276 m_startupAddViewTemplateName = "";
277 }
278 });
279
280 m_inStartup = false;
281
282 connect(qGuiApp, &QGuiApplication::screenAdded, this, &Corona::addOutput, Qt::UniqueConnection);
283 connect(qGuiApp, &QGuiApplication::screenRemoved, this, &Corona::screenRemoved, Qt::UniqueConnection);
284 }
285 }
286
unload()287 void Corona::unload()
288 {
289 qDebug() << "unload: removing containments...";
290
291 while (!containments().isEmpty()) {
292 //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona
293 //this form doesn't crash, while qDeleteAll(containments()) does
294 delete containments().first();
295 }
296 }
297
setupWaylandIntegration()298 void Corona::setupWaylandIntegration()
299 {
300 if (!KWindowSystem::isPlatformWayland()) {
301 return;
302 }
303
304 using namespace KWayland::Client;
305
306 auto connection = ConnectionThread::fromApplication(this);
307
308 if (!connection) {
309 return;
310 }
311
312 Registry *registry{new Registry(this)};
313 registry->create(connection);
314
315 connect(registry, &Registry::plasmaShellAnnounced, this
316 , [this, registry](quint32 name, quint32 version) {
317 m_waylandCorona = registry->createPlasmaShell(name, version, this);
318 });
319
320 QObject::connect(registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced,
321 [this, registry](quint32 name, quint32 version) {
322 KWayland::Client::PlasmaWindowManagement *pwm = registry->createPlasmaWindowManagement(name, version, this);
323
324 WindowSystem::WaylandInterface *wI = qobject_cast<WindowSystem::WaylandInterface *>(m_wm);
325
326 if (wI) {
327 wI->initWindowManagement(pwm);
328 }
329 });
330
331 #if KF5_VERSION_MINOR >= 52
332 QObject::connect(registry, &KWayland::Client::Registry::plasmaVirtualDesktopManagementAnnounced,
333 [this, registry] (quint32 name, quint32 version) {
334 KWayland::Client::PlasmaVirtualDesktopManagement *vdm = registry->createPlasmaVirtualDesktopManagement(name, version, this);
335
336 WindowSystem::WaylandInterface *wI = qobject_cast<WindowSystem::WaylandInterface *>(m_wm);
337
338 if (wI) {
339 wI->initVirtualDesktopManagement(vdm);
340 }
341 });
342 #endif
343
344 registry->setup();
345 connection->roundtrip();
346 }
347
waylandCoronaInterface() const348 KWayland::Client::PlasmaShell *Corona::waylandCoronaInterface() const
349 {
350 return m_waylandCorona;
351 }
352
cleanConfig()353 void Corona::cleanConfig()
354 {
355 auto containmentsEntries = config()->group("Containments");
356 bool changed = false;
357
358 for(const auto &cId : containmentsEntries.groupList()) {
359 if (!containmentExists(cId.toUInt())) {
360 //cleanup obsolete containments
361 containmentsEntries.group(cId).deleteGroup();
362 changed = true;
363 qDebug() << "obsolete containment configuration deleted:" << cId;
364 } else {
365 //cleanup obsolete applets of running containments
366 auto appletsEntries = containmentsEntries.group(cId).group("Applets");
367
368 for(const auto &appletId : appletsEntries.groupList()) {
369 if (!appletExists(cId.toUInt(), appletId.toUInt())) {
370 appletsEntries.group(appletId).deleteGroup();
371 changed = true;
372 qDebug() << "obsolete applet configuration deleted:" << appletId;
373 }
374 }
375 }
376 }
377
378 if (changed) {
379 config()->sync();
380 qDebug() << "configuration file cleaned...";
381 }
382 }
383
containmentExists(uint id) const384 bool Corona::containmentExists(uint id) const
385 {
386 for(const auto containment : containments()) {
387 if (id == containment->id()) {
388 return true;
389 }
390 }
391
392 return false;
393 }
394
appletExists(uint containmentId,uint appletId) const395 bool Corona::appletExists(uint containmentId, uint appletId) const
396 {
397 Plasma::Containment *containment = nullptr;
398
399 for(const auto cont : containments()) {
400 if (containmentId == cont->id()) {
401 containment = cont;
402 break;
403 }
404 }
405
406 if (!containment) {
407 return false;
408 }
409
410 for(const auto applet : containment->applets()) {
411 if (applet->id() == appletId) {
412 return true;
413 }
414 }
415
416 return false;
417 }
418
inQuit() const419 bool Corona::inQuit() const
420 {
421 return m_inQuit;
422 }
423
activitiesConsumer() const424 KActivities::Consumer *Corona::activitiesConsumer() const
425 {
426 return m_activitiesConsumer;
427 }
428
dialogShadows() const429 PanelShadows *Corona::dialogShadows() const
430 {
431 return m_dialogShadows;
432 }
433
globalShortcuts() const434 GlobalShortcuts *Corona::globalShortcuts() const
435 {
436 return m_globalShortcuts;
437 }
438
screenPool() const439 ScreenPool *Corona::screenPool() const
440 {
441 return m_screenPool;
442 }
443
universalSettings() const444 UniversalSettings *Corona::universalSettings() const
445 {
446 return m_universalSettings;
447 }
448
viewSettingsFactory() const449 ViewSettingsFactory *Corona::viewSettingsFactory() const
450 {
451 return m_viewSettingsFactory;
452 }
453
wm() const454 WindowSystem::AbstractWindowInterface *Corona::wm() const
455 {
456 return m_wm;
457 }
458
indicatorFactory() const459 Indicator::Factory *Corona::indicatorFactory() const
460 {
461 return m_indicatorFactory;
462 }
463
layoutsManager() const464 Layouts::Manager *Corona::layoutsManager() const
465 {
466 return m_layoutsManager;
467 }
468
templatesManager() const469 Templates::Manager *Corona::templatesManager() const
470 {
471 return m_templatesManager;
472 }
473
plasmaScreenPool() const474 PlasmaExtended::ScreenPool *Corona::plasmaScreenPool() const
475 {
476 return m_plasmaScreenPool;
477 }
478
themeExtended() const479 PlasmaExtended::Theme *Corona::themeExtended() const
480 {
481 return m_themeExtended;
482 }
483
numScreens() const484 int Corona::numScreens() const
485 {
486 return qGuiApp->screens().count();
487 }
488
screenGeometry(int id) const489 QRect Corona::screenGeometry(int id) const
490 {
491 const auto screens = qGuiApp->screens();
492 const QScreen *screen{qGuiApp->primaryScreen()};
493
494 QString screenName;
495
496 if (m_screenPool->hasScreenId(id)) {
497 screenName = m_screenPool->connector(id);
498 }
499
500 for(const auto scr : screens) {
501 if (scr->name() == screenName) {
502 screen = scr;
503 break;
504 }
505 }
506
507 return screen->geometry();
508 }
509
centralLayout(QString name) const510 CentralLayout *Corona::centralLayout(QString name) const
511 {
512 CentralLayout *result{nullptr};
513
514 if (!name.isEmpty()) {
515 result = m_layoutsManager->synchronizer()->centralLayout(name);
516 }
517
518 return result;
519 }
520
layout(QString name) const521 Layout::GenericLayout *Corona::layout(QString name) const
522 {
523 Layout::GenericLayout *result{nullptr};
524
525 if (!name.isEmpty()) {
526 result = m_layoutsManager->synchronizer()->layout(name);
527 }
528
529 return result;
530 }
531
availableScreenRegion(int id) const532 QRegion Corona::availableScreenRegion(int id) const
533 {
534 //! ignore modes are added in order for notifications to be placed
535 //! in better positioning and not overlap with sidebars or usually hidden views
536 QList<Types::Visibility> ignoremodes({Latte::Types::AutoHide,
537 Latte::Types::SidebarOnDemand,
538 Latte::Types::SidebarAutoHide});
539
540
541 return availableScreenRegionWithCriteria(id,
542 QString(),
543 ignoremodes);
544 }
545
availableScreenRegionWithCriteria(int id,QString activityid,QList<Types::Visibility> ignoreModes,QList<Plasma::Types::Location> ignoreEdges,bool ignoreExternalPanels,bool desktopUse) const546 QRegion Corona::availableScreenRegionWithCriteria(int id,
547 QString activityid,
548 QList<Types::Visibility> ignoreModes,
549 QList<Plasma::Types::Location> ignoreEdges,
550 bool ignoreExternalPanels,
551 bool desktopUse) const
552 {
553 const QScreen *screen = m_screenPool->screenForId(id);
554 bool inCurrentActivity{activityid.isEmpty()};
555
556 if (!screen) {
557 return {};
558 }
559
560 QRegion available = ignoreExternalPanels ? screen->geometry() : screen->availableGeometry();
561
562 QList<Latte::View *> views;
563
564 if (inCurrentActivity) {
565 views = m_layoutsManager->synchronizer()->viewsBasedOnActivityId(m_activitiesConsumer->currentActivity());
566 } else {
567 views = m_layoutsManager->synchronizer()->viewsBasedOnActivityId(activityid);
568 }
569
570 if (views.isEmpty()) {
571 return available;
572 }
573
574 //! blacklist irrelevant visibility modes
575 if (!ignoreModes.contains(Latte::Types::None)) {
576 ignoreModes << Latte::Types::None;
577 }
578
579 if (!ignoreModes.contains(Latte::Types::NormalWindow)) {
580 ignoreModes << Latte::Types::NormalWindow;
581 }
582
583 bool allEdges = ignoreEdges.isEmpty();
584
585 for (const auto *view : views) {
586 if (view && view->containment() && view->screen() == screen
587 && ((allEdges || !ignoreEdges.contains(view->location()))
588 && (view->visibility() && !ignoreModes.contains(view->visibility()->mode())))) {
589 int realThickness = view->normalThickness();
590
591 int x = 0; int y = 0; int w = 0; int h = 0;
592
593 switch (view->formFactor()) {
594 case Plasma::Types::Horizontal:
595 if (view->behaveAsPlasmaPanel()) {
596 w = view->width();
597 x = view->x();
598 } else {
599 w = view->maxLength() * view->width();
600 int offsetW = view->offset() * view->width();
601
602 switch (view->alignment()) {
603 case Latte::Types::Left:
604 x = view->x() + offsetW;
605 break;
606
607 case Latte::Types::Center:
608 case Latte::Types::Justify:
609 x = (view->geometry().center().x() - w/2) + offsetW;
610 break;
611
612 case Latte::Types::Right:
613 x = view->geometry().right() - w - offsetW;
614 break;
615 }
616 }
617 break;
618 case Plasma::Types::Vertical:
619 if (view->behaveAsPlasmaPanel()) {
620 h = view->height();
621 y = view->y();
622 } else {
623 h = view->maxLength() * view->height();
624 int offsetH = view->offset() * view->height();
625
626 switch (view->alignment()) {
627 case Latte::Types::Top:
628 y = view->y() + offsetH;
629 break;
630
631 case Latte::Types::Center:
632 case Latte::Types::Justify:
633 y = (view->geometry().center().y() - h/2) + offsetH;
634 break;
635
636 case Latte::Types::Bottom:
637 y = view->geometry().bottom() - h - offsetH;
638 break;
639 }
640 }
641 break;
642 }
643
644 // Usually availableScreenRect is used by the desktop,
645 // but Latte don't have desktop, then here just
646 // need calculate available space for top and bottom location,
647 // because the left and right are those who dodge others views
648 switch (view->location()) {
649 case Plasma::Types::TopEdge:
650 if (view->behaveAsPlasmaPanel()) {
651 QRect viewGeometry = view->geometry();
652
653 if (desktopUse) {
654 //! ignore any real window slide outs in all cases
655 viewGeometry.moveTop(view->screen()->geometry().top() + view->screenEdgeMargin());
656 }
657
658 available -= viewGeometry;
659 } else {
660 y = view->y();
661 available -= QRect(x, y, w, realThickness);
662 }
663
664 break;
665
666 case Plasma::Types::BottomEdge:
667 if (view->behaveAsPlasmaPanel()) {
668 QRect viewGeometry = view->geometry();
669
670 if (desktopUse) {
671 //! ignore any real window slide outs in all cases
672 viewGeometry.moveTop(view->screen()->geometry().bottom() - view->screenEdgeMargin() - viewGeometry.height());
673 }
674
675 available -= viewGeometry;
676 } else {
677 y = view->geometry().bottom() - realThickness + 1;
678 available -= QRect(x, y, w, realThickness);
679 }
680
681 break;
682
683 case Plasma::Types::LeftEdge:
684 if (view->behaveAsPlasmaPanel()) {
685 QRect viewGeometry = view->geometry();
686
687 if (desktopUse) {
688 //! ignore any real window slide outs in all cases
689 viewGeometry.moveLeft(view->screen()->geometry().left() + view->screenEdgeMargin());
690 }
691
692 available -= viewGeometry;
693 } else {
694 x = view->x();
695 available -= QRect(x, y, realThickness, h);
696 }
697
698 break;
699
700 case Plasma::Types::RightEdge:
701 if (view->behaveAsPlasmaPanel()) {
702 QRect viewGeometry = view->geometry();
703
704 if (desktopUse) {
705 //! ignore any real window slide outs in all cases
706 viewGeometry.moveLeft(view->screen()->geometry().right() - view->screenEdgeMargin() - viewGeometry.width());
707 }
708
709 available -= viewGeometry;
710 } else {
711 x = view->geometry().right() - realThickness + 1;
712 available -= QRect(x, y, realThickness, h);
713 }
714
715 break;
716
717 default:
718 //! bypass clang warnings
719 break;
720 }
721 }
722 }
723
724 /*qDebug() << "::::: FREE AREAS :::::";
725
726 for (int i = 0; i < available.rectCount(); ++i) {
727 qDebug() << available.rects().at(i);
728 }
729
730 qDebug() << "::::: END OF FREE AREAS :::::";*/
731
732 return available;
733 }
734
availableScreenRect(int id) const735 QRect Corona::availableScreenRect(int id) const
736 {
737 //! ignore modes are added in order for notifications to be placed
738 //! in better positioning and not overlap with sidebars or usually hidden views
739 QList<Types::Visibility> ignoremodes({Latte::Types::AutoHide,
740 Latte::Types::SidebarOnDemand,
741 Latte::Types::SidebarAutoHide});
742
743 return availableScreenRectWithCriteria(id,
744 QString(),
745 ignoremodes);
746 }
747
availableScreenRectWithCriteria(int id,QString activityid,QList<Types::Visibility> ignoreModes,QList<Plasma::Types::Location> ignoreEdges,bool ignoreExternalPanels,bool desktopUse) const748 QRect Corona::availableScreenRectWithCriteria(int id,
749 QString activityid,
750 QList<Types::Visibility> ignoreModes,
751 QList<Plasma::Types::Location> ignoreEdges,
752 bool ignoreExternalPanels,
753 bool desktopUse) const
754 {
755 const QScreen *screen = m_screenPool->screenForId(id);
756 bool inCurrentActivity{activityid.isEmpty()};
757
758 if (!screen) {
759 return {};
760 }
761
762 QRect available = ignoreExternalPanels ? screen->geometry() : screen->availableGeometry();
763
764 QList<Latte::View *> views;
765
766 if (inCurrentActivity) {
767 views = m_layoutsManager->synchronizer()->viewsBasedOnActivityId(m_activitiesConsumer->currentActivity());
768 } else {
769 views = m_layoutsManager->synchronizer()->viewsBasedOnActivityId(activityid);
770 }
771
772 if (views.isEmpty()) {
773 return available;
774 }
775
776 //! blacklist irrelevant visibility modes
777 if (!ignoreModes.contains(Latte::Types::None)) {
778 ignoreModes << Latte::Types::None;
779 }
780
781 if (!ignoreModes.contains(Latte::Types::NormalWindow)) {
782 ignoreModes << Latte::Types::NormalWindow;
783 }
784
785 bool allEdges = ignoreEdges.isEmpty();
786
787 for (const auto *view : views) {
788 if (view && view->containment() && view->screen() == screen
789 && ((allEdges || !ignoreEdges.contains(view->location()))
790 && (view->visibility() && !ignoreModes.contains(view->visibility()->mode())))) {
791
792 int appliedThickness = view->behaveAsPlasmaPanel() ? view->screenEdgeMargin() + view->normalThickness() : view->normalThickness();
793
794 // Usually availableScreenRect is used by the desktop,
795 // but Latte don't have desktop, then here just
796 // need calculate available space for top and bottom location,
797 // because the left and right are those who dodge others docks
798 switch (view->location()) {
799 case Plasma::Types::TopEdge:
800 if (view->behaveAsPlasmaPanel() && desktopUse) {
801 //! ignore any real window slide outs in all cases
802 available.setTop(qMax(available.top(), view->screen()->geometry().top() + appliedThickness));
803 } else {
804 available.setTop(qMax(available.top(), view->y() + appliedThickness));
805 }
806 break;
807
808 case Plasma::Types::BottomEdge:
809 if (view->behaveAsPlasmaPanel() && desktopUse) {
810 //! ignore any real window slide outs in all cases
811 available.setBottom(qMin(available.bottom(), view->screen()->geometry().bottom() - appliedThickness));
812 } else {
813 available.setBottom(qMin(available.bottom(), view->y() + view->height() - appliedThickness));
814 }
815 break;
816
817 case Plasma::Types::LeftEdge:
818 if (view->behaveAsPlasmaPanel() && desktopUse) {
819 //! ignore any real window slide outs in all cases
820 available.setLeft(qMax(available.left(), view->screen()->geometry().left() + appliedThickness));
821 } else {
822 available.setLeft(qMax(available.left(), view->x() + appliedThickness));
823 }
824 break;
825
826 case Plasma::Types::RightEdge:
827 if (view->behaveAsPlasmaPanel() && desktopUse) {
828 //! ignore any real window slide outs in all cases
829 available.setRight(qMin(available.right(), view->screen()->geometry().right() - appliedThickness));
830 } else {
831 available.setRight(qMin(available.right(), view->x() + view->width() - appliedThickness));
832 }
833 break;
834
835 default:
836 //! bypass clang warnings
837 break;
838 }
839 }
840 }
841
842 return available;
843 }
844
addOutput(QScreen * screen)845 void Corona::addOutput(QScreen *screen)
846 {
847 Q_ASSERT(screen);
848
849 int id = m_screenPool->id(screen->name());
850
851 if (id == -1) {
852 m_screenPool->insertScreenMapping(screen->name());
853 }
854
855 connect(screen, &QScreen::geometryChanged, this, [ = ]() {
856 const int id = m_screenPool->id(screen->name());
857
858 if (id >= 0) {
859 emit screenGeometryChanged(id);
860 emit availableScreenRegionChanged();
861 emit availableScreenRectChanged();
862 }
863 });
864
865 emit availableScreenRectChanged();
866 emit screenAdded(m_screenPool->id(screen->name()));
867
868 screenCountChanged();
869 }
870
primaryOutputChanged()871 void Corona::primaryOutputChanged()
872 {
873 m_viewsScreenSyncTimer.start();
874 }
875
screenRemoved(QScreen * screen)876 void Corona::screenRemoved(QScreen *screen)
877 {
878 screenCountChanged();
879 }
880
screenCountChanged()881 void Corona::screenCountChanged()
882 {
883 m_viewsScreenSyncTimer.start();
884 }
885
886 //! the central functions that updates loading/unloading latteviews
887 //! concerning screen changed (for multi-screen setups mainly)
syncLatteViewsToScreens()888 void Corona::syncLatteViewsToScreens()
889 {
890 m_layoutsManager->synchronizer()->syncLatteViewsToScreens();
891 }
892
primaryScreenId() const893 int Corona::primaryScreenId() const
894 {
895 return m_screenPool->id(qGuiApp->primaryScreen()->name());
896 }
897
quitApplication()898 void Corona::quitApplication()
899 {
900 m_inQuit = true;
901
902 //! this code must be called asynchronously because it is called
903 //! also from qml (Settings window).
904 QTimer::singleShot(300, [this]() {
905 m_layoutsManager->hideLatteSettingsDialog();
906 m_layoutsManager->synchronizer()->hideAllViews();
907 });
908
909 //! give the time for the views to hide themselves
910 QTimer::singleShot(800, [this]() {
911 qGuiApp->quit();
912 });
913 }
914
aboutApplication()915 void Corona::aboutApplication()
916 {
917 if (aboutDialog) {
918 aboutDialog->hide();
919 aboutDialog->deleteLater();
920 }
921
922 aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData());
923 connect(aboutDialog.data(), &QDialog::finished, aboutDialog.data(), &QObject::deleteLater);
924 m_wm->skipTaskBar(*aboutDialog);
925 m_wm->setKeepAbove(aboutDialog->winId(), true);
926
927 aboutDialog->show();
928 }
929
loadDefaultLayout()930 void Corona::loadDefaultLayout()
931 {
932 //disabled
933 }
934
screenForContainment(const Plasma::Containment * containment) const935 int Corona::screenForContainment(const Plasma::Containment *containment) const
936 {
937 //FIXME: indexOf is not a proper way to support multi-screen
938 // as for environment to environment the indexes change
939 // also there is the following issue triggered
940 // from latteView adaptToScreen()
941 //
942 // in a multi-screen environment that
943 // primary screen is not set to 0 it was
944 // created an endless showing loop at
945 // startup (catch-up race) between
946 // screen:0 and primaryScreen
947
948 //case in which this containment is child of an applet, hello systray :)
949 if (Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent())) {
950 if (Plasma::Containment *cont = parentApplet->containment()) {
951 return screenForContainment(cont);
952 } else {
953 return -1;
954 }
955 }
956
957 Plasma::Containment *c = const_cast<Plasma::Containment *>(containment);
958 int scrId = m_layoutsManager->synchronizer()->screenForContainment(c);
959
960 if (scrId >= 0) {
961 return scrId;
962 }
963
964 return containment->lastScreen();
965 }
966
showAlternativesForApplet(Plasma::Applet * applet)967 void Corona::showAlternativesForApplet(Plasma::Applet *applet)
968 {
969 const QString alternativesQML = kPackage().filePath("appletalternativesui");
970
971 if (alternativesQML.isEmpty()) {
972 return;
973 }
974
975 Latte::View *latteView = m_layoutsManager->synchronizer()->viewForContainment(applet->containment());
976
977 KDeclarative::QmlObjectSharedEngine *qmlObj{nullptr};
978
979 if (latteView) {
980 latteView->setAlternativesIsShown(true);
981 qmlObj = new KDeclarative::QmlObjectSharedEngine(latteView);
982 } else {
983 qmlObj = new KDeclarative::QmlObjectSharedEngine(this);
984 }
985
986 qmlObj->setInitializationDelayed(true);
987 qmlObj->setSource(QUrl::fromLocalFile(alternativesQML));
988
989 AlternativesHelper *helper = new AlternativesHelper(applet, qmlObj);
990 qmlObj->rootContext()->setContextProperty(QStringLiteral("alternativesHelper"), helper);
991
992 m_alternativesObjects << qmlObj;
993 qmlObj->completeInitialization();
994
995 //! Alternative dialog signals
996 connect(helper, &QObject::destroyed, this, [latteView]() {
997 latteView->setAlternativesIsShown(false);
998 });
999
1000 connect(qmlObj->rootObject(), SIGNAL(visibleChanged(bool)),
1001 this, SLOT(alternativesVisibilityChanged(bool)));
1002
1003 connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj](bool destroyed) {
1004 if (!destroyed) {
1005 return;
1006 }
1007
1008 QMutableListIterator<KDeclarative::QmlObjectSharedEngine *> it(m_alternativesObjects);
1009
1010 while (it.hasNext()) {
1011 KDeclarative::QmlObjectSharedEngine *obj = it.next();
1012
1013 if (obj == qmlObj) {
1014 it.remove();
1015 obj->deleteLater();
1016 }
1017 }
1018 });
1019 }
1020
alternativesVisibilityChanged(bool visible)1021 void Corona::alternativesVisibilityChanged(bool visible)
1022 {
1023 if (visible) {
1024 return;
1025 }
1026
1027 QObject *root = sender();
1028
1029 QMutableListIterator<KDeclarative::QmlObjectSharedEngine *> it(m_alternativesObjects);
1030
1031 while (it.hasNext()) {
1032 KDeclarative::QmlObjectSharedEngine *obj = it.next();
1033
1034 if (obj->rootObject() == root) {
1035 it.remove();
1036 obj->deleteLater();
1037 }
1038 }
1039 }
1040
containmentsIds()1041 QStringList Corona::containmentsIds()
1042 {
1043 QStringList ids;
1044
1045 for(const auto containment : containments()) {
1046 ids << QString::number(containment->id());
1047 }
1048
1049 return ids;
1050 }
1051
appletsIds()1052 QStringList Corona::appletsIds()
1053 {
1054 QStringList ids;
1055
1056 for(const auto containment : containments()) {
1057 auto applets = containment->config().group("Applets");
1058 ids << applets.groupList();
1059 }
1060
1061 return ids;
1062 }
1063
1064 //! Activate launcher menu through dbus interface
activateLauncherMenu()1065 void Corona::activateLauncherMenu()
1066 {
1067 m_globalShortcuts->activateLauncherMenu();
1068 }
1069
windowColorScheme(QString windowIdAndScheme)1070 void Corona::windowColorScheme(QString windowIdAndScheme)
1071 {
1072 int firstSlash = windowIdAndScheme.indexOf("-");
1073 QString windowIdStr = windowIdAndScheme.mid(0, firstSlash);
1074 QString schemeStr = windowIdAndScheme.mid(firstSlash + 1);
1075
1076 if (KWindowSystem::isPlatformWayland()) {
1077 QTimer::singleShot(200, [this, schemeStr]() {
1078 //! [Wayland Case] - give the time to be informed correctly for the active window id
1079 //! otherwise the active window id may not be the same with the one trigerred
1080 //! the color scheme dbus signal
1081 QString windowIdStr = m_wm->activeWindow().toString();
1082 m_wm->schemesTracker()->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr);
1083 });
1084 } else {
1085 m_wm->schemesTracker()->setColorSchemeForWindow(windowIdStr.toUInt(), schemeStr);
1086 }
1087 }
1088
1089 //! update badge for specific view item
updateDockItemBadge(QString identifier,QString value)1090 void Corona::updateDockItemBadge(QString identifier, QString value)
1091 {
1092 m_globalShortcuts->updateViewItemBadge(identifier, value);
1093 }
1094
setAutostart(const bool & enabled)1095 void Corona::setAutostart(const bool &enabled)
1096 {
1097 m_universalSettings->setAutostart(enabled);
1098 }
1099
switchToLayout(QString layout)1100 void Corona::switchToLayout(QString layout)
1101 {
1102 if ((layout.startsWith("file:/") || layout.startsWith("/")) && layout.endsWith(".layout.latte")) {
1103 importLayoutFile(layout);
1104 } else {
1105 m_layoutsManager->switchToLayout(layout);
1106 }
1107 }
1108
importLayoutFile(const QString & filepath,const QString & suggestedLayoutName)1109 void Corona::importLayoutFile(const QString &filepath, const QString &suggestedLayoutName)
1110 {
1111 bool isFilepathValid = (filepath.startsWith("file:/") || filepath.startsWith("/")) && filepath.endsWith(".layout.latte");
1112
1113 if (!isFilepathValid) {
1114 qDebug() << i18n("The layout cannot be imported from file :: ") << filepath;
1115 return;
1116 }
1117
1118 //! Import and load runtime a layout through dbus interface
1119 //! It can be used from external programs that want to update runtime
1120 //! the Latte shown layout
1121 QString layoutPath = filepath;
1122
1123 //! cleanup layout path
1124 if (layoutPath.startsWith("file:///")) {
1125 layoutPath = layoutPath.remove("file://");
1126 } else if (layoutPath.startsWith("file://")) {
1127 layoutPath = layoutPath.remove("file:/");
1128 }
1129
1130 //! check out layoutpath existence
1131 if (QFileInfo(layoutPath).exists()) {
1132 qDebug() << " Layout is going to be imported and loaded from file :: " << layoutPath << " with suggested name :: " << suggestedLayoutName;
1133
1134 QString importedLayout = m_layoutsManager->importer()->importLayout(layoutPath, suggestedLayoutName);
1135
1136 if (importedLayout.isEmpty()) {
1137 qDebug() << i18n("The layout cannot be imported from file :: ") << layoutPath;
1138 } else {
1139 m_layoutsManager->switchToLayout(importedLayout, MemoryUsage::SingleLayout);
1140 }
1141 } else {
1142 qDebug() << " Layout from missing file can not be imported and loaded :: " << layoutPath;
1143 }
1144 }
1145
showSettingsWindow(int page)1146 void Corona::showSettingsWindow(int page)
1147 {
1148 if (m_inStartup) {
1149 return;
1150 }
1151
1152 Settings::Dialog::ConfigurationPage p = Settings::Dialog::LayoutPage;
1153
1154 if (page >= Settings::Dialog::LayoutPage && page <= Settings::Dialog::PreferencesPage) {
1155 p = static_cast<Settings::Dialog::ConfigurationPage>(page);
1156 }
1157
1158 m_layoutsManager->showLatteSettingsDialog(p);
1159 }
1160
contextMenuData(const uint & containmentId)1161 QStringList Corona::contextMenuData(const uint &containmentId)
1162 {
1163 QStringList data;
1164 Types::ViewType viewType{Types::DockView};
1165 auto view = m_layoutsManager->synchronizer()->viewForContainment(containmentId);
1166
1167 if (view) {
1168 viewType = view->type();
1169 }
1170
1171 data << QString::number((int)m_layoutsManager->memoryUsage()); // Memory Usage
1172 data << m_layoutsManager->centralLayoutsNames().join(";;"); // All Active layouts
1173 data << m_layoutsManager->synchronizer()->currentLayoutsNames().join(";;"); // All Current layouts
1174 data << m_universalSettings->contextMenuActionsAlwaysShown().join(";;");
1175
1176 QStringList layoutsmenu;
1177
1178 for(const auto &layoutName : m_layoutsManager->synchronizer()->menuLayouts()) {
1179 if (m_layoutsManager->synchronizer()->centralLayout(layoutName)
1180 || m_layoutsManager->memoryUsage() == Latte::MemoryUsage::SingleLayout) {
1181 QStringList layoutdata;
1182 Data::LayoutIcon layouticon = m_layoutsManager->iconForLayout(layoutName);
1183 layoutdata << layoutName;
1184 layoutdata << QString::number(layouticon.isBackgroundFile);
1185 layoutdata << layouticon.name;
1186 layoutsmenu << layoutdata.join("**");
1187 }
1188 }
1189
1190 data << layoutsmenu.join(";;");
1191 data << QString::number((int)viewType); //Selected View type
1192 data << (view ? view->layout()->name() : QString()); //Selected View layout*/
1193
1194 return data;
1195 }
1196
viewTemplatesData()1197 QStringList Corona::viewTemplatesData()
1198 {
1199 QStringList data;
1200
1201 Latte::Data::GenericTable<Data::Generic> viewtemplates = m_templatesManager->viewTemplates();
1202
1203 for(int i=0; i<viewtemplates.rowCount(); ++i) {
1204 data << viewtemplates[i].name;
1205 data << viewtemplates[i].id;
1206 }
1207
1208 return data;
1209 }
1210
addView(const uint & containmentId,const QString & templateId)1211 void Corona::addView(const uint &containmentId, const QString &templateId)
1212 {
1213 if (containmentId <= 0) {
1214 auto currentlayouts = m_layoutsManager->currentLayouts();
1215 if (currentlayouts.count() > 0) {
1216 currentlayouts[0]->newView(templateId);
1217 }
1218 } else {
1219 auto view = m_layoutsManager->synchronizer()->viewForContainment((int)containmentId);
1220 if (view) {
1221 view->newView(templateId);
1222 }
1223 }
1224 }
1225
duplicateView(const uint & containmentId)1226 void Corona::duplicateView(const uint &containmentId)
1227 {
1228 auto view = m_layoutsManager->synchronizer()->viewForContainment((int)containmentId);
1229 if (view) {
1230 view->duplicateView();
1231 }
1232 }
1233
exportViewTemplate(const uint & containmentId)1234 void Corona::exportViewTemplate(const uint &containmentId)
1235 {
1236 auto view = m_layoutsManager->synchronizer()->viewForContainment((int)containmentId);
1237 if (view) {
1238 view->exportTemplate();
1239 }
1240 }
1241
moveViewToLayout(const uint & containmentId,const QString & layoutName)1242 void Corona::moveViewToLayout(const uint &containmentId, const QString &layoutName)
1243 {
1244 auto view = m_layoutsManager->synchronizer()->viewForContainment((int)containmentId);
1245 if (view && !layoutName.isEmpty() && view->layout()->name() != layoutName) {
1246 view->positioner()->setNextLocation(layoutName, "", Plasma::Types::Floating, Latte::Types::NoneAlignment);
1247 }
1248 }
1249
removeView(const uint & containmentId)1250 void Corona::removeView(const uint &containmentId)
1251 {
1252 auto view = m_layoutsManager->synchronizer()->viewForContainment((int)containmentId);
1253 if (view) {
1254 view->removeView();
1255 }
1256 }
1257
setBackgroundFromBroadcast(QString activity,QString screenName,QString filename)1258 void Corona::setBackgroundFromBroadcast(QString activity, QString screenName, QString filename)
1259 {
1260 if (filename.startsWith("file://")) {
1261 filename = filename.remove(0,7);
1262 }
1263
1264 PlasmaExtended::BackgroundCache::self()->setBackgroundFromBroadcast(activity, screenName, filename);
1265 }
1266
setBroadcastedBackgroundsEnabled(QString activity,QString screenName,bool enabled)1267 void Corona::setBroadcastedBackgroundsEnabled(QString activity, QString screenName, bool enabled)
1268 {
1269 PlasmaExtended::BackgroundCache::self()->setBroadcastedBackgroundsEnabled(activity, screenName, enabled);
1270 }
1271
toggleHiddenState(QString layoutName,QString viewName,QString screenName,int screenEdge)1272 void Corona::toggleHiddenState(QString layoutName, QString viewName, QString screenName, int screenEdge)
1273 {
1274 if (layoutName.isEmpty()) {
1275 for(auto layout : m_layoutsManager->currentLayouts()) {
1276 layout->toggleHiddenState(viewName, screenName, (Plasma::Types::Location)screenEdge);
1277 }
1278 } else {
1279 Layout::GenericLayout *gLayout = layout(layoutName);
1280
1281 if (gLayout) {
1282 gLayout->toggleHiddenState(viewName, screenName, (Plasma::Types::Location)screenEdge);
1283 }
1284 }
1285 }
1286
importFullConfiguration(const QString & file)1287 void Corona::importFullConfiguration(const QString &file)
1288 {
1289 m_importFullConfigurationFile = file;
1290 quitApplication();
1291 }
1292
qmlRegisterTypes() const1293 inline void Corona::qmlRegisterTypes() const
1294 {
1295 qmlRegisterUncreatableMetaObject(Latte::Settings::staticMetaObject,
1296 "org.kde.latte.private.app", // import statement
1297 0, 1, // major and minor version of the import
1298 "Settings", // name in QML
1299 "Error: only enums of latte app settings");
1300
1301 qmlRegisterType<Latte::BackgroundTracker>("org.kde.latte.private.app", 0, 1, "BackgroundTracker");
1302 qmlRegisterType<Latte::Interfaces>("org.kde.latte.private.app", 0, 1, "Interfaces");
1303
1304
1305 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
1306 qmlRegisterType<QScreen>();
1307 qmlRegisterType<Latte::View>();
1308 qmlRegisterType<Latte::ViewPart::WindowsTracker>();
1309 qmlRegisterType<Latte::ViewPart::TrackerPart::CurrentScreenTracker>();
1310 qmlRegisterType<Latte::ViewPart::TrackerPart::AllScreensTracker>();
1311 qmlRegisterType<Latte::WindowSystem::SchemeColors>();
1312 qmlRegisterType<Latte::WindowSystem::Tracker::LastActiveWindow>();
1313 qmlRegisterType<Latte::Types>();
1314 #else
1315 qmlRegisterAnonymousType<QScreen>("latte-dock", 1);
1316 qmlRegisterAnonymousType<Latte::View>("latte-dock", 1);
1317 qmlRegisterAnonymousType<Latte::ViewPart::WindowsTracker>("latte-dock", 1);
1318 qmlRegisterAnonymousType<Latte::ViewPart::TrackerPart::CurrentScreenTracker>("latte-dock", 1);
1319 qmlRegisterAnonymousType<Latte::ViewPart::TrackerPart::AllScreensTracker>("latte-dock", 1);
1320 qmlRegisterAnonymousType<Latte::WindowSystem::SchemeColors>("latte-dock", 1);
1321 qmlRegisterAnonymousType<Latte::WindowSystem::Tracker::LastActiveWindow>("latte-dock", 1);
1322 qmlRegisterAnonymousType<Latte::Types>("latte-dock", 1);
1323 #endif
1324 }
1325
1326 }
1327