1 /*
2     SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org>
3     SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "view.h"
9 
10 // local
11 #include "contextmenu.h"
12 #include "effects.h"
13 #include "positioner.h"
14 #include "visibilitymanager.h"
15 #include "settings/primaryconfigview.h"
16 #include "settings/secondaryconfigview.h"
17 #include "settings/viewsettingsfactory.h"
18 #include "settings/widgetexplorerview.h"
19 #include "../apptypes.h"
20 #include "../lattecorona.h"
21 #include "../data/layoutdata.h"
22 #include "../data/viewstable.h"
23 #include "../declarativeimports/interfaces.h"
24 #include "../indicator/factory.h"
25 #include "../layout/genericlayout.h"
26 #include "../layouts/manager.h"
27 #include "../layouts/storage.h"
28 #include "../plasma/extended/theme.h"
29 #include "../screenpool.h"
30 #include "../settings/universalsettings.h"
31 #include "../settings/exporttemplatedialog/exporttemplatedialog.h"
32 #include "../shortcuts/globalshortcuts.h"
33 #include "../shortcuts/shortcutstracker.h"
34 
35 // Qt
36 #include <QAction>
37 #include <QDragEnterEvent>
38 #include <QDragMoveEvent>
39 #include <QDropEvent>
40 #include <QMouseEvent>
41 #include <QQmlContext>
42 #include <QQmlEngine>
43 #include <QQmlProperty>
44 #include <QQuickItem>
45 #include <QMenu>
46 
47 // KDe
48 #include <KActionCollection>
49 #include <KActivities/Consumer>
50 #include <KWayland/Client/plasmashell.h>
51 #include <KWayland/Client/surface.h>
52 #include <KWindowSystem>
53 
54 // Plasma
55 #include <Plasma/Containment>
56 #include <Plasma/ContainmentActions>
57 #include <PlasmaQuick/AppletQuickItem>
58 
59 #define BLOCKHIDINGDRAGTYPE "View::ContainsDrag()"
60 #define BLOCKHIDINGNEEDSATTENTIONTYPE "View::Containment::NeedsAttentionState()"
61 #define BLOCKHIDINGREQUESTSINPUTTYPE "View::Containment::RequestsInputState()"
62 
63 namespace Latte {
64 
65 //! both alwaysVisible and byPassWMX11 are passed through corona because
66 //! during the view window creation containment hasn't been set, but these variables
67 //! are needed in order for window flags to be set correctly
View(Plasma::Corona * corona,QScreen * targetScreen,bool byPassX11WM)68 View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassX11WM)
69     : PlasmaQuick::ContainmentView(corona),
70       m_contextMenu(new ViewPart::ContextMenu(this)),
71       m_effects(new ViewPart::Effects(this)),
72       m_interface(new ViewPart::ContainmentInterface(this)),
73       m_parabolic(new ViewPart::Parabolic(this)),
74       m_sink(new ViewPart::EventsSink(this))
75 {
76     //this is disabled because under wayland breaks Views positioning
77     //setVisible(false);
78 
79     //! needs to be created after Effects because it catches some of its signals
80     //! and avoid a crash from View::winId() at the same time
81     m_positioner = new ViewPart::Positioner(this);
82 
83     // setTitle(corona->kPackage().metadata().name());
84     setIcon(qGuiApp->windowIcon());
85     setResizeMode(QuickViewSharedEngine::SizeRootObjectToView);
86     setColor(QColor(Qt::transparent));
87     setClearBeforeRendering(true);
88 
89     const auto flags = Qt::FramelessWindowHint
90             | Qt::NoDropShadowWindowHint
91             | Qt::WindowDoesNotAcceptFocus;
92 
93     if (byPassX11WM) {
94         setFlags(flags | Qt::BypassWindowManagerHint);
95         //! needs to be set early enough
96         m_byPassWM = byPassX11WM;
97     } else {
98         setFlags(flags);
99     }
100 
101     if (targetScreen)
102         m_positioner->setScreenToFollow(targetScreen);
103     else
104         m_positioner->setScreenToFollow(qGuiApp->primaryScreen());
105 
106     m_releaseGrabTimer.setInterval(400);
107     m_releaseGrabTimer.setSingleShot(true);
108     connect(&m_releaseGrabTimer, &QTimer::timeout, this, &View::releaseGrab);
109 
110     connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::updateTransientWindowsTracking);
111     connect(m_interface, &ViewPart::ContainmentInterface::hasExpandedAppletChanged, this, &View::updateTransientWindowsTracking);
112 
113     connect(this, &View::containmentChanged
114             , this, [ &, byPassX11WM]() {
115         qDebug() << "dock view c++ containment changed 1...";
116 
117         if (!this->containment())
118             return;
119 
120         qDebug() << "dock view c++ containment changed 2...";
121 
122         setTitle(validTitle());
123 
124         //! First load default values from file
125         restoreConfig();
126 
127         //! Afterwards override that values in case during creation something different is needed
128         setByPassWM(byPassX11WM);
129 
130         //! Check the screen assigned to this dock
131         reconsiderScreen();
132 
133         //! needs to be created before visibility creation because visibility uses it
134         if (!m_windowsTracker) {
135             m_windowsTracker = new ViewPart::WindowsTracker(this);
136             emit windowsTrackerChanged();
137         }
138 
139         if (!m_visibility) {
140             m_visibility = new ViewPart::VisibilityManager(this);
141 
142             connect(m_visibility, &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() {
143                 if (m_visibility->isHidden()) {
144                     m_interface->deactivateApplets();
145                 }
146             });
147 
148             connect(m_visibility, &ViewPart::VisibilityManager::containsMouseChanged,
149                     this, &View::updateTransientWindowsTracking);
150 
151             //! Deprecated because with Plasma 5.19.3 the issue does not appear.
152             //! The issue was that when FrameExtents where zero strange behaviors were
153             //! occuring from KWin, e.g. the panels were moving outside of screen and
154             //! panel external shadows were positioned out of place.
155             /*connect(m_visibility, &ViewPart::VisibilityManager::frameExtentsCleared, this, [&]() {
156                 if (behaveAsPlasmaPanel()) {
157                     //! recreate view because otherwise compositor frame extents implementation
158                     //! is triggering a crazy behavior of moving/hiding the view and freezing Latte
159                     //! in some cases.
160                     //reloadSource();
161                 }
162             });*/
163 
164             emit visibilityChanged();
165         }
166 
167         if (!m_indicator) {
168             m_indicator = new ViewPart::Indicator(this);
169             emit indicatorChanged();
170         }
171 
172         if (m_positioner) {
173             //! immediateSyncGeometry helps avoiding binding loops from containment qml side
174             m_positioner->immediateSyncGeometry();
175         }
176 
177         connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus)));
178         connect(this->containment(), &Plasma::Containment::showAddWidgetsInterface, this, &View::showWidgetExplorer);
179         connect(this->containment(), &Plasma::Containment::userConfiguringChanged, this, [&]() {
180             emit inEditModeChanged();
181         });
182 
183         if (m_corona->viewSettingsFactory()->hasOrphanSettings()
184                 && m_corona->viewSettingsFactory()->hasVisibleSettings()
185                 && m_corona->viewSettingsFactory()->lastContainment() == containment()) {
186             //! used mostly from view recreations in order to inform config windows that view has been updated
187             m_primaryConfigView = m_corona->viewSettingsFactory()->primaryConfigView();
188             m_primaryConfigView->setParentView(this, true);
189         }
190 
191         emit containmentActionsChanged();
192     }, Qt::DirectConnection);
193 
194     m_corona = qobject_cast<Latte::Corona *>(this->corona());
195 
196     if (m_corona) {
197         connect(m_corona, &Latte::Corona::viewLocationChanged, this, &View::dockLocationChanged);
198     }
199 }
200 
~View()201 View::~View()
202 {
203     m_inDelete = true;
204 
205     //! clear Layout connections
206     m_visibleHackTimer1.stop();
207     m_visibleHackTimer2.stop();
208     for (auto &c : connectionsLayout) {
209         disconnect(c);
210     }
211 
212     //! unload indicators
213     if (m_indicator) {
214         m_indicator->unloadIndicators();
215     }
216 
217     disconnectSensitiveSignals();
218     disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus)));
219 
220     qDebug() << "dock view deleting...";
221 
222     //! this disconnect does not free up connections correctly when
223     //! latteView is deleted. A crash for this example is the following:
224     //! switch to Alternative Session and disable compositing,
225     //! the signal creating the crash was probably from deleted
226     //! windows.
227     //! this->disconnect();
228 
229     if (m_primaryConfigView && m_corona->inQuit()) {
230         //! delete only when application is quitting
231         delete m_primaryConfigView;
232     }
233 
234     if (m_appletConfigView) {
235         delete m_appletConfigView;
236     }
237 
238     if (m_contextMenu) {
239         delete m_contextMenu;
240     }
241 
242     //needs to be deleted before Effects because it catches some of its signals
243     if (m_positioner) {
244         delete m_positioner;
245     }
246 
247     if (m_effects) {
248         delete m_effects;
249     }
250 
251     if (m_indicator) {
252         delete m_indicator;
253     }
254 
255     if (m_interface) {
256         delete m_interface;
257     }
258 
259     if (m_visibility) {
260         delete m_visibility;
261     }
262 
263     if (m_windowsTracker) {
264         delete m_windowsTracker;
265     }
266 }
267 
init(Plasma::Containment * plasma_containment)268 void View::init(Plasma::Containment *plasma_containment)
269 {
270     connect(this, &QQuickWindow::xChanged, this, &View::geometryChanged);
271     connect(this, &QQuickWindow::yChanged, this, &View::geometryChanged);
272     connect(this, &QQuickWindow::widthChanged, this, &View::geometryChanged);
273     connect(this, &QQuickWindow::heightChanged, this, &View::geometryChanged);
274 
275     connect(this, &QQuickWindow::xChanged, this, &View::xChanged);
276     connect(this, &QQuickWindow::xChanged, this, &View::updateAbsoluteGeometry);
277     connect(this, &QQuickWindow::yChanged, this, &View::yChanged);
278     connect(this, &QQuickWindow::yChanged, this, &View::updateAbsoluteGeometry);
279     connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged);
280     connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsoluteGeometry);
281     connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged);
282     connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsoluteGeometry);
283 
284     connect(this, &View::fontPixelSizeChanged, this, &View::editThicknessChanged);
285     connect(this, &View::maxNormalThicknessChanged, this, &View::editThicknessChanged);
286 
287     connect(this, &View::activitiesChanged, this, &View::applyActivitiesToWindows);
288     connect(m_positioner, &ViewPart::Positioner::winIdChanged, this, &View::applyActivitiesToWindows);
289 
290     connect(this, &View::maxLengthChanged, this, [&]() {
291         if (m_inDelete) {
292             return;
293         }
294 
295         emit availableScreenRectChangedFrom(this);
296         emit availableScreenRegionChangedFrom(this);
297     });
298 
299     connect(this, &View::offsetChanged, this, [&]() {
300         if (m_inDelete ) {
301             return;
302         }
303 
304         emit availableScreenRectChangedFrom(this);
305         emit availableScreenRegionChangedFrom(this);
306     });
307 
308     connect(this, &View::localGeometryChanged, this, [&]() {
309         updateAbsoluteGeometry();
310     });
311     connect(this, &View::screenEdgeMarginEnabledChanged, this, [&]() {
312         updateAbsoluteGeometry();
313     });
314 
315     //! used in order to disconnect it when it should NOT be called because it creates crashes
316     connect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom);
317     connect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom);
318     connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot);
319     connect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop);
320 
321     connect(this, &View::byPassWMChanged, this, &View::saveConfig);
322     connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig);
323     connect(this, &View::nameChanged, this, &View::saveConfig);
324     connect(this, &View::onPrimaryChanged, this, &View::saveConfig);
325     connect(this, &View::typeChanged, this, &View::saveConfig);
326 
327     connect(this, &View::normalThicknessChanged, this, [&]() {
328         emit availableScreenRectChangedFrom(this);
329     });
330 
331     connect(m_effects, &ViewPart::Effects::innerShadowChanged, this, [&]() {
332         emit availableScreenRectChangedFrom(this);
333     });
334     connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut);
335     connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged);
336     connect(m_positioner, &ViewPart::Positioner::windowSizeChanged, this, [&]() {
337         emit availableScreenRectChangedFrom(this);
338     });
339 
340     connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged);
341 
342     connect(m_interface, &ViewPart::ContainmentInterface::hasExpandedAppletChanged, this, &View::verticalUnityViewHasFocus);
343 
344     //! View sends this signal in order to avoid crashes from ViewPart::Indicator when the view is recreated
345     connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorChanged, this, [&](const QString &indicatorId) {
346         emit indicatorPluginChanged(indicatorId);
347     });
348 
349     connect(this, &View::indicatorPluginChanged, this, [&](const QString &indicatorId) {
350         if (m_indicator && m_indicator->isCustomIndicator() && m_indicator->type() == indicatorId) {
351             reloadSource();
352         }
353     });
354 
355     connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorRemoved, this, &View::indicatorPluginRemoved);
356 
357     //! Assign app interfaces in be accessible through containment graphic item
358     QQuickItem *containmentGraphicItem = qobject_cast<QQuickItem *>(plasma_containment->property("_plasma_graphicObject").value<QObject *>());
359 
360     if (containmentGraphicItem) {
361         containmentGraphicItem->setProperty("_latte_globalShortcuts_object", QVariant::fromValue(m_corona->globalShortcuts()->shortcutsTracker()));
362         containmentGraphicItem->setProperty("_latte_layoutsManager_object", QVariant::fromValue(m_corona->layoutsManager()));
363         containmentGraphicItem->setProperty("_latte_themeExtended_object", QVariant::fromValue(m_corona->themeExtended()));
364         containmentGraphicItem->setProperty("_latte_universalSettings_object", QVariant::fromValue(m_corona->universalSettings()));
365         containmentGraphicItem->setProperty("_latte_view_object", QVariant::fromValue(this));
366 
367         Latte::Interfaces *ifacesGraphicObject = qobject_cast<Latte::Interfaces *>(containmentGraphicItem->property("_latte_view_interfacesobject").value<QObject *>());
368 
369         if (ifacesGraphicObject) {
370             ifacesGraphicObject->updateView();
371             setInterfacesGraphicObj(ifacesGraphicObject);
372         }
373     }
374 
375     setSource(corona()->kPackage().filePath("lattedockui"));
376 
377     //! immediateSyncGeometry helps avoiding binding loops from containment qml side
378     m_positioner->immediateSyncGeometry();
379 
380     qDebug() << "SOURCE:" << source();
381 }
382 
reloadSource()383 void View::reloadSource()
384 {
385     if (m_layout && containment()) {
386         // if (settingsWindowIsShown()) {
387         //     m_configView->deleteLater();
388         // }
389 
390         engine()->clearComponentCache();
391         m_layout->recreateView(containment(), settingsWindowIsShown());
392     }
393 }
394 
inDelete() const395 bool View::inDelete() const
396 {
397     return m_inDelete;
398 }
399 
inReadyState() const400 bool View::inReadyState() const
401 {
402     return (m_layout != nullptr);
403 }
404 
disconnectSensitiveSignals()405 void View::disconnectSensitiveSignals()
406 {
407     m_initLayoutTimer.stop();
408 
409     disconnect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom);
410     disconnect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom);
411     disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot);
412     disconnect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop);
413 
414     setLayout(nullptr);
415 }
416 
availableScreenRectChangedFromSlot(View * origin)417 void View::availableScreenRectChangedFromSlot(View *origin)
418 {
419     if (m_inDelete || origin == this || !origin) {
420         return;
421     }
422 
423     if (formFactor() == Plasma::Types::Vertical
424             && origin->formFactor() == Plasma::Types::Horizontal //! accept only horizontal views
425             && !(origin->location() == Plasma::Types::TopEdge && m_positioner->isStickedOnTopEdge()) //! ignore signals in such case
426             && !(origin->location() == Plasma::Types::BottomEdge && m_positioner->isStickedOnBottomEdge()) //! ignore signals in such case
427             && origin->layout()
428             && m_layout
429             && origin->layout()->lastUsedActivity() == m_layout->lastUsedActivity()) {
430         //! must be in same activity
431         m_positioner->syncGeometry();
432     }
433 }
434 
setupWaylandIntegration()435 void View::setupWaylandIntegration()
436 {
437     if (m_shellSurface)
438         return;
439 
440     if (Latte::Corona *c = qobject_cast<Latte::Corona *>(corona())) {
441         using namespace KWayland::Client;
442         PlasmaShell *interface {c->waylandCoronaInterface()};
443 
444         if (!interface)
445             return;
446 
447         Surface *s{Surface::fromWindow(this)};
448 
449         if (!s)
450             return;
451 
452         m_shellSurface = interface->createSurface(s, this);
453         qDebug() << "WAYLAND dock window surface was created...";
454         if (m_visibility) {
455             m_visibility->initViewFlags();
456         }
457         if (m_positioner) {
458             m_positioner->updateWaylandId();
459         }
460     }
461 }
462 
surface()463 KWayland::Client::PlasmaShellSurface *View::surface()
464 {
465     return m_shellSurface;
466 }
467 
468 //! the main function which decides if this dock is at the
469 //! correct screen
reconsiderScreen()470 void View::reconsiderScreen()
471 {
472     m_positioner->reconsiderScreen();
473 }
474 
duplicateView()475 void View::duplicateView()
476 {
477     QString storedTmpViewFilepath = m_layout->storedView(containment()->id());
478     newView(storedTmpViewFilepath);
479 }
480 
exportTemplate()481 void View::exportTemplate()
482 {
483     Latte::Settings::Dialog::ExportTemplateDialog *exportDlg = new Latte::Settings::Dialog::ExportTemplateDialog(this);
484     exportDlg->show();
485 }
486 
newView(const QString & templateFile)487 void View::newView(const QString &templateFile)
488 {
489     if (templateFile.isEmpty() || !m_layout) {
490         return;
491     }
492 
493     Data::ViewsTable templateviews = Layouts::Storage::self()->views(templateFile);
494 
495     if (templateviews.rowCount() <= 0) {
496         return;
497     }
498 
499     Data::View nextdata = templateviews[0];
500     int scrId = onPrimary() ? m_corona->screenPool()->primaryScreenId() : m_positioner->currentScreenId();
501 
502     QList<Plasma::Types::Location> freeedges = m_layout->freeEdges(scrId);
503 
504     if (!freeedges.contains(nextdata.edge)) {
505         nextdata.edge = (freeedges.count() > 0 ? freeedges[0] : Plasma::Types::BottomEdge);
506     }
507 
508     nextdata.setState(Data::View::OriginFromViewTemplate, templateFile);
509 
510     m_layout->newView(nextdata);
511 }
512 
removeView()513 void View::removeView()
514 {
515     if (m_layout) {
516         m_inDelete = true;
517 
518         QAction *removeAct = action("remove");
519 
520         if (removeAct) {
521             removeAct->trigger();
522         }
523     }
524 }
525 
settingsWindowIsShown()526 bool View::settingsWindowIsShown()
527 {
528     return m_primaryConfigView && (m_primaryConfigView->parentView()==this) && m_primaryConfigView->isVisible();
529 }
530 
showSettingsWindow()531 void View::showSettingsWindow()
532 {
533     if (!settingsWindowIsShown()) {
534         emit m_visibility->mustBeShown();
535         showConfigurationInterface(containment());
536         applyActivitiesToWindows();
537     }
538 }
539 
configView()540 QQuickView *View::configView()
541 {
542     return m_primaryConfigView.data();
543 }
544 
showConfigurationInterface(Plasma::Applet * applet)545 void View::showConfigurationInterface(Plasma::Applet *applet)
546 {
547     if (!applet || !applet->containment())
548         return;
549 
550     Plasma::Containment *c = qobject_cast<Plasma::Containment *>(applet);
551 
552     if (m_primaryConfigView && c && c->isContainment() && c == this->containment()) {
553         if (m_primaryConfigView->isVisible()) {
554             m_primaryConfigView->hideConfigWindow();
555         } else {
556             m_primaryConfigView->showConfigWindow();
557             applyActivitiesToWindows();
558         }
559 
560         return;
561     } else if (m_appletConfigView) {
562         if (m_appletConfigView->applet() == applet) {
563             m_appletConfigView->show();
564 
565             if (KWindowSystem::isPlatformX11()) {
566                 m_appletConfigView->requestActivate();
567             }
568             return;
569         } else {
570             m_appletConfigView->hide();
571         }
572     }
573 
574     bool delayConfigView = false;
575 
576     if (c && containment() && c->isContainment() && c->id() == containment()->id()) {
577         m_primaryConfigView = m_corona->viewSettingsFactory()->primaryConfigView(this);
578         applyActivitiesToWindows();
579     } else {
580         m_appletConfigView = new PlasmaQuick::ConfigView(applet);
581         m_appletConfigView.data()->init();
582         m_appletConfigView->show();
583     }
584 }
585 
showWidgetExplorer(const QPointF & point)586 void View::showWidgetExplorer(const QPointF &point)
587 {
588     auto widgetExplorerView = m_corona->viewSettingsFactory()->widgetExplorerView(this);
589 
590     if (!widgetExplorerView->isVisible()) {
591         widgetExplorerView->showAfter(250);
592     }
593 }
594 
localGeometry() const595 QRect View::localGeometry() const
596 {
597     return m_localGeometry;
598 }
599 
setLocalGeometry(const QRect & geometry)600 void View::setLocalGeometry(const QRect &geometry)
601 {
602     if (m_localGeometry == geometry) {
603         return;
604     }
605 
606     m_localGeometry = geometry;
607     emit localGeometryChanged();
608 }
609 
610 
name() const611 QString View::name() const
612 {
613     return m_name;
614 }
615 
setName(const QString & newname)616 void View::setName(const QString &newname)
617 {
618     if (m_name == newname) {
619         return;
620     }
621 
622     m_name = newname;
623     emit nameChanged();
624 }
625 
validTitle() const626 QString View::validTitle() const
627 {
628     if (!containment()) {
629         return QString();
630     }
631 
632     return QString("#view#" + QString::number(containment()->id()));
633 }
634 
updateAbsoluteGeometry(bool bypassChecks)635 void View::updateAbsoluteGeometry(bool bypassChecks)
636 {
637     //! there was a -1 in height and width here. The reason of this
638     //! if I remember correctly was related to multi-screen but I cant
639     //! remember exactly the reason, something related to right edge in
640     //! multi screen environment. BUT this was breaking the entire AlwaysVisible
641     //! experience with struts. Removing them in order to restore correct
642     //! behavior and keeping this comment in order to check for
643     //! multi-screen breakage
644     QRect absGeometry = m_localGeometry;
645     absGeometry.moveLeft(x() + m_localGeometry.x());
646     absGeometry.moveTop(y() + m_localGeometry.y());
647 
648     if (behaveAsPlasmaPanel()) {
649         int currentScreenEdgeMargin = m_screenEdgeMarginEnabled ? qMax(0, m_screenEdgeMargin) : 0;
650 
651         if (location() == Plasma::Types::BottomEdge) {
652             absGeometry.moveTop(screenGeometry().bottom() - currentScreenEdgeMargin - m_normalThickness);
653         } else if (location() == Plasma::Types::TopEdge) {
654             absGeometry.moveTop(screenGeometry().top() + currentScreenEdgeMargin);
655         } else if (location() == Plasma::Types::LeftEdge) {
656             absGeometry.moveLeft(screenGeometry().left() + currentScreenEdgeMargin);
657         } else if (location() == Plasma::Types::RightEdge) {
658             absGeometry.moveLeft(screenGeometry().right() - currentScreenEdgeMargin - m_normalThickness);
659         }
660     }
661 
662     if (KWindowSystem::isPlatformX11() && devicePixelRatio() != 1.0) {
663         //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
664         auto factor = devicePixelRatio();
665         absGeometry = QRect(qRound(absGeometry.x() * factor),
666                             qRound(absGeometry.y() * factor),
667                             qRound(absGeometry.width() * factor),
668                             qRound(absGeometry.height() * factor));
669     }
670 
671     if (m_absoluteGeometry == absGeometry && !bypassChecks) {
672         return;
673     }
674 
675     if (m_absoluteGeometry != absGeometry) {
676         m_absoluteGeometry = absGeometry;
677         emit absoluteGeometryChanged(m_absoluteGeometry);
678     }
679 
680     if ((m_absoluteGeometry != absGeometry) || bypassChecks) {
681         //! inform others such as neighbour vertical views that new geometries are applied
682         //! main use of BYPASSCKECKS is from Positioner when the view changes screens
683         emit availableScreenRectChangedFrom(this);
684         emit availableScreenRegionChangedFrom(this);
685     }
686 }
687 
statusChanged(Plasma::Types::ItemStatus status)688 void View::statusChanged(Plasma::Types::ItemStatus status)
689 {
690     if (!containment()) {
691         return;
692     }
693 
694     //! Fix for #443236, following setFlags(...) need to be added at all three cases
695     //! but initViewFlags() should be called afterwards because setFlags(...) breaks
696     //! the Dock window default behavior under x11
697     if (status == Plasma::Types::NeedsAttentionStatus || status == Plasma::Types::RequiresAttentionStatus) {
698         m_visibility->addBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE);
699         setFlags(flags() | Qt::WindowDoesNotAcceptFocus);
700         m_visibility->initViewFlags();
701     } else if (status == Plasma::Types::AcceptingInputStatus) {
702         m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE);
703         setFlags(flags() & ~Qt::WindowDoesNotAcceptFocus);
704         m_visibility->initViewFlags();
705         KWindowSystem::forceActiveWindow(winId());
706     } else {
707         updateTransientWindowsTracking();
708         m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE);
709         setFlags(flags() | Qt::WindowDoesNotAcceptFocus);
710         m_visibility->initViewFlags();
711     }
712 }
713 
addTransientWindow(QWindow * window)714 void View::addTransientWindow(QWindow *window)
715 {
716     if (!m_transientWindows.contains(window) && !window->flags().testFlag(Qt::ToolTip) && !window->title().startsWith("#debugwindow#")) {
717         m_transientWindows.append(window);
718 
719         QString winPtrStr = "0x" + QString::number((qulonglong)window,16);
720         m_visibility->addBlockHidingEvent(winPtrStr);
721 
722         if (m_visibility->hasBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE)) {
723             m_visibility->removeBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE);
724         }
725 
726         connect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow);
727     }
728 }
729 
removeTransientWindow(const bool & visible)730 void View::removeTransientWindow(const bool &visible)
731 {
732     QWindow *window = static_cast<QWindow *>(QObject::sender());
733 
734     if (window && !visible) {
735         QString winPtrStr = "0x" + QString::number((qulonglong)window,16);
736         m_visibility->removeBlockHidingEvent(winPtrStr);
737         disconnect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow);
738         m_transientWindows.removeAll(window);
739 
740         if (m_visibility->hasBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE)) {
741             m_visibility->removeBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE);
742         }
743 
744         updateTransientWindowsTracking();
745     }
746 }
747 
updateTransientWindowsTracking()748 void View::updateTransientWindowsTracking()
749 {
750     for(QWindow *window: qApp->topLevelWindows()) {
751         if (window->transientParent() == this && window->isVisible()){
752             addTransientWindow(window);
753             break;
754         }
755     }
756 }
757 
type() const758 Types::ViewType View::type() const
759 {
760     return m_type;
761 }
762 
setType(Types::ViewType type)763 void View::setType(Types::ViewType type)
764 {
765     if (m_type == type) {
766         return;
767     }
768 
769     m_type = type;
770     emit typeChanged();
771 }
772 
alternativesIsShown() const773 bool View::alternativesIsShown() const
774 {
775     return m_alternativesIsShown;
776 }
777 
setAlternativesIsShown(bool show)778 void View::setAlternativesIsShown(bool show)
779 {
780     if (m_alternativesIsShown == show) {
781         return;
782     }
783 
784     m_alternativesIsShown = show;
785 
786     emit alternativesIsShownChanged();
787 }
788 
containsDrag() const789 bool View::containsDrag() const
790 {
791     return m_containsDrag;
792 }
793 
setContainsDrag(bool contains)794 void View::setContainsDrag(bool contains)
795 {
796     if (m_containsDrag == contains) {
797         return;
798     }
799 
800     m_containsDrag = contains;
801 
802 
803     if (m_containsDrag) {
804         m_visibility->addBlockHidingEvent(BLOCKHIDINGDRAGTYPE);
805     } else {
806         m_visibility->removeBlockHidingEvent(BLOCKHIDINGDRAGTYPE);
807     }
808 
809     emit containsDragChanged();
810 }
811 
containsMouse() const812 bool View::containsMouse() const
813 {
814     return m_containsMouse;
815 }
816 
contextMenuIsShown() const817 bool View::contextMenuIsShown() const
818 {
819     if (!m_contextMenu) {
820         return false;
821     }
822 
823     return m_contextMenu->menu();
824 }
825 
normalThickness() const826 int View::normalThickness() const
827 {
828     return m_normalThickness;
829 }
830 
setNormalThickness(int thickness)831 void View::setNormalThickness(int thickness)
832 {
833     if (m_normalThickness == thickness) {
834         return;
835     }
836 
837     m_normalThickness = thickness;
838     emit normalThicknessChanged();
839 }
840 
maxNormalThickness() const841 int View::maxNormalThickness() const
842 {
843     return m_maxNormalThickness;
844 }
845 
setMaxNormalThickness(int thickness)846 void View::setMaxNormalThickness(int thickness)
847 {
848     if (m_maxNormalThickness == thickness) {
849         return;
850     }
851 
852     m_maxNormalThickness = thickness;
853     emit maxNormalThicknessChanged();
854 }
855 
headThicknessGap() const856 int View::headThicknessGap() const
857 {
858     return m_headThicknessGap;
859 }
860 
setHeadThicknessGap(int thickness)861 void View::setHeadThicknessGap(int thickness)
862 {
863     if (m_headThicknessGap == thickness) {
864         return;
865     }
866 
867     m_headThicknessGap = thickness;
868     emit headThicknessGapChanged();
869 }
870 
byPassWM() const871 bool View::byPassWM() const
872 {
873     return m_byPassWM;
874 }
875 
setByPassWM(bool bypass)876 void View::setByPassWM(bool bypass)
877 {
878     if (m_byPassWM == bypass) {
879         return;
880     }
881 
882     m_byPassWM = bypass;
883     emit byPassWMChanged();
884 }
885 
behaveAsPlasmaPanel() const886 bool View::behaveAsPlasmaPanel() const
887 {
888     return m_behaveAsPlasmaPanel;
889 }
890 
setBehaveAsPlasmaPanel(bool behavior)891 void View::setBehaveAsPlasmaPanel(bool behavior)
892 {
893     if (m_behaveAsPlasmaPanel == behavior) {
894         return;
895     }
896 
897     m_behaveAsPlasmaPanel = behavior;
898 
899     emit behaveAsPlasmaPanelChanged();
900 }
901 
inEditMode() const902 bool View::inEditMode() const
903 {
904     return containment() && containment()->isUserConfiguring();
905 }
906 
isFloatingPanel() const907 bool View::isFloatingPanel() const
908 {
909     return m_behaveAsPlasmaPanel && m_screenEdgeMarginEnabled && (m_screenEdgeMargin>0);
910 }
911 
isPreferredForShortcuts() const912 bool View::isPreferredForShortcuts() const
913 {
914     return m_isPreferredForShortcuts;
915 }
916 
setIsPreferredForShortcuts(bool preferred)917 void View::setIsPreferredForShortcuts(bool preferred)
918 {
919     if (m_isPreferredForShortcuts == preferred) {
920         return;
921     }
922 
923     m_isPreferredForShortcuts = preferred;
924 
925     emit isPreferredForShortcutsChanged();
926 
927     if (m_isPreferredForShortcuts && m_layout) {
928         emit m_layout->preferredViewForShortcutsChanged(this);
929     }
930 }
931 
inSettingsAdvancedMode() const932 bool View::inSettingsAdvancedMode() const
933 {
934     return m_primaryConfigView && m_corona->universalSettings()->inAdvancedModeForEditSettings();
935 }
936 
isTouchingBottomViewAndIsBusy() const937 bool View::isTouchingBottomViewAndIsBusy() const
938 {
939     return m_isTouchingBottomViewAndIsBusy;
940 }
941 
setIsTouchingBottomViewAndIsBusy(bool touchAndBusy)942 void View::setIsTouchingBottomViewAndIsBusy(bool touchAndBusy)
943 {
944     if (m_isTouchingBottomViewAndIsBusy == touchAndBusy) {
945         return;
946     }
947 
948     m_isTouchingBottomViewAndIsBusy = touchAndBusy;
949 
950     emit isTouchingBottomViewAndIsBusyChanged();
951 }
952 
isTouchingTopViewAndIsBusy() const953 bool View::isTouchingTopViewAndIsBusy() const
954 {
955     return m_isTouchingTopViewAndIsBusy;
956 }
957 
setIsTouchingTopViewAndIsBusy(bool touchAndBusy)958 void View::setIsTouchingTopViewAndIsBusy(bool touchAndBusy)
959 {
960     if (m_isTouchingTopViewAndIsBusy == touchAndBusy) {
961         return;
962     }
963 
964     m_isTouchingTopViewAndIsBusy = touchAndBusy;
965     emit isTouchingTopViewAndIsBusyChanged();
966 }
967 
preferredViewForShortcutsChangedSlot(Latte::View * view)968 void View::preferredViewForShortcutsChangedSlot(Latte::View *view)
969 {
970     if (view != this) {
971         setIsPreferredForShortcuts(false);
972     }
973 }
974 
onPrimary() const975 bool View::onPrimary() const
976 {
977     return m_onPrimary;
978 }
979 
setOnPrimary(bool flag)980 void View::setOnPrimary(bool flag)
981 {
982     if (m_onPrimary == flag) {
983         return;
984     }
985 
986     m_onPrimary = flag;
987     emit onPrimaryChanged();
988 }
989 
maxLength() const990 float View::maxLength() const
991 {
992     return m_maxLength;
993 }
994 
setMaxLength(float length)995 void View::setMaxLength(float length)
996 {
997     if (m_maxLength == length) {
998         return;
999     }
1000 
1001     m_maxLength = length;
1002     emit maxLengthChanged();
1003 }
1004 
editThickness() const1005 int View::editThickness() const
1006 {
1007     int smallspacing = 4;
1008     int ruler_height{m_fontPixelSize};
1009     int header_height{m_fontPixelSize + 2*smallspacing};
1010 
1011     int edgeThickness = behaveAsPlasmaPanel() && screenEdgeMarginEnabled() ? m_screenEdgeMargin : 0;
1012 
1013     return edgeThickness + m_maxNormalThickness + ruler_height + header_height + 6*smallspacing;
1014 }
1015 
maxThickness() const1016 int View::maxThickness() const
1017 {
1018     return m_maxThickness;
1019 }
1020 
setMaxThickness(int thickness)1021 void View::setMaxThickness(int thickness)
1022 {
1023     if (m_maxThickness == thickness)
1024         return;
1025 
1026     m_maxThickness = thickness;
1027     emit maxThicknessChanged();
1028 }
1029 
alignment() const1030 int View::alignment() const
1031 {
1032     return m_alignment;
1033 }
1034 
setAlignment(int alignment)1035 void View::setAlignment(int alignment)
1036 {
1037     Types::Alignment align = static_cast<Types::Alignment>(alignment);
1038 
1039     if (m_alignment == alignment) {
1040         return;
1041     }
1042 
1043     m_alignment = align;
1044     emit alignmentChanged();
1045 }
1046 
absoluteGeometry() const1047 QRect View::absoluteGeometry() const
1048 {
1049     return m_absoluteGeometry;
1050 }
1051 
screenGeometry() const1052 QRect View::screenGeometry() const
1053 {
1054     if (this->screen()) {
1055         QRect geom = this->screen()->geometry();
1056         return geom;
1057     }
1058 
1059     return QRect();
1060 }
1061 
offset() const1062 float View::offset() const
1063 {
1064     return m_offset;
1065 }
1066 
setOffset(float offset)1067 void View::setOffset(float offset)
1068 {
1069     if (m_offset == offset) {
1070         return;
1071     }
1072 
1073     m_offset = offset;
1074     emit offsetChanged();
1075 }
1076 
screenEdgeMarginEnabled() const1077 bool View::screenEdgeMarginEnabled() const
1078 {
1079     return m_screenEdgeMarginEnabled;
1080 }
1081 
setScreenEdgeMarginEnabled(bool enabled)1082 void View::setScreenEdgeMarginEnabled(bool enabled)
1083 {
1084     if (m_screenEdgeMarginEnabled == enabled) {
1085         return;
1086     }
1087 
1088     m_screenEdgeMarginEnabled = enabled;
1089     emit screenEdgeMarginEnabledChanged();
1090 }
1091 
screenEdgeMargin() const1092 int View::screenEdgeMargin() const
1093 {
1094     return m_screenEdgeMargin;
1095 }
1096 
setScreenEdgeMargin(int margin)1097 void View::setScreenEdgeMargin(int margin)
1098 {
1099     if (m_screenEdgeMargin == margin) {
1100         return;
1101     }
1102 
1103 
1104 
1105     m_screenEdgeMargin = margin;
1106     emit screenEdgeMarginChanged();
1107 }
1108 
fontPixelSize() const1109 int View::fontPixelSize() const
1110 {
1111     return m_fontPixelSize;
1112 }
1113 
setFontPixelSize(int size)1114 void View::setFontPixelSize(int size)
1115 {
1116     if (m_fontPixelSize == size) {
1117         return;
1118     }
1119 
1120     m_fontPixelSize = size;
1121 
1122     emit fontPixelSizeChanged();
1123 }
1124 
isOnAllActivities() const1125 bool View::isOnAllActivities() const
1126 {
1127     return m_activities.isEmpty() || m_activities[0] == Data::Layout::ALLACTIVITIESID;
1128 }
1129 
isOnActivity(const QString & activity) const1130 bool View::isOnActivity(const QString &activity) const
1131 {
1132     return isOnAllActivities() || m_activities.contains(activity);
1133 }
1134 
activities() const1135 QStringList View::activities() const
1136 {
1137     QStringList running;
1138 
1139     QStringList runningAll = m_corona->activitiesConsumer()->runningActivities();
1140 
1141     for(int i=0; i<m_activities.count(); ++i) {
1142         if (runningAll.contains(m_activities[i])) {
1143             running << m_activities[i];
1144         }
1145     }
1146 
1147     return running;
1148 }
1149 
setActivities(const QStringList & ids)1150 void View::setActivities(const QStringList &ids)
1151 {
1152     if (m_activities == ids) {
1153         return;
1154     }
1155 
1156     m_activities = ids;
1157     emit activitiesChanged();
1158 }
1159 
applyActivitiesToWindows()1160 void View::applyActivitiesToWindows()
1161 {
1162     if (m_visibility && m_positioner && m_layout) {
1163         QStringList runningActivities = activities();
1164 
1165         m_positioner->setWindowOnActivities(m_positioner->trackedWindowId(), runningActivities);
1166 
1167         //! config windows
1168         if (m_primaryConfigView) {
1169             m_primaryConfigView->setOnActivities(runningActivities);
1170         }
1171 
1172         if (m_appletConfigView) {
1173             Latte::WindowSystem::WindowId appletconfigviewid;
1174 
1175             if (KWindowSystem::isPlatformX11()) {
1176                 appletconfigviewid = m_appletConfigView->winId();
1177             } else {
1178                 appletconfigviewid = m_corona->wm()->winIdFor("latte-dock", m_appletConfigView->title());
1179             }
1180 
1181             m_positioner->setWindowOnActivities(appletconfigviewid, runningActivities);
1182         }
1183 
1184         //! hidden windows
1185         if (m_visibility->supportsKWinEdges()) {
1186             m_visibility->applyActivitiesToHiddenWindows(runningActivities);
1187         }
1188     }
1189 }
1190 
showHiddenViewFromActivityStopping()1191 void View::showHiddenViewFromActivityStopping()
1192 {
1193     if (m_layout && m_visibility && !inDelete() && !isVisible() && !m_visibility->isHidden()) {
1194         show();
1195 
1196         if (m_effects) {
1197             m_effects->updateEnabledBorders();
1198         }
1199 
1200         //qDebug() << "View:: Enforce reshow from timer 1...";
1201         emit forcedShown();
1202     } else if (m_layout && isVisible()) {
1203         m_inDelete = false;
1204         //qDebug() << "View:: No needed reshow from timer 1...";
1205     }
1206 }
1207 
layout() const1208 Layout::GenericLayout *View::layout() const
1209 {
1210     return m_layout;
1211 }
1212 
setLayout(Layout::GenericLayout * layout)1213 void View::setLayout(Layout::GenericLayout *layout)
1214 {
1215     if (m_layout == layout) {
1216         return;
1217     }
1218 
1219     // clear mode
1220     for (auto &c : connectionsLayout) {
1221         disconnect(c);
1222     }
1223 
1224     m_layout = layout;
1225 
1226     if (m_layout) {
1227         connectionsLayout << connect(containment(), &Plasma::Applet::destroyedChanged, m_layout, &Layout::GenericLayout::destroyedChanged);
1228         connectionsLayout << connect(containment(), &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged);
1229         connectionsLayout << connect(containment(), &Plasma::Containment::appletAlternativesRequested, m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection);
1230 
1231         if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1232             connectionsLayout << connect(containment(), &Plasma::Containment::appletCreated, m_layout, &Layout::GenericLayout::appletCreated);
1233         }
1234 
1235         connectionsLayout << connect(m_positioner, &Latte::ViewPart::Positioner::edgeChanged, m_layout, &Layout::GenericLayout::viewEdgeChanged);
1236         connectionsLayout << connect(m_layout, &Layout::GenericLayout::popUpMarginChanged, m_effects, &Latte::ViewPart::Effects::popUpMarginChanged);
1237 
1238         //! Sometimes the activity isnt completely ready, by adding a delay
1239         //! we try to catch up
1240         m_initLayoutTimer.setInterval(100);
1241         m_initLayoutTimer.setSingleShot(true);
1242         connectionsLayout << connect(&m_initLayoutTimer, &QTimer::timeout, this, [&]() {
1243             if (m_layout && m_visibility) {
1244                 setActivities(m_layout->appliedActivities());
1245                 qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_layout->name() << " - activities: " << m_activities;
1246             }
1247         });
1248         m_initLayoutTimer.start();
1249 
1250         connectionsLayout << connect(m_layout, &Layout::GenericLayout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot);
1251 
1252         Latte::Corona *latteCorona = qobject_cast<Latte::Corona *>(this->corona());
1253 
1254         connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() {
1255             if (m_layout && m_visibility) {
1256                 setActivities(m_layout->appliedActivities());
1257                 //! update activities in case KWin did its magic and assigned windows to faulty activities
1258                 applyActivitiesToWindows();
1259                 showHiddenViewFromActivityStopping();
1260                 qDebug() << "DOCK VIEW FROM LAYOUT (currentActivityChanged) ::: " << m_layout->name() << " - activities: " << m_activities;
1261             }
1262         });
1263 
1264         if (latteCorona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1265             connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() {
1266                 if (m_layout && m_visibility) {
1267                     setActivities(m_layout->appliedActivities());
1268                     qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_layout->name()
1269                              << " - activities: " << m_activities;
1270                 }
1271             });
1272 
1273             connectionsLayout << connect(m_layout, &Layout::GenericLayout::activitiesChanged, this, [&]() {
1274                 if (m_layout) {
1275                     setActivities(m_layout->appliedActivities());
1276                 }
1277             });
1278 
1279             connectionsLayout << connect(latteCorona->layoutsManager()->synchronizer(), &Layouts::Synchronizer::layoutsChanged, this, [&]() {
1280                 if (m_layout) {
1281                     setActivities(m_layout->appliedActivities());
1282                 }
1283             });
1284 
1285             //! BEGIN OF KWIN HACK
1286             //! IMPORTANT ::: Fixing KWin Faulty Behavior that KWin hides ALL Views when an Activity stops
1287             //! with no reason!!
1288 
1289             m_visibleHackTimer1.setInterval(400);
1290             m_visibleHackTimer2.setInterval(2500);
1291             m_visibleHackTimer1.setSingleShot(true);
1292             m_visibleHackTimer2.setSingleShot(true);
1293 
1294             connectionsLayout << connect(this, &QWindow::visibleChanged, this, [&]() {
1295                 if (m_layout && !inDelete() && !isVisible() && !m_positioner->inLayoutUnloading()) {
1296                     m_visibleHackTimer1.start();
1297                     m_visibleHackTimer2.start();
1298                 }
1299             });
1300 
1301             connectionsLayout << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() {
1302                 applyActivitiesToWindows();
1303                 showHiddenViewFromActivityStopping();
1304                 emit activitiesChanged();
1305             });
1306 
1307             connectionsLayout << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() {
1308                 applyActivitiesToWindows();
1309                 showHiddenViewFromActivityStopping();
1310                 emit activitiesChanged();
1311             });
1312 
1313             //! END OF KWIN HACK
1314         }
1315 
1316         emit layoutChanged();
1317     } else {
1318         m_activities.clear();
1319     }
1320 }
1321 
hideWindowsForSlidingOut()1322 void View::hideWindowsForSlidingOut()
1323 {
1324     if (m_primaryConfigView) {
1325         m_primaryConfigView->hideConfigWindow();
1326     }
1327 }
1328 
1329 //!check if the plasmoid with _name_ exists in the midedata
mimeContainsPlasmoid(QMimeData * mimeData,QString name)1330 bool View::mimeContainsPlasmoid(QMimeData *mimeData, QString name)
1331 {
1332     if (!mimeData) {
1333         return false;
1334     }
1335 
1336     if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) {
1337         QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename"));
1338         const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
1339 
1340         for (const QString &appletName : appletNames) {
1341             if (appletName == name)
1342                 return true;
1343         }
1344     }
1345 
1346     return false;
1347 }
1348 
data() const1349 Latte::Data::View View::data() const
1350 {
1351     Latte::Data::View vdata;
1352     vdata.id = QString::number(containment()->id());
1353     vdata.name = name();
1354     vdata.isActive = true;
1355     vdata.onPrimary = onPrimary();
1356 
1357     vdata.screen = containment()->screen();
1358     if (!Layouts::Storage::isValid(vdata.screen)) {
1359         vdata.screen = containment()->lastScreen();
1360     }
1361 
1362     //!screen edge margin can be more accurate in the config file
1363     vdata.screenEdgeMargin = m_screenEdgeMargin > 0 ? m_screenEdgeMargin : containment()->config().group("General").readEntry("screenEdgeMargin", (int)-1);
1364 
1365     vdata.edge = location();
1366     vdata.maxLength = m_maxLength * 100;
1367     vdata.alignment = m_alignment;
1368     vdata.subcontainments = Layouts::Storage::self()->subcontainments(layout(), containment());
1369 
1370     vdata.setState(Latte::Data::View::IsCreated);
1371     return vdata;
1372 }
1373 
colorizer() const1374 QQuickItem *View::colorizer() const
1375 {
1376     return m_colorizer;
1377 }
1378 
setColorizer(QQuickItem * colorizer)1379 void View::setColorizer(QQuickItem *colorizer)
1380 {
1381     if (m_colorizer == colorizer) {
1382         return;
1383     }
1384 
1385     m_colorizer = colorizer;
1386     emit colorizerChanged();
1387 }
1388 
metrics() const1389 QQuickItem *View::metrics() const
1390 {
1391     return m_metrics;
1392 }
1393 
setMetrics(QQuickItem * metrics)1394 void View::setMetrics(QQuickItem *metrics)
1395 {
1396     if (m_metrics == metrics) {
1397         return;
1398     }
1399 
1400     m_metrics = metrics;
1401     emit metricsChanged();
1402 }
1403 
effects() const1404 ViewPart::Effects *View::effects() const
1405 {
1406     return m_effects;
1407 }
1408 
indicator() const1409 ViewPart::Indicator *View::indicator() const
1410 {
1411     return m_indicator;
1412 }
1413 
contextMenu() const1414 ViewPart::ContextMenu *View::contextMenu() const
1415 {
1416     return m_contextMenu;
1417 }
1418 
extendedInterface() const1419 ViewPart::ContainmentInterface *View::extendedInterface() const
1420 {
1421     return m_interface;
1422 }
1423 
parabolic() const1424 ViewPart::Parabolic *View::parabolic() const
1425 {
1426     return m_parabolic;
1427 }
1428 
positioner() const1429 ViewPart::Positioner *View::positioner() const
1430 {
1431     return m_positioner;
1432 }
1433 
sink() const1434 ViewPart::EventsSink *View::sink() const
1435 {
1436     return m_sink;
1437 }
1438 
visibility() const1439 ViewPart::VisibilityManager *View::visibility() const
1440 {
1441     return m_visibility;
1442 }
1443 
windowsTracker() const1444 ViewPart::WindowsTracker *View::windowsTracker() const
1445 {
1446     return m_windowsTracker;
1447 }
1448 
interfacesGraphicObj() const1449 Latte::Interfaces *View::interfacesGraphicObj() const
1450 {
1451     return m_interfacesGraphicObj;
1452 }
1453 
setInterfacesGraphicObj(Latte::Interfaces * ifaces)1454 void View::setInterfacesGraphicObj(Latte::Interfaces *ifaces)
1455 {
1456     if (m_interfacesGraphicObj == ifaces) {
1457         return;
1458     }
1459 
1460     m_interfacesGraphicObj = ifaces;
1461 
1462     if (containment()) {
1463         QQuickItem *containmentGraphicItem = qobject_cast<QQuickItem *>(containment()->property("_plasma_graphicObject").value<QObject *>());
1464 
1465         if (containmentGraphicItem) {
1466             containmentGraphicItem->setProperty("_latte_view_interfacesobject", QVariant::fromValue(m_interfacesGraphicObj));
1467         }
1468     }
1469 
1470     emit interfacesGraphicObjChanged();
1471 }
1472 
event(QEvent * e)1473 bool View::event(QEvent *e)
1474 {
1475     QEvent *sunkevent = e;
1476 
1477     if (!m_inDelete) {
1478         emit eventTriggered(e);
1479 
1480         bool sinkableevent{false};
1481 
1482         switch (e->type()) {
1483         case QEvent::Enter:
1484             m_containsMouse = true;
1485             break;
1486 
1487         case QEvent::Leave:
1488             m_containsMouse = false;
1489             setContainsDrag(false);
1490             sinkableevent = true;
1491             break;
1492 
1493         case QEvent::DragEnter:
1494             setContainsDrag(true);
1495             sinkableevent = true;
1496             break;
1497 
1498         case QEvent::DragLeave:
1499             setContainsDrag(false);
1500             break;
1501 
1502         case QEvent::DragMove:
1503             sinkableevent = true;
1504             break;
1505 
1506         case QEvent::Drop:
1507             setContainsDrag(false);
1508             sinkableevent = true;
1509             break;
1510 
1511         case QEvent::MouseMove:
1512             sinkableevent = true;
1513             break;
1514 
1515         case QEvent::MouseButtonPress:
1516             if (auto me = dynamic_cast<QMouseEvent *>(e)) {
1517                 emit mousePressed(me->pos(), me->button());
1518                 sinkableevent = true;
1519             }
1520             break;
1521 
1522         case QEvent::MouseButtonRelease:
1523             if (auto me = dynamic_cast<QMouseEvent *>(e)) {
1524                 emit mouseReleased(me->pos(), me->button());
1525                 sinkableevent = true;
1526             }
1527             break;
1528 
1529         case QEvent::PlatformSurface:
1530             if (auto pe = dynamic_cast<QPlatformSurfaceEvent *>(e)) {
1531                 switch (pe->surfaceEventType()) {
1532                 case QPlatformSurfaceEvent::SurfaceCreated:
1533                     setupWaylandIntegration();
1534 
1535                     if (m_shellSurface) {
1536                         //! immediateSyncGeometry helps avoiding binding loops from containment qml side
1537                         m_positioner->immediateSyncGeometry();
1538                         m_effects->updateShadows();
1539                     }
1540 
1541                     break;
1542 
1543                 case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
1544                     if (m_shellSurface) {
1545                         delete m_shellSurface;
1546                         m_shellSurface = nullptr;
1547                         qDebug() << "WAYLAND dock window surface was deleted...";
1548                         m_effects->clearShadows();
1549                     }
1550 
1551                     break;
1552                 }
1553             }
1554 
1555             break;
1556 
1557         case QEvent::Show:
1558             if (m_visibility) {
1559                 m_visibility->initViewFlags();
1560             }
1561             break;
1562 
1563         case QEvent::Wheel:
1564             if (auto we = dynamic_cast<QWheelEvent *>(e)) {
1565 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
1566                 QPoint pos = QPoint(we->x(), we->y());
1567 #else
1568                 QPoint pos = we->position().toPoint();
1569 #endif
1570                 emit wheelScrolled(pos, we->angleDelta(), we->buttons());
1571 
1572                 sinkableevent = true;
1573             }
1574             break;
1575         default:
1576             break;
1577         }
1578 
1579         if (sinkableevent && m_sink->isActive()) {
1580             sunkevent = m_sink->onEvent(e);
1581         }
1582     }
1583 
1584     return ContainmentView::event(sunkevent);
1585 }
1586 
releaseConfigView()1587 void View::releaseConfigView()
1588 {
1589     m_primaryConfigView = nullptr;
1590 }
1591 
1592 //! release grab and restore mouse state
unblockMouse(int x,int y)1593 void View::unblockMouse(int x, int y)
1594 {
1595     setMouseGrabEnabled(false);
1596 
1597     m_releaseGrab_x = x;
1598     m_releaseGrab_y = y;
1599     m_releaseGrabTimer.start();
1600 }
1601 
releaseGrab()1602 void View::releaseGrab()
1603 {
1604     //! ungrab mouse
1605     if (mouseGrabberItem()) {
1606         mouseGrabberItem()->ungrabMouse();
1607     }
1608 
1609     //! properly release grabbed mouse in order to inform all views
1610     setMouseGrabEnabled(true);
1611     setMouseGrabEnabled(false);
1612 
1613     //! Send a fake QEvent::Leave to inform applets for mouse leaving the view
1614     QHoverEvent e(QEvent::Leave, QPoint(-5,-5),  QPoint(m_releaseGrab_x, m_releaseGrab_y));
1615     QCoreApplication::instance()->sendEvent(this, &e);
1616 }
1617 
action(const QString & name)1618 QAction *View::action(const QString &name)
1619 {
1620     if (!containment()) {
1621         return nullptr;
1622     }
1623 
1624     return this->containment()->actions()->action(name);
1625 }
1626 
containmentActions() const1627 QVariantList View::containmentActions() const
1628 {
1629     QVariantList actions;
1630 
1631     if (!containment()) {
1632         return actions;
1633     }
1634 
1635     const QString trigger = "RightButton;NoModifier";
1636     Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger);
1637 
1638     if (!plugin) {
1639         return actions;
1640     }
1641 
1642     if (plugin->containment() != this->containment()) {
1643         plugin->setContainment(this->containment());
1644         // now configure it
1645         KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins");
1646         cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType()));
1647         KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
1648         plugin->restore(pluginConfig);
1649     }
1650 
1651     for (QAction *ac : plugin->contextualActions()) {
1652         actions << QVariant::fromValue<QAction *>(ac);
1653     }
1654 
1655     return actions;
1656 }
1657 
isHighestPriorityView()1658 bool View::isHighestPriorityView() {
1659     if (m_layout) {
1660         return this == m_layout->highestPriorityView();
1661     }
1662 
1663     return false;
1664 }
1665 
1666 //! BEGIN: WORKAROUND order to force top panels always on top and above left/right panels
topViewAlwaysOnTop()1667 void View::topViewAlwaysOnTop()
1668 {
1669     if (!m_visibility) {
1670         return;
1671     }
1672 
1673     if (location() == Plasma::Types::TopEdge
1674             && m_visibility->mode() != Latte::Types::WindowsCanCover
1675             && m_visibility->mode() != Latte::Types::WindowsAlwaysCover) {
1676         //! this is needed in order to preserve that the top dock will be above others.
1677         //! Unity layout paradigm is a good example for this. The top panel shadow
1678         //! should be always on top compared to left panel
1679         m_visibility->setViewOnFrontLayer();
1680     }
1681 }
1682 
verticalUnityViewHasFocus()1683 void View::verticalUnityViewHasFocus()
1684 {
1685     if (formFactor() == Plasma::Types::Vertical
1686             && (y() != screenGeometry().y())
1687             && ( (m_alignment == Latte::Types::Justify && m_maxLength == 1.0)
1688                  ||(m_alignment == Latte::Types::Top && m_offset == 0.0) )) {
1689         emit m_corona->verticalUnityViewHasFocus();
1690     }
1691 }
1692 //! END: WORKAROUND
1693 
1694 //!BEGIN overriding context menus behavior
mousePressEvent(QMouseEvent * event)1695 void View::mousePressEvent(QMouseEvent *event)
1696 {
1697     bool result = m_contextMenu->mousePressEvent(event);
1698 
1699     if (result) {
1700         PlasmaQuick::ContainmentView::mousePressEvent(event);
1701         updateTransientWindowsTracking();
1702     }
1703 
1704     verticalUnityViewHasFocus();
1705 }
1706 //!END overriding context menus behavior
1707 
1708 //!BEGIN configuration functions
saveConfig()1709 void View::saveConfig()
1710 {
1711     if (!this->containment())
1712         return;
1713 
1714     auto config = this->containment()->config();
1715     config.writeEntry("onPrimary", onPrimary());
1716     config.writeEntry("byPassWM", byPassWM());
1717     config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts());
1718     config.writeEntry("name", m_name);
1719     config.writeEntry("viewType", (int)m_type);
1720 }
1721 
restoreConfig()1722 void View::restoreConfig()
1723 {
1724     if (!this->containment())
1725         return;
1726 
1727     auto config = this->containment()->config();
1728     m_onPrimary = config.readEntry("onPrimary", true);
1729     m_alignment = static_cast<Latte::Types::Alignment>(config.group("General").readEntry("alignment", (int)Latte::Types::Center));
1730     m_byPassWM = config.readEntry("byPassWM", false);
1731     m_isPreferredForShortcuts = config.readEntry("isPreferredForShortcuts", false);
1732     m_name = config.readEntry("name", QString());
1733 
1734     //! Send changed signals at the end in order to be sure that saveConfig
1735     //! wont rewrite default/invalid values
1736     emit alignmentChanged();
1737     emit nameChanged();
1738     emit onPrimaryChanged();
1739     emit byPassWMChanged();
1740 }
1741 //!END configuration functions
1742 
1743 }
1744 //!END namespace
1745