File indexing completed on 2024-04-21 16:17:17
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