File indexing completed on 2024-06-16 14:04:38

0001 /*
0002 *  Copyright 2016  Smith AR <audoban@openmailbox.org>
0003 *                  Michail Vourlakos <mvourlakos@gmail.com>
0004 *
0005 *  This file is part of Latte-Dock
0006 *
0007 *  Latte-Dock is free software; you can redistribute it and/or
0008 *  modify it under the terms of the GNU General Public License as
0009 *  published by the Free Software Foundation; either version 2 of
0010 *  the License, or (at your option) any later version.
0011 *
0012 *  Latte-Dock is distributed in the hope that it will be useful,
0013 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015 *  GNU General Public License for more details.
0016 *
0017 *  You should have received a copy of the GNU General Public License
0018 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include "view.h"
0022 
0023 // local
0024 #include "contextmenu.h"
0025 #include "effects.h"
0026 #include "positioner.h"
0027 #include "visibilitymanager.h"
0028 #include "settings/primaryconfigview.h"
0029 #include "settings/secondaryconfigview.h"
0030 #include "../indicator/factory.h"
0031 #include "../lattecorona.h"
0032 #include "../layout/genericlayout.h"
0033 #include "../layouts/manager.h"
0034 #include "../plasma/extended/theme.h"
0035 #include "../screenpool.h"
0036 #include "../settings/universalsettings.h"
0037 #include "../shortcuts/globalshortcuts.h"
0038 #include "../shortcuts/shortcutstracker.h"
0039 #include "../../liblatte2/extras.h"
0040 
0041 // Qt
0042 #include <QAction>
0043 #include <QMouseEvent>
0044 #include <QQmlContext>
0045 #include <QQmlEngine>
0046 #include <QQmlProperty>
0047 #include <QQuickItem>
0048 #include <QMenu>
0049 
0050 // KDe
0051 #include <KActionCollection>
0052 #include <KActivities/Consumer>
0053 #include <KWayland/Client/plasmashell.h>
0054 #include <KWayland/Client/surface.h>
0055 #include <KWindowSystem>
0056 
0057 // Plasma
0058 #include <Plasma/Containment>
0059 #include <Plasma/ContainmentActions>
0060 #include <PlasmaQuick/AppletQuickItem>
0061 
0062 namespace Latte {
0063 
0064 //! both alwaysVisible and byPassWM are passed through corona because
0065 //! during the view window creation containment hasn't been set, but these variables
0066 //! are needed in order for window flags to be set correctly
0067 View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassWM)
0068     : PlasmaQuick::ContainmentView(corona),
0069       m_contextMenu(new ViewPart::ContextMenu(this)),
0070       m_effects(new ViewPart::Effects(this)),
0071       m_interface(new ViewPart::ContainmentInterface(this))
0072 {      
0073     //! needs to be created after Effects because it catches some of its signals
0074     //! and avoid a crash from View::winId() at the same time
0075     m_positioner = new ViewPart::Positioner(this);
0076 
0077     setTitle(corona->kPackage().metadata().name());
0078     setIcon(qGuiApp->windowIcon());
0079     setResizeMode(QuickViewSharedEngine::SizeRootObjectToView);
0080     setColor(QColor(Qt::transparent));
0081     setDefaultAlphaBuffer(true);
0082     setClearBeforeRendering(true);
0083 
0084     const auto flags = Qt::FramelessWindowHint
0085             | Qt::WindowStaysOnTopHint
0086             | Qt::NoDropShadowWindowHint
0087             | Qt::WindowDoesNotAcceptFocus;
0088 
0089     if (byPassWM) {
0090         setFlags(flags | Qt::BypassWindowManagerHint);
0091     } else {
0092         setFlags(flags);
0093     }
0094 
0095     if (targetScreen)
0096         m_positioner->setScreenToFollow(targetScreen);
0097     else
0098         m_positioner->setScreenToFollow(qGuiApp->primaryScreen());
0099 
0100     m_releaseGrabTimer.setInterval(400);
0101     m_releaseGrabTimer.setSingleShot(true);
0102     connect(&m_releaseGrabTimer, &QTimer::timeout, this, &View::releaseGrab);
0103 
0104     connect(this, &View::containmentChanged
0105             , this, [ &, byPassWM]() {
0106         qDebug() << "dock view c++ containment changed 1...";
0107 
0108         if (!this->containment())
0109             return;
0110 
0111         qDebug() << "dock view c++ containment changed 2...";
0112 
0113         //! First load default values from file
0114         restoreConfig();
0115 
0116         //! Afterwards override that values in case during creation something different is needed
0117         setByPassWM(byPassWM);
0118 
0119         //! Check the screen assigned to this dock
0120         reconsiderScreen();
0121 
0122         //! needs to be created before visibility creation because visibility uses it
0123         if (!m_windowsTracker) {
0124             m_windowsTracker = new ViewPart::WindowsTracker(this);
0125             emit windowsTrackerChanged();
0126         }
0127 
0128         if (!m_visibility) {
0129             m_visibility = new ViewPart::VisibilityManager(this);
0130 
0131             connect(m_visibility, &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() {
0132                 if (m_visibility->isHidden()) {
0133                     deactivateApplets();
0134                 }
0135             });
0136 
0137             emit visibilityChanged();
0138         }
0139 
0140         if (!m_indicator) {
0141             m_indicator = new ViewPart::Indicator(this);
0142             emit indicatorChanged();
0143         }
0144 
0145         connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus)));
0146     }, Qt::DirectConnection);
0147 
0148     m_corona = qobject_cast<Latte::Corona *>(this->corona());
0149 
0150     if (m_corona) {
0151         connect(m_corona, &Latte::Corona::viewLocationChanged, this, &View::dockLocationChanged);
0152     }
0153 }
0154 
0155 View::~View()
0156 {
0157     m_inDelete = true;
0158 
0159     //! clear Layout connections
0160     m_visibleHackTimer1.stop();
0161     m_visibleHackTimer2.stop();
0162     for (auto &c : connectionsLayout) {
0163         disconnect(c);
0164     }
0165 
0166     //! unload indicators
0167     if (m_indicator) {
0168         m_indicator->unloadIndicators();
0169     }
0170 
0171     disconnectSensitiveSignals();
0172     disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus)));
0173 
0174     qDebug() << "dock view deleting...";
0175     rootContext()->setContextProperty(QStringLiteral("dock"), nullptr);
0176     rootContext()->setContextProperty(QStringLiteral("layoutsManager"), nullptr);
0177     rootContext()->setContextProperty(QStringLiteral("shortcutsEngine"), nullptr);
0178     rootContext()->setContextProperty(QStringLiteral("themeExtended"), nullptr);
0179     rootContext()->setContextProperty(QStringLiteral("universalSettings"), nullptr);
0180 
0181     //! this disconnect does not free up connections correctly when
0182     //! latteView is deleted. A crash for this example is the following:
0183     //! switch to Alternative Session and disable compositing,
0184     //! the signal creating the crash was probably from deleted
0185     //! windows.
0186     //! this->disconnect();
0187 
0188     if (m_configView) {
0189         m_configView->deleteLater();
0190     }
0191 
0192     if (m_contextMenu) {
0193         delete m_contextMenu;
0194     }
0195 
0196     //needs to be deleted before Effects because it catches some of its signals
0197     if (m_positioner) {
0198         delete m_positioner;
0199     }
0200 
0201     if (m_effects) {
0202         delete m_effects;
0203     }
0204 
0205     if (m_indicator) {
0206         delete m_indicator;
0207     }
0208 
0209     if (m_interface) {
0210         delete m_interface;
0211     }
0212 
0213     if (m_visibility) {
0214         delete m_visibility;
0215     }
0216 
0217     if (m_windowsTracker) {
0218         delete m_windowsTracker;
0219     }
0220 }
0221 
0222 void View::init()
0223 {
0224     connect(this, &QQuickWindow::xChanged, this, &View::xChanged);
0225     connect(this, &QQuickWindow::xChanged, this, &View::updateAbsoluteGeometry);
0226     connect(this, &QQuickWindow::yChanged, this, &View::yChanged);
0227     connect(this, &QQuickWindow::yChanged, this, &View::updateAbsoluteGeometry);
0228     connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged);
0229     connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsoluteGeometry);
0230     connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged);
0231     connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsoluteGeometry);
0232 
0233     //! used in order to disconnect it when it should NOT be called because it creates crashes
0234     connect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom);
0235     connect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom);
0236     connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot);
0237 
0238     connect(this, &View::byPassWMChanged, this, &View::saveConfig);
0239     connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig);
0240     connect(this, &View::onPrimaryChanged, this, &View::saveConfig);
0241     connect(this, &View::typeChanged, this, &View::saveConfig);
0242 
0243     connect(this, &View::normalThicknessChanged, this, [&]() {
0244         emit availableScreenRectChangedFrom(this);
0245     });
0246 
0247     connect(m_effects, &ViewPart::Effects::innerShadowChanged, this, [&]() {
0248         emit availableScreenRectChangedFrom(this);
0249     });
0250     connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut);
0251     connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged);
0252     connect(m_positioner, &ViewPart::Positioner::windowSizeChanged, this, [&]() {
0253         emit availableScreenRectChangedFrom(this);
0254     });
0255 
0256     connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged);
0257 
0258     connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::pluginsUpdated, this, &View::reloadSource);
0259     //! View sends this signal in order to avoid crashes from ViewPart::Indicator when the view is recreated
0260     connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::customPluginsChanged, this, &View::customPluginsChanged);
0261 
0262     ///!!!!!
0263     rootContext()->setContextProperty(QStringLiteral("latteView"), this);
0264 
0265     if (m_corona) {
0266         rootContext()->setContextProperty(QStringLiteral("layoutsManager"), m_corona->layoutsManager());
0267         rootContext()->setContextProperty(QStringLiteral("shortcutsEngine"), m_corona->globalShortcuts()->shortcutsTracker());
0268         rootContext()->setContextProperty(QStringLiteral("themeExtended"), m_corona->themeExtended());
0269         rootContext()->setContextProperty(QStringLiteral("universalSettings"), m_corona->universalSettings());
0270     }
0271 
0272     setSource(corona()->kPackage().filePath("lattedockui"));
0273     m_positioner->syncGeometry();
0274 
0275     qDebug() << "SOURCE:" << source();
0276 }
0277 
0278 void View::reloadSource()
0279 {
0280     if (m_layout && containment()) {
0281         if (settingsWindowIsShown()) {
0282             m_configView->deleteLater();
0283         }
0284 
0285         engine()->clearComponentCache();
0286         m_layout->recreateView(containment(), settingsWindowIsShown());
0287     }
0288 }
0289 
0290 
0291 bool View::inDelete() const
0292 {
0293     return m_inDelete;
0294 }
0295 
0296 void View::disconnectSensitiveSignals()
0297 {
0298     disconnect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom);
0299     disconnect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom);
0300     disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot);
0301 
0302     setLayout(nullptr);
0303 
0304     if (m_windowsTracker) {
0305       //  m_windowsTracker->setEnabled(false);
0306     }
0307 }
0308 
0309 void View::availableScreenRectChangedFromSlot(View *origin)
0310 {
0311     if (m_inDelete || origin == this)
0312         return;
0313 
0314     if (formFactor() == Plasma::Types::Vertical) {
0315         m_positioner->syncGeometry();
0316     }
0317 
0318 }
0319 
0320 void View::setupWaylandIntegration()
0321 {
0322     if (m_shellSurface)
0323         return;
0324 
0325     if (Latte::Corona *c = qobject_cast<Latte::Corona *>(corona())) {
0326         using namespace KWayland::Client;
0327         PlasmaShell *interface {c->waylandCoronaInterface()};
0328 
0329         if (!interface)
0330             return;
0331 
0332         Surface *s{Surface::fromWindow(this)};
0333 
0334         if (!s)
0335             return;
0336 
0337         m_shellSurface = interface->createSurface(s, this);
0338         qDebug() << "WAYLAND dock window surface was created...";
0339 
0340         m_shellSurface->setSkipTaskbar(true);
0341         m_shellSurface->setRole(PlasmaShellSurface::Role::Panel);
0342         m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsGoBelow);
0343     }
0344 }
0345 
0346 KWayland::Client::PlasmaShellSurface *View::surface()
0347 {
0348     return m_shellSurface;
0349 }
0350 
0351 //! the main function which decides if this dock is at the
0352 //! correct screen
0353 void View::reconsiderScreen()
0354 {
0355     m_positioner->reconsiderScreen();
0356 }
0357 
0358 void View::copyView()
0359 {
0360     m_layout->copyView(containment());
0361 }
0362 
0363 void View::removeView()
0364 {
0365     if (m_layout && m_layout->viewsCount() > 1) {
0366         QAction *removeAct = this->containment()->actions()->action(QStringLiteral("remove"));
0367 
0368         if (removeAct) {
0369             removeAct->trigger();
0370         }
0371     }
0372 }
0373 
0374 bool View::settingsWindowIsShown()
0375 {
0376     auto configView = qobject_cast<ViewPart::PrimaryConfigView *>(m_configView);
0377 
0378     return (configView != nullptr);
0379 }
0380 
0381 void View::showSettingsWindow()
0382 {
0383     if (!settingsWindowIsShown()) {
0384         emit m_visibility->mustBeShown();
0385         showConfigurationInterface(containment());
0386         applyActivitiesToWindows();
0387     }
0388 }
0389 
0390 PlasmaQuick::ConfigView *View::configView()
0391 {
0392     return m_configView;
0393 }
0394 
0395 void View::showConfigurationInterface(Plasma::Applet *applet)
0396 {
0397     if (!applet || !applet->containment())
0398         return;
0399 
0400     Plasma::Containment *c = qobject_cast<Plasma::Containment *>(applet);
0401 
0402     if (m_configView && c && c->isContainment() && c == this->containment()) {
0403         if (m_configView->isVisible()) {
0404             m_configView->hide();
0405         } else {
0406             m_configView->show();
0407         }
0408 
0409         return;
0410     } else if (m_configView) {
0411         if (m_configView->applet() == applet) {
0412             m_configView->show();
0413 
0414             if (KWindowSystem::isPlatformX11()) {
0415                 m_configView->requestActivate();
0416             }
0417             return;
0418         } else {
0419             m_configView->hide();
0420         }
0421     }
0422 
0423     bool delayConfigView = false;
0424 
0425     if (c && containment() && c->isContainment() && c->id() == this->containment()->id()) {
0426         m_configView = new ViewPart::PrimaryConfigView(c, this);
0427         delayConfigView = true;
0428     } else {
0429         m_configView = new PlasmaQuick::ConfigView(applet);
0430     }
0431 
0432     m_configView.data()->init();
0433 
0434     if (!delayConfigView) {
0435         m_configView->show();
0436     } else {
0437         //add a timer for showing the configuration window the first time it is
0438         //created in order to give the containment's layouts the time to
0439         //calculate the window's height
0440         QTimer::singleShot(150, [this]() {
0441             if (m_configView) {
0442                 m_configView->show();
0443             }
0444         });
0445     }
0446 }
0447 
0448 QRect View::localGeometry() const
0449 {
0450     return m_localGeometry;
0451 }
0452 
0453 void View::setLocalGeometry(const QRect &geometry)
0454 {
0455     if (m_localGeometry == geometry) {
0456         return;
0457     }
0458 
0459     m_localGeometry = geometry;
0460     emit localGeometryChanged();
0461     updateAbsoluteGeometry();
0462 }
0463 
0464 void View::updateAbsoluteGeometry(bool bypassChecks)
0465 {
0466     //! there was a -1 in height and width here. The reason of this
0467     //! if I remember correctly was related to multi-screen but I cant
0468     //! remember exactly the reason, something related to right edge in
0469     //! multi screen environment. BUT this was breaking the entire AlwaysVisible
0470     //! experience with struts. Removing them in order to restore correct
0471     //! behavior and keeping this comment in order to check for
0472     //! multi-screen breakage
0473     QRect absGeometry {x() + m_localGeometry.x(), y() + m_localGeometry.y()
0474                 , m_localGeometry.width(), m_localGeometry.height()};
0475 
0476     if (m_absoluteGeometry == absGeometry && !bypassChecks) {
0477         return;
0478     }
0479 
0480     if (m_absoluteGeometry != absGeometry) {
0481         m_absoluteGeometry = absGeometry;
0482         emit absoluteGeometryChanged(m_absoluteGeometry);
0483     }
0484 
0485     //! this is needed in order to update correctly the screenGeometries
0486     if (visibility() && corona() && visibility()->mode() == Types::AlwaysVisible) {
0487         //! main use of BYPASSCKECKS is from Positioner when the view changes screens
0488 
0489         emit availableScreenRectChangedFrom(this);
0490         emit availableScreenRegionChangedFrom(this);
0491     }
0492 }
0493 
0494 void View::statusChanged(Plasma::Types::ItemStatus status)
0495 {
0496     if (containment()) {
0497         if (containment()->status() >= Plasma::Types::NeedsAttentionStatus &&
0498                 containment()->status() != Plasma::Types::HiddenStatus) {
0499             setBlockHiding(true);
0500         } else if (!containment()->isUserConfiguring()){
0501             setBlockHiding(false);
0502         }
0503     }
0504 }
0505 
0506 Types::ViewType View::type() const
0507 {
0508     return m_type;
0509 }
0510 
0511 void View::setType(Types::ViewType type)
0512 {
0513     if (m_type == type) {
0514         return;
0515     }
0516 
0517     m_type = type;
0518     emit typeChanged();
0519 }
0520 
0521 bool View::alternativesIsShown() const
0522 {
0523     return m_alternativesIsShown;
0524 }
0525 
0526 void View::setAlternativesIsShown(bool show)
0527 {
0528     if (m_alternativesIsShown == show) {
0529         return;
0530     }
0531 
0532     m_alternativesIsShown = show;
0533 
0534     setBlockHiding(show);
0535     emit alternativesIsShownChanged();
0536 }
0537 
0538 bool View::containsDrag() const
0539 {
0540     return m_containsDrag;
0541 }
0542 
0543 void View::setContainsDrag(bool contains)
0544 {
0545     if (m_containsDrag == contains) {
0546         return;
0547     }
0548 
0549     m_containsDrag = contains;
0550     emit containsDragChanged();
0551 }
0552 
0553 bool View::containsMouse() const
0554 {
0555     return m_containsMouse;
0556 }
0557 
0558 bool View::contextMenuIsShown() const
0559 {
0560     if (!m_contextMenu) {
0561         return false;
0562     }
0563 
0564     return m_contextMenu->menu();
0565 }
0566 
0567 int View::currentThickness() const
0568 {
0569     if (formFactor() == Plasma::Types::Vertical) {
0570         return m_effects->mask().isNull() ? width() : m_effects->mask().width() - m_effects->innerShadow();
0571     } else {
0572         return m_effects->mask().isNull() ? height() : m_effects->mask().height() - m_effects->innerShadow();
0573     }
0574 }
0575 
0576 int View::normalThickness() const
0577 {
0578     return m_normalThickness;
0579 }
0580 
0581 void View::setNormalThickness(int thickness)
0582 {
0583     if (m_normalThickness == thickness) {
0584         return;
0585     }
0586 
0587     m_normalThickness = thickness;
0588     emit normalThicknessChanged();
0589 }
0590 
0591 bool View::byPassWM() const
0592 {
0593     return m_byPassWM;
0594 }
0595 
0596 void View::setByPassWM(bool bypass)
0597 {
0598     if (m_byPassWM == bypass) {
0599         return;
0600     }
0601 
0602     m_byPassWM = bypass;
0603     emit byPassWMChanged();
0604 }
0605 
0606 bool View::behaveAsPlasmaPanel() const
0607 {
0608     return m_behaveAsPlasmaPanel;
0609 }
0610 
0611 void View::setBehaveAsPlasmaPanel(bool behavior)
0612 {
0613     if (m_behaveAsPlasmaPanel == behavior) {
0614         return;
0615     }
0616 
0617     m_behaveAsPlasmaPanel = behavior;
0618 
0619     emit behaveAsPlasmaPanelChanged();
0620 }
0621 
0622 bool View::inEditMode() const
0623 {
0624     return m_inEditMode;
0625 }
0626 
0627 void View::setInEditMode(bool edit)
0628 {
0629     if (m_inEditMode == edit) {
0630         return;
0631     }
0632 
0633     m_inEditMode = edit;
0634 
0635     emit inEditModeChanged();
0636 }
0637 
0638 bool View::isPreferredForShortcuts() const
0639 {
0640     return m_isPreferredForShortcuts;
0641 }
0642 
0643 void View::setIsPreferredForShortcuts(bool preferred)
0644 {
0645     if (m_isPreferredForShortcuts == preferred) {
0646         return;
0647     }
0648 
0649     m_isPreferredForShortcuts = preferred;
0650 
0651     emit isPreferredForShortcutsChanged();
0652 
0653     if (m_isPreferredForShortcuts && m_layout) {
0654         emit m_layout->preferredViewForShortcutsChanged(this);
0655     }
0656 }
0657 
0658 bool View::latteTasksArePresent() const
0659 {
0660     return m_latteTasksArePresent;
0661 }
0662 
0663 void View::setLatteTasksArePresent(bool present)
0664 {
0665     if (m_latteTasksArePresent == present) {
0666         return;
0667     }
0668 
0669     m_latteTasksArePresent = present;
0670     emit latteTasksArePresentChanged();
0671 }
0672 
0673 bool View::isTouchingBottomViewAndIsBusy() const
0674 {
0675     return m_isTouchingBottomViewAndIsBusy;
0676 }
0677 
0678 void View::setIsTouchingBottomViewAndIsBusy(bool touchAndBusy)
0679 {
0680     if (m_isTouchingBottomViewAndIsBusy == touchAndBusy) {
0681         return;
0682     }
0683 
0684     m_isTouchingBottomViewAndIsBusy = touchAndBusy;
0685 
0686     emit isTouchingBottomViewAndIsBusyChanged();
0687 }
0688 
0689 bool View::isTouchingTopViewAndIsBusy() const
0690 {
0691     return m_isTouchingTopViewAndIsBusy;
0692 }
0693 
0694 void View::setIsTouchingTopViewAndIsBusy(bool touchAndBusy)
0695 {
0696     if (m_isTouchingTopViewAndIsBusy == touchAndBusy) {
0697         return;
0698     }
0699 
0700     m_isTouchingTopViewAndIsBusy = touchAndBusy;
0701     emit isTouchingTopViewAndIsBusyChanged();
0702 }
0703 
0704 void View::preferredViewForShortcutsChangedSlot(Latte::View *view)
0705 {
0706     if (view != this) {
0707         setIsPreferredForShortcuts(false);
0708     }
0709 }
0710 
0711 bool View::onPrimary() const
0712 {
0713     return m_onPrimary;
0714 }
0715 
0716 void View::setOnPrimary(bool flag)
0717 {
0718     if (m_onPrimary == flag) {
0719         return;
0720     }
0721 
0722     m_onPrimary = flag;
0723     emit onPrimaryChanged();
0724 }
0725 
0726 float View::maxLength() const
0727 {
0728     return m_maxLength;
0729 }
0730 
0731 void View::setMaxLength(float length)
0732 {
0733     if (m_maxLength == length) {
0734         return;
0735     }
0736 
0737     m_maxLength = length;
0738     emit maxLengthChanged();
0739 }
0740 
0741 int View::editThickness() const
0742 {
0743     return m_editThickness;
0744 }
0745 
0746 void View::setEditThickness(int thickness)
0747 {
0748     if (m_editThickness == thickness) {
0749         return;
0750     }
0751 
0752     m_editThickness = thickness;
0753 
0754     emit editThicknessChanged();
0755 }
0756 
0757 int View::maxThickness() const
0758 {
0759     return m_maxThickness;
0760 }
0761 
0762 void View::setMaxThickness(int thickness)
0763 {
0764     if (m_maxThickness == thickness)
0765         return;
0766 
0767     m_maxThickness = thickness;
0768     emit maxThicknessChanged();
0769 }
0770 
0771 int View::alignment() const
0772 {
0773     return m_alignment;
0774 }
0775 
0776 void View::setAlignment(int alignment)
0777 {
0778     Types::Alignment align = static_cast<Types::Alignment>(alignment);
0779 
0780     if (m_alignment == alignment) {
0781         return;
0782     }
0783 
0784     m_alignment = align;
0785     emit alignmentChanged();
0786 }
0787 
0788 QRect View::absoluteGeometry() const
0789 {
0790     return m_absoluteGeometry;
0791 }
0792 
0793 QRect View::screenGeometry() const
0794 {
0795     if (this->screen()) {
0796         QRect geom = this->screen()->geometry();
0797         return geom;
0798     }
0799 
0800     return QRect();
0801 }
0802 
0803 int View::offset() const
0804 {
0805     return m_offset;
0806 }
0807 
0808 void View::setOffset(int offset)
0809 {
0810     if (m_offset == offset) {
0811         return;
0812     }
0813 
0814     m_offset = offset;
0815     emit offsetChanged();
0816 }
0817 
0818 int View::fontPixelSize() const
0819 {
0820     return m_fontPixelSize;
0821 }
0822 
0823 void View::setFontPixelSize(int size)
0824 {
0825     if (m_fontPixelSize == size) {
0826         return;
0827     }
0828 
0829     m_fontPixelSize = size;
0830 
0831     emit fontPixelSizeChanged();
0832 }
0833 
0834 bool View::isOnAllActivities() const
0835 {
0836     return m_activities.isEmpty() || m_activities[0] == "0";
0837 }
0838 
0839 bool View::isOnActivity(const QString &activity) const
0840 {
0841     return isOnAllActivities() || m_activities.contains(activity);
0842 }
0843 
0844 QStringList View::activities() const
0845 {
0846     return m_activities;
0847 }
0848 
0849 void View::applyActivitiesToWindows()
0850 {
0851     if (m_visibility && m_layout) {
0852         m_windowsTracker->setWindowOnActivities(*this, m_activities);
0853 
0854         if (m_configView) {
0855             m_windowsTracker->setWindowOnActivities(*m_configView, m_activities);
0856 
0857             auto configView = qobject_cast<ViewPart::PrimaryConfigView *>(m_configView);
0858 
0859             if (configView && configView->secondaryWindow()) {
0860                 m_windowsTracker->setWindowOnActivities(*configView->secondaryWindow(), m_activities);
0861             }
0862         }
0863 
0864         if (m_visibility->supportsKWinEdges()) {
0865             m_visibility->applyActivitiesToHiddenWindows(m_activities);
0866         }
0867     }
0868 }
0869 
0870 Layout::GenericLayout *View::layout() const
0871 {
0872     return m_layout;
0873 }
0874 
0875 void View::setLayout(Layout::GenericLayout *layout)
0876 {
0877     if (m_layout == layout) {
0878         return;
0879     }
0880 
0881     // clear mode
0882     for (auto &c : connectionsLayout) {
0883         disconnect(c);
0884     }
0885 
0886     m_layout = layout;
0887 
0888     if (m_layout) {
0889         connectionsLayout << connect(containment(), &Plasma::Applet::destroyedChanged, m_layout, &Layout::GenericLayout::destroyedChanged);
0890         connectionsLayout << connect(containment(), &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged);
0891         connectionsLayout << connect(containment(), &Plasma::Containment::appletAlternativesRequested, m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection);
0892 
0893         if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0894             connectionsLayout << connect(containment(), &Plasma::Containment::appletCreated, m_layout, &Layout::GenericLayout::appletCreated);
0895         }
0896 
0897         connectionsLayout << connect(m_positioner, &Latte::ViewPart::Positioner::edgeChanged, m_layout, &Layout::GenericLayout::viewEdgeChanged);
0898 
0899         //! Sometimes the activity isnt completely ready, by adding a delay
0900         //! we try to catch up
0901         QTimer::singleShot(100, [this]() {
0902             if (m_layout && m_visibility) {
0903                 m_activities = m_layout->appliedActivities();
0904                 qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_layout->name() << " - activities: " << m_activities;
0905                 applyActivitiesToWindows();
0906                 emit activitiesChanged();
0907             }
0908         });
0909 
0910         connectionsLayout << connect(m_layout, &Layout::GenericLayout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot);
0911         connectionsLayout << connect(m_layout, &Layout::GenericLayout::lastConfigViewForChanged, this, &View::configViewCreatedFor);
0912 
0913         Latte::Corona *latteCorona = qobject_cast<Latte::Corona *>(this->corona());
0914 
0915         if (latteCorona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0916             connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() {
0917                 if (m_layout && m_visibility) {
0918                     m_activities = m_layout->appliedActivities();
0919                     qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_layout->name()
0920                              << " - activities: " << m_activities;
0921                     applyActivitiesToWindows();
0922                     emit activitiesChanged();
0923                 }
0924             });
0925 
0926             connectionsLayout << connect(m_layout, &Layout::GenericLayout::activitiesChanged, this, [&]() {
0927                 if (m_layout) {
0928                     m_activities = m_layout->appliedActivities();
0929                     applyActivitiesToWindows();
0930                     emit activitiesChanged();
0931                 }
0932             });
0933 
0934             connectionsLayout << connect(latteCorona->layoutsManager(), &Layouts::Manager::layoutsChanged, this, [&]() {
0935                 if (m_layout) {
0936                     m_activities = m_layout->appliedActivities();
0937                     applyActivitiesToWindows();
0938                     emit activitiesChanged();
0939                 }
0940             });
0941 
0942             //! BEGIN OF KWIN HACK
0943             //! IMPORTANT ::: Fixing KWin Faulty Behavior that KWin hides ALL Views when an Activity stops
0944             //! with no reason!!
0945 
0946             m_visibleHackTimer1.setInterval(400);
0947             m_visibleHackTimer2.setInterval(2500);
0948             m_visibleHackTimer1.setSingleShot(true);
0949             m_visibleHackTimer2.setSingleShot(true);
0950 
0951             connectionsLayout << connect(this, &QWindow::visibleChanged, this, [&]() {
0952                 if (m_layout && !inDelete() & !isVisible()) {
0953                     m_visibleHackTimer1.start();
0954                     m_visibleHackTimer2.start();
0955                 }
0956             });
0957 
0958             connectionsLayout << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() {               
0959                 applyActivitiesToWindows();
0960                 emit activitiesChanged();
0961 
0962                 if (m_layout && !inDelete() & !isVisible()) {
0963                     show();
0964                     //qDebug() << "View:: Enforce reshow from timer 1...";
0965                     emit forcedShown();
0966                 } else {                   
0967                     //qDebug() << "View:: No needed reshow from timer 1...";
0968                 }
0969             });
0970 
0971             connectionsLayout << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() {
0972                 applyActivitiesToWindows();
0973                 emit activitiesChanged();
0974 
0975                 if (m_layout && !inDelete() & !isVisible()) {
0976                     show();
0977                     //qDebug() << "View:: Enforce reshow from timer 1...";
0978                     emit forcedShown();
0979                 } else {
0980                     //qDebug() << "View:: No needed reshow from timer 1...";
0981                 }
0982             });
0983 
0984             //! END OF KWIN HACK
0985         }
0986 
0987         emit layoutChanged();
0988     } else {
0989         m_activities.clear();
0990     }
0991 }
0992 
0993 void View::moveToLayout(QString layoutName)
0994 {
0995     if (!m_layout) {
0996         return;
0997     }
0998 
0999     QList<Plasma::Containment *> containments = m_layout->unassignFromLayout(this);
1000 
1001     Latte::Corona *latteCorona = qobject_cast<Latte::Corona *>(this->corona());
1002 
1003     if (latteCorona && containments.size() > 0) {
1004         Layout::GenericLayout *newLayout = latteCorona->layoutsManager()->synchronizer()->layout(layoutName);
1005 
1006         if (newLayout) {
1007             newLayout->assignToLayout(this, containments);
1008         }
1009     }
1010 }
1011 
1012 void View::setBlockHiding(bool block)
1013 {
1014     if (!block) {
1015         auto *configView = qobject_cast<ViewPart::PrimaryConfigView *>(m_configView);
1016 
1017         if (m_alternativesIsShown || (configView && configView->sticker() && configView->isVisible())) {
1018             return;
1019         }
1020 
1021         if (m_visibility) {
1022             m_visibility->setBlockHiding(false);
1023         }
1024     } else {
1025         if (m_visibility) {
1026             m_visibility->setBlockHiding(true);
1027         }
1028     }
1029 }
1030 
1031 void View::configViewCreatedFor(Latte::View *view)
1032 {
1033     if (view!=this && m_configView) {
1034         //! for each layout only one dock should show its configuration windows
1035         //! otherwise we could reach a point that because a settings window
1036         //! is below another Latte View its options are not reachable
1037         auto configDialog = qobject_cast<ViewPart::PrimaryConfigView *>(m_configView);
1038 
1039         if (configDialog) {
1040             configDialog->hideConfigWindow();
1041         }
1042     }
1043 }
1044 
1045 void View::hideWindowsForSlidingOut()
1046 {
1047     setBlockHiding(false);
1048 
1049     if (m_configView) {
1050         auto configDialog = qobject_cast<ViewPart::PrimaryConfigView *>(m_configView);
1051 
1052         if (configDialog) {
1053             configDialog->hideConfigWindow();
1054         }
1055     }
1056 }
1057 
1058 //! remove latte tasks plasmoid
1059 void View::removeTasksPlasmoid()
1060 {
1061     if (!tasksPresent() || !containment()) {
1062         return;
1063     }
1064 
1065     for (const Plasma::Applet *applet : containment()->applets()) {
1066         KPluginMetaData meta = applet->kPackage().metadata();
1067 
1068         if (meta.pluginId() == "org.kde.latte.plasmoid") {
1069             QAction *closeApplet = applet->actions()->action(QStringLiteral("remove"));
1070 
1071             if (closeApplet) {
1072                 closeApplet->trigger();
1073                 //! remove only the first found
1074                 return;
1075             }
1076         }
1077     }
1078 }
1079 
1080 //! check if the tasks plasmoid exist in the dock
1081 bool View::tasksPresent()
1082 {
1083     if (!this->containment()) {
1084         return false;
1085     }
1086 
1087     for (const Plasma::Applet *applet : this->containment()->applets()) {
1088         const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides"));
1089 
1090         if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) {
1091             return true;
1092         }
1093     }
1094 
1095     return false;
1096 }
1097 
1098 //!check if the plasmoid with _name_ exists in the midedata
1099 bool View::mimeContainsPlasmoid(QMimeData *mimeData, QString name)
1100 {
1101     if (!mimeData) {
1102         return false;
1103     }
1104 
1105     if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) {
1106         QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename"));
1107         const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
1108 
1109         for (const QString &appletName : appletNames) {
1110             if (appletName == name)
1111                 return true;
1112         }
1113     }
1114 
1115     return false;
1116 }
1117 
1118 ViewPart::Effects *View::effects() const
1119 {
1120     return m_effects;
1121 }
1122 
1123 ViewPart::Indicator *View::indicator() const
1124 {
1125     return m_indicator;
1126 }
1127 
1128 ViewPart::ContainmentInterface *View::interface() const
1129 {
1130     return m_interface;
1131 }
1132 
1133 ViewPart::Positioner *View::positioner() const
1134 {
1135     return m_positioner;
1136 }
1137 
1138 ViewPart::VisibilityManager *View::visibility() const
1139 {
1140     return m_visibility;
1141 }
1142 
1143 ViewPart::WindowsTracker *View::windowsTracker() const
1144 {
1145     return m_windowsTracker;
1146 }
1147 
1148 bool View::event(QEvent *e)
1149 {   
1150     if (!m_inDelete) {
1151         emit eventTriggered(e);
1152 
1153         switch (e->type()) {
1154         case QEvent::Enter:
1155             m_containsMouse = true;
1156 
1157             if (m_configView) {
1158                 ViewPart::PrimaryConfigView *primaryConfigView = qobject_cast<ViewPart::PrimaryConfigView *>(m_configView);
1159 
1160                 if (primaryConfigView) {
1161                     if (primaryConfigView->secondaryWindow()) {
1162                         ViewPart::SecondaryConfigView *secConfigView = qobject_cast<ViewPart::SecondaryConfigView *>(primaryConfigView->secondaryWindow());
1163                         if (secConfigView) {
1164                             secConfigView->requestActivate();
1165                         }
1166                     }
1167 
1168                     primaryConfigView->requestActivate();
1169                 }
1170             }
1171             break;
1172 
1173         case QEvent::Leave:
1174             m_containsMouse = false;
1175             setContainsDrag(false);
1176             engine()->trimComponentCache();
1177             break;
1178 
1179         case QEvent::DragEnter:
1180             setContainsDrag(true);
1181             break;
1182 
1183         case QEvent::DragLeave:
1184         case QEvent::Drop:
1185             setContainsDrag(false);
1186             break;
1187 
1188         case QEvent::MouseButtonPress:
1189             if (auto mouseEvent = dynamic_cast<QMouseEvent *>(e)) {
1190                 emit mousePressed(mouseEvent->pos(), mouseEvent->button());
1191             }
1192             break;
1193         case QEvent::MouseButtonRelease:
1194             if (auto mouseEvent = dynamic_cast<QMouseEvent *>(e)) {
1195                 emit mouseReleased(mouseEvent->pos(), mouseEvent->button());
1196             }
1197             break;
1198        /* case QEvent::DragMove:
1199             qDebug() << "DRAG MOVING>>>>>>";
1200             break;*/
1201         case QEvent::PlatformSurface:
1202             if (auto pe = dynamic_cast<QPlatformSurfaceEvent *>(e)) {
1203                 switch (pe->surfaceEventType()) {
1204                 case QPlatformSurfaceEvent::SurfaceCreated:
1205                     setupWaylandIntegration();
1206 
1207                     if (m_shellSurface) {
1208                         m_positioner->syncGeometry();
1209                         m_effects->updateShadows();
1210                     }
1211 
1212                     break;
1213 
1214                 case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
1215                     if (m_shellSurface) {
1216                         delete m_shellSurface;
1217                         m_shellSurface = nullptr;
1218                         qDebug() << "WAYLAND dock window surface was deleted...";
1219                         m_effects->clearShadows();
1220                     }
1221 
1222                     break;
1223                 }
1224             }
1225 
1226             break;
1227 
1228         case QEvent::Show:
1229             m_corona->wm()->setViewExtraFlags(*this);
1230             break;
1231 
1232         default:
1233             break;
1234         }
1235     }
1236 
1237     return ContainmentView::event(e);
1238 }
1239 
1240 //! release grab and restore mouse state
1241 void View::unblockMouse(int x, int y)
1242 {
1243     setMouseGrabEnabled(false);
1244 
1245     m_releaseGrab_x = x;
1246     m_releaseGrab_y = y;
1247     m_releaseGrabTimer.start();
1248 }
1249 
1250 void View::releaseGrab()
1251 {
1252     //! ungrab mouse
1253     if (mouseGrabberItem()) {
1254         mouseGrabberItem()->ungrabMouse();
1255     }
1256 
1257     //! properly release grabbed mouse in order to inform all views
1258     setMouseGrabEnabled(true);
1259     setMouseGrabEnabled(false);
1260 
1261     //! Send a fake QEvent::Leave to inform applets for mouse leaving the view
1262     QHoverEvent e(QEvent::Leave, QPoint(-5,-5),  QPoint(m_releaseGrab_x, m_releaseGrab_y));
1263     QCoreApplication::instance()->sendEvent(this, &e);
1264 }
1265 
1266 void View::deactivateApplets()
1267 {
1268     if (!containment()) {
1269         return;
1270     }
1271 
1272     for (const auto applet : containment()->applets()) {
1273         PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
1274 
1275         if (ai) {
1276             ai->setExpanded(false);
1277         }
1278     }
1279 }
1280 
1281 bool View::appletIsExpandable(const int id)
1282 {
1283     if (!containment()) {
1284         return false;
1285     }
1286 
1287     for (const auto applet : containment()->applets()) {
1288         if (applet->id() == id) {
1289             PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
1290 
1291             if (ai) {
1292                 return (ai->preferredRepresentation() != ai->fullRepresentation());
1293             }
1294         }
1295     }
1296 
1297     return false;
1298 }
1299 
1300 void View::toggleAppletExpanded(const int id)
1301 {
1302     if (!containment()) {
1303         return;
1304     }
1305 
1306     for (const auto applet : containment()->applets()) {
1307         if (applet->id() == id) {
1308             PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
1309 
1310             if (ai) {
1311                 if (!ai->isActivationTogglesExpanded()) {
1312                     ai->setActivationTogglesExpanded(true);
1313                 }
1314 
1315                 emit applet->activated();
1316             }
1317         }
1318     }
1319 }
1320 
1321 QVariantList View::containmentActions()
1322 {
1323     QVariantList actions;
1324     /*if (containment()->corona()->immutability() != Plasma::Types::Mutable) {
1325         return actions;
1326     }*/
1327     //FIXME: the trigger string it should be better to be supported this way
1328     //const QString trigger = Plasma::ContainmentActions::eventToString(event);
1329     const QString trigger = "RightButton;NoModifier";
1330     Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger);
1331 
1332     if (!plugin) {
1333         return actions;
1334     }
1335 
1336     if (plugin->containment() != this->containment()) {
1337         plugin->setContainment(this->containment());
1338         // now configure it
1339         KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins");
1340         cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType()));
1341         KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
1342         plugin->restore(pluginConfig);
1343     }
1344 
1345     for (QAction *ac : plugin->contextualActions()) {
1346         actions << QVariant::fromValue<QAction *>(ac);
1347     }
1348 
1349     return actions;
1350 }
1351 
1352 bool View::isHighestPriorityView() {
1353     if (m_layout) {
1354         return this == m_layout->highestPriorityView();
1355     }
1356 
1357     return false;
1358 }
1359 
1360 //!BEGIN overriding context menus behavior
1361 void View::mousePressEvent(QMouseEvent *event)
1362 {
1363     bool result = m_contextMenu->mousePressEvent(event);
1364     emit contextMenuIsShownChanged();
1365 
1366     if (result) {
1367         PlasmaQuick::ContainmentView::mousePressEvent(event);
1368     }
1369 }
1370 //!END overriding context menus behavior
1371 
1372 //!BEGIN configuration functions
1373 void View::saveConfig()
1374 {
1375     if (!this->containment())
1376         return;
1377 
1378     auto config = this->containment()->config();
1379     config.writeEntry("onPrimary", onPrimary());
1380     config.writeEntry("byPassWM", byPassWM());
1381     config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts());
1382     config.writeEntry("viewType", (int)m_type);
1383     config.sync();
1384 }
1385 
1386 void View::restoreConfig()
1387 {
1388     if (!this->containment())
1389         return;
1390 
1391     auto config = this->containment()->config();
1392     m_onPrimary = config.readEntry("onPrimary", true);
1393     m_byPassWM = config.readEntry("byPassWM", false);
1394     m_isPreferredForShortcuts = config.readEntry("isPreferredForShortcuts", false);
1395 
1396     //! Send changed signals at the end in order to be sure that saveConfig
1397     //! wont rewrite default/invalid values
1398     emit onPrimaryChanged();
1399     emit byPassWMChanged();
1400 }
1401 //!END configuration functions
1402 
1403 }
1404 //!END namespace