File indexing completed on 2024-04-21 16:30:33
0001 // SPDX-License-Identifier: GPL-3.0-or-later 0002 /* 0003 Copyright 2017 - 2023 Martin Koller, kollix@aon.at 0004 0005 This file is part of liquidshell. 0006 0007 liquidshell is free software: you can redistribute it and/or modify 0008 it under the terms of the GNU General Public License as published by 0009 the Free Software Foundation, either version 3 of the License, or 0010 (at your option) any later version. 0011 0012 liquidshell 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 liquidshell. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include <DesktopWidget.hxx> 0022 #include <DesktopPanel.hxx> 0023 #include <WeatherApplet.hxx> 0024 #include <DiskUsageApplet.hxx> 0025 #include <PictureFrameApplet.hxx> 0026 #include <ConfigureDesktopDialog.hxx> 0027 #include <OnScreenVolume.hxx> 0028 #include <OnScreenBrightness.hxx> 0029 #include <KWinCompat.hxx> 0030 0031 #include <QApplication> 0032 #include <QScreen> 0033 #include <QPainter> 0034 #include <QAction> 0035 #include <QIcon> 0036 #include <QMenu> 0037 #include <QCursor> 0038 #include <QDBusConnection> 0039 #include <QDBusMessage> 0040 0041 #include <KConfig> 0042 #include <KConfigGroup> 0043 #include <KLocalizedString> 0044 #include <KCMultiDialog> 0045 #include <KHelpMenu> 0046 #include <kcmutils_version.h> 0047 0048 //-------------------------------------------------------------------------------- 0049 0050 int DesktopWidget::appletNum = 0; 0051 DesktopWidget *DesktopWidget::instance = nullptr; 0052 0053 //-------------------------------------------------------------------------------- 0054 0055 DesktopWidget::DesktopWidget() 0056 : QWidget(nullptr) 0057 // would love to add Qt::WindowDoesNotAcceptFocus, but this leads to global shortcuts 0058 // no longer working when last maximized firefox window is closed on an empty desktop 0059 { 0060 instance = this; 0061 0062 setAttribute(Qt::WA_AlwaysShowToolTips); 0063 setAutoFillBackground(true); 0064 setFixedSize(QApplication::primaryScreen()->virtualSize()); 0065 setWindowIcon(QIcon::fromTheme("liquidshell")); 0066 0067 KWindowSystem::setType(winId(), NET::Desktop); 0068 0069 panel = new DesktopPanel(nullptr); 0070 panel->show(); 0071 connect(panel, &DesktopPanel::resized, this, &DesktopWidget::placePanel); 0072 placePanel(); 0073 0074 connect(KWinCompat::self(), &KWinCompat::currentDesktopChanged, this, &DesktopWidget::desktopChanged); 0075 connect(KWinCompat::self(), &KWinCompat::numberOfDesktopsChanged, this, &DesktopWidget::loadSettings); 0076 0077 setContextMenuPolicy(Qt::ActionsContextMenu); 0078 QAction *action = new QAction(QIcon::fromTheme("configure"), i18n("Configure Wallpaper..."), this); 0079 connect(action, &QAction::triggered, this, &DesktopWidget::configureWallpaper); 0080 addAction(action); 0081 0082 action = new QAction(i18n("Add Applet"), this); 0083 addAction(action); 0084 QMenu *menu = new QMenu; 0085 action->setMenu(menu); 0086 action = menu->addAction(QIcon::fromTheme("weather-clouds"), i18n("Weather")); 0087 connect(action, &QAction::triggered, [this]() { addApplet("Weather"); }); 0088 0089 action = menu->addAction(QIcon::fromTheme("drive-harddisk"), i18n("Disk Usage")); 0090 connect(action, &QAction::triggered, [this]() { addApplet("DiskUsage"); }); 0091 0092 action = menu->addAction(QIcon::fromTheme("image-x-generix"), i18n("Picture Frame")); 0093 connect(action, &QAction::triggered, [this]() { addApplet("PictureFrame"); }); 0094 0095 action = new QAction(QIcon::fromTheme("preferences-desktop-display"), i18n("Configure Display..."), this); 0096 connect(action, &QAction::triggered, this, &DesktopWidget::configureDisplay); 0097 addAction(action); 0098 0099 action = new QAction(QIcon::fromTheme("system-run"), i18n("Run Command..."), this); 0100 connect(action, &QAction::triggered, 0101 []() 0102 { 0103 QDBusConnection::sessionBus().send( 0104 QDBusMessage::createMethodCall("org.kde.krunner", "/App", 0105 "org.kde.krunner.App", "display")); 0106 }); 0107 addAction(action); 0108 0109 action = new QAction(i18n("Help"), this); 0110 KHelpMenu *helpMenu = new KHelpMenu(this); 0111 helpMenu->action(KHelpMenu::menuHelpContents)->setVisible(false); // no handbook 0112 helpMenu->action(KHelpMenu::menuWhatsThis)->setVisible(false); 0113 action->setMenu(helpMenu->menu()); 0114 addAction(action); 0115 0116 // restore applets 0117 KConfig config; 0118 KConfigGroup group = config.group("DesktopApplets"); 0119 QStringList appletNames = group.readEntry("applets", QStringList()); 0120 for (const QString &appletName : appletNames) 0121 { 0122 DesktopApplet *applet = nullptr; 0123 0124 if ( appletName.startsWith("Weather_") ) 0125 applet = new WeatherApplet(this, appletName); 0126 else if ( appletName.startsWith("DiskUsage_") ) 0127 applet = new DiskUsageApplet(this, appletName); 0128 else if ( appletName.startsWith("PictureFrame_") ) 0129 applet = new PictureFrameApplet(this, appletName); 0130 0131 if ( applet ) 0132 { 0133 int num = appletName.mid(appletName.indexOf('_') + 1).toInt(); 0134 if ( num > appletNum ) 0135 appletNum = num; 0136 0137 applet->loadConfig(); 0138 applets.append(applet); 0139 connect(applet, &DesktopApplet::removeThis, this, &DesktopWidget::removeApplet); 0140 } 0141 } 0142 0143 loadSettings(); 0144 0145 connect(qApp, &QApplication::primaryScreenChanged, this, &DesktopWidget::placePanel); 0146 0147 connect(qApp, &QApplication::screenAdded, 0148 [this]() { setFixedSize(QApplication::primaryScreen()->virtualSize()); desktopChanged(); }); 0149 0150 connect(qApp, &QApplication::screenRemoved, 0151 [this]() { setFixedSize(QApplication::primaryScreen()->virtualSize()); desktopChanged(); }); 0152 0153 connect(QApplication::primaryScreen(), &QScreen::virtualGeometryChanged, 0154 [this]() { setFixedSize(QApplication::primaryScreen()->virtualSize()); placePanel(); desktopChanged(); }); 0155 0156 new OnScreenVolume(this); 0157 new OnScreenBrightness(this); 0158 } 0159 0160 //-------------------------------------------------------------------------------- 0161 0162 void DesktopWidget::loadSettings() 0163 { 0164 // simple check for default files matching current desktop size 0165 QStringList dirNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, 0166 "wallpapers", QStandardPaths::LocateDirectory); 0167 0168 QStringList defaultFiles; 0169 const QString geometryString = QString("%1x%2").arg(width()).arg(height()); 0170 for (const QString &dirName : dirNames) 0171 { 0172 for (const QString &subdir : QDir(dirName).entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable)) 0173 { 0174 QDir dir(dirName + '/' + subdir + "/contents/images"); 0175 for (const QString &fileName : dir.entryList(QDir::Files | QDir::Readable)) 0176 { 0177 if ( fileName.startsWith(geometryString) ) 0178 defaultFiles.append(dir.absoluteFilePath(fileName)); 0179 } 0180 } 0181 } 0182 0183 KConfig config; 0184 for (int i = wallpapers.count() + 1; i <= KWinCompat::numberOfDesktops(); i++) 0185 { 0186 KConfigGroup group = config.group(QString("Desktop_%1").arg(i)); 0187 0188 Wallpaper wallpaper; 0189 0190 wallpaper.color = group.readEntry("color", QColor(Qt::black)); 0191 wallpaper.mode = group.readEntry("wallpaperMode", QString()); 0192 0193 int idx = defaultFiles.count() ? ((i - 1) % defaultFiles.count()) : -1; 0194 wallpaper.fileName = group.readEntry("wallpaper", (idx != -1) ? defaultFiles[idx] : QString()); 0195 0196 wallpapers.append(wallpaper); 0197 } 0198 0199 wallpapers.resize(KWinCompat::numberOfDesktops()); // if we had more before, reduce size 0200 wallpapers.squeeze(); 0201 0202 desktopChanged(); 0203 } 0204 0205 //-------------------------------------------------------------------------------- 0206 0207 void DesktopWidget::configureWallpaper() 0208 { 0209 if ( dialog ) // already open, do nothing 0210 return; 0211 0212 bool showingDesktop = KWindowSystem::showingDesktop(); 0213 KWindowSystem::setShowingDesktop(true); 0214 0215 int desktopNum = currentDesktop; 0216 Wallpaper origWallpaper = wallpapers[desktopNum]; 0217 0218 dialog = new ConfigureDesktopDialog(nullptr, wallpapers[desktopNum]); 0219 0220 connect(dialog, &ConfigureDesktopDialog::changed, 0221 [=]() { wallpapers[desktopNum] = dialog->getWallpaper(); desktopChanged(); }); 0222 0223 connect(dialog, &QDialog::finished, 0224 [=](int result) 0225 { 0226 if ( result == QDialog::Rejected ) 0227 { 0228 wallpapers[desktopNum] = origWallpaper; 0229 desktopChanged(); 0230 } 0231 else // store in config file 0232 { 0233 const Wallpaper &wallpaper = wallpapers[desktopNum]; 0234 KConfig config; 0235 KConfigGroup group = config.group(QString("Desktop_%1").arg(desktopNum + 1)); 0236 group.writeEntry("color", wallpaper.color); 0237 group.writeEntry("wallpaper", wallpaper.fileName); 0238 group.writeEntry("wallpaperMode", wallpaper.mode); 0239 } 0240 KWindowSystem::setShowingDesktop(showingDesktop); // restore 0241 dialog->deleteLater(); 0242 dialog = nullptr; 0243 }); 0244 0245 dialog->show(); // not modal 0246 } 0247 0248 //-------------------------------------------------------------------------------- 0249 0250 void DesktopWidget::configureDisplay() 0251 { 0252 KCMultiDialog *dialog = new KCMultiDialog(this); 0253 dialog->setAttribute(Qt::WA_DeleteOnClose); 0254 0255 // different KDE versions need different ways ... 0256 KPluginMetaData module("plasma/kcms/systemsettings/kcm_kscreen"); 0257 if ( module.isValid() ) 0258 dialog->addModule(module); 0259 else 0260 dialog->addModule("kcm_kscreen"); 0261 0262 dialog->adjustSize(); 0263 dialog->setWindowTitle(i18n("Configure Display")); 0264 dialog->show(); // not modal 0265 } 0266 0267 //-------------------------------------------------------------------------------- 0268 0269 void DesktopWidget::placePanel() 0270 { 0271 int panelHeight = qMin(panel->sizeHint().height(), panel->height()); 0272 QRect r = QApplication::primaryScreen()->geometry(); 0273 QSize vs = QApplication::primaryScreen()->virtualSize(); 0274 panel->setGeometry(r.x(), r.y() + r.height() - panelHeight, r.width(), panelHeight); 0275 0276 // struts are to the combined screen geoms, not the single screen 0277 KWinCompat::setStrut(panel->winId(), 0, 0, 0, panelHeight + vs.height() - r.bottom()); 0278 } 0279 0280 //-------------------------------------------------------------------------------- 0281 0282 QRect DesktopWidget::availableGeometry() 0283 { 0284 QRect geom = QApplication::primaryScreen()->geometry(); 0285 geom.setHeight(geom.height() - instance->panel->height()); 0286 return geom; 0287 } 0288 0289 //-------------------------------------------------------------------------------- 0290 0291 QSize DesktopWidget::availableSize() 0292 { 0293 return availableGeometry().size(); 0294 } 0295 0296 //-------------------------------------------------------------------------------- 0297 0298 void DesktopWidget::desktopChanged() 0299 { 0300 // free memory in previous shown desktop 0301 if ( (currentDesktop >= 0) && (currentDesktop < wallpapers.count()) ) 0302 wallpapers[currentDesktop].pixmaps.clear(); 0303 0304 currentDesktop = KWinCompat::currentDesktop() - 1; // num to index 0305 0306 if ( currentDesktop >= wallpapers.count() ) 0307 return; 0308 0309 Wallpaper &wallpaper = wallpapers[currentDesktop]; 0310 0311 if ( wallpaper.color.isValid() ) 0312 { 0313 QPalette pal = palette(); 0314 pal.setColor(backgroundRole(), wallpaper.color); 0315 setPalette(pal); 0316 } 0317 0318 if ( !wallpaper.fileName.isEmpty() ) 0319 { 0320 QPixmap pixmap(wallpaper.fileName); 0321 0322 if ( !pixmap.isNull() ) 0323 { 0324 for (int i = 0; i < QApplication::screens().count(); i++) 0325 { 0326 QRect r = QApplication::screens().at(i)->geometry(); 0327 QPixmap scaledPixmap = pixmap; 0328 0329 if ( wallpaper.mode == "Scaled" ) 0330 scaledPixmap = pixmap.scaled(r.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0331 else if ( wallpaper.mode == "ScaledKeepRatio" ) 0332 scaledPixmap = pixmap.scaled(r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); 0333 else if ( wallpaper.mode == "ScaledKeepRatioExpand" ) 0334 scaledPixmap = pixmap.scaled(r.size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); 0335 0336 wallpaper.pixmaps.append(scaledPixmap); 0337 } 0338 } 0339 } 0340 0341 update(); 0342 0343 // show applets for new desktop 0344 for (DesktopApplet *applet : applets) 0345 { 0346 if ( !applet->isConfiguring() ) 0347 applet->setVisible(applet->isOnDesktop(currentDesktop + 1)); 0348 } 0349 } 0350 0351 //-------------------------------------------------------------------------------- 0352 0353 void DesktopWidget::paintEvent(QPaintEvent *event) 0354 { 0355 Q_UNUSED(event) 0356 0357 if ( currentDesktop < wallpapers.count() ) 0358 { 0359 Wallpaper &wallpaper = wallpapers[currentDesktop]; 0360 0361 QPainter painter(this); 0362 0363 for (int i = 0; i < QApplication::screens().count(); i++) 0364 { 0365 if ( i < wallpaper.pixmaps.count() ) 0366 { 0367 QRect r = QApplication::screens().at(i)->geometry(); 0368 painter.setClipRect(r); 0369 0370 painter.drawPixmap(r.x() + (r.width() - wallpaper.pixmaps[i].width()) / 2, 0371 r.y() + (r.height() - wallpaper.pixmaps[i].height()) / 2, 0372 wallpaper.pixmaps[i]); 0373 } 0374 } 0375 } 0376 } 0377 0378 //-------------------------------------------------------------------------------- 0379 0380 void DesktopWidget::addApplet(const QString &type) 0381 { 0382 DesktopApplet *applet = nullptr; 0383 0384 if ( type == "Weather" ) 0385 { 0386 applet = new WeatherApplet(this, QString("%1_%2").arg(type).arg(++appletNum)); 0387 } 0388 else if ( type == "DiskUsage" ) 0389 { 0390 applet = new DiskUsageApplet(this, QString("%1_%2").arg(type).arg(++appletNum)); 0391 } 0392 else if ( type == "PictureFrame" ) 0393 { 0394 applet = new PictureFrameApplet(this, QString("%1_%2").arg(type).arg(++appletNum)); 0395 } 0396 0397 if ( applet ) 0398 { 0399 applets.append(applet); 0400 applet->loadConfig(); // defaults + size 0401 applet->move(QCursor::pos()); 0402 applet->saveConfig(); 0403 applet->show(); 0404 connect(applet, &DesktopApplet::removeThis, this, &DesktopWidget::removeApplet); 0405 } 0406 0407 saveAppletsList(); 0408 } 0409 0410 //-------------------------------------------------------------------------------- 0411 0412 void DesktopWidget::removeApplet(DesktopApplet *applet) 0413 { 0414 applets.removeOne(applet); 0415 applet->deleteLater(); 0416 saveAppletsList(); 0417 } 0418 0419 //-------------------------------------------------------------------------------- 0420 0421 void DesktopWidget::saveAppletsList() 0422 { 0423 KConfig config; 0424 KConfigGroup group = config.group("DesktopApplets"); 0425 0426 QStringList appletNames; 0427 for (DesktopApplet *applet : applets) 0428 appletNames.append(applet->getId()); 0429 0430 group.writeEntry("applets", appletNames); 0431 } 0432 0433 //--------------------------------------------------------------------------------