diff --git a/AppMenu.cxx b/AppMenu.cxx
index 546ebb0..86431d1 100644
--- a/AppMenu.cxx
+++ b/AppMenu.cxx
@@ -1,204 +1,189 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
-#include
-#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
AppMenu::AppMenu(DesktopPanel *parent)
: Launcher(parent, "AppMenu")
{
button = new QToolButton; // QToolButton is smaller than QPushButton
adjustIconSize();
button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
- popup = new Menu(button);
connect(button, &QToolButton::pressed, this, &AppMenu::showMenu);
layout()->addWidget(button);
loadConfig(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
connect(parent, &DesktopPanel::rowsChanged, this, &AppMenu::adjustIconSize);
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &AppMenu::adjustIconSize);
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &AppMenu::fill);
- connect(QApplication::primaryScreen(), &QScreen::geometryChanged, this, &AppMenu::fill);
- connect(qApp, &QApplication::primaryScreenChanged, this, &AppMenu::fill);
}
//--------------------------------------------------------------------------------
void AppMenu::adjustIconSize()
{
const int MAX_ROWS = qobject_cast(parentWidget())->getRows();
if ( MAX_ROWS > 1 )
button->setIconSize(QSize(48, 48));
else
{
int size = KIconLoader::global()->currentSize(KIconLoader::Panel);
button->setIconSize(QSize(size, size));
}
}
//--------------------------------------------------------------------------------
void AppMenu::fill()
{
- if ( QApplication::primaryScreen()->availableSize().height() == QApplication::primaryScreen()->size().height() )
- {
- // when the desktop panel size was not set yet, delay the creation
- QTimer::singleShot(0, this, &AppMenu::fill);
- return;
- }
-
KDesktopFile desktopFile(dirPath + "/.directory");
if ( !desktopFile.readIcon().isEmpty() )
button->setIcon(QIcon::fromTheme(desktopFile.readIcon()));
else if ( dirPath == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) )
button->setIcon(QIcon::fromTheme("user-desktop"));
else // fallback
button->setIcon(QIcon::fromTheme("folder"));
+}
- QLayoutItem *child;
- while ( (child = popup->layout()->takeAt(0)) )
- {
- delete child->widget();
- delete child;
- }
+//--------------------------------------------------------------------------------
+
+void AppMenu::showMenu()
+{
+ Menu *popup = new Menu(button);
QDir dir(dirPath);
QFileInfoList entries = dir.entryInfoList(QDir::AllEntries | QDir::NoDotDot);
int row = 0, col = 0, h = 0;
- const int maxHeight = QApplication::primaryScreen()->availableSize().height();
+ const int maxHeight = DesktopWidget::availableSize().height();
for (const QFileInfo &info : entries)
{
QUrl url(QUrl::fromLocalFile(info.absoluteFilePath()));
QString name;
QIcon icon;
KFileItem item(url);
if ( item.isDesktopFile() )
{
KDesktopFile desktopFile(info.absoluteFilePath());
if ( desktopFile.noDisplay() )
continue;
name = desktopFile.readName();
if ( name.isEmpty() )
name = desktopFile.readGenericName();
QString iconName = desktopFile.readIcon();
icon = QIcon::fromTheme(iconName.isEmpty() ? name : iconName);
}
else if ( info.isDir() )
{
if ( info.fileName() == "." )
{
name = info.dir().dirName();
icon = button->icon();
}
else
icon = QIcon::fromTheme("folder");
}
else
{
QMimeDatabase db;
icon = QIcon::fromTheme(db.mimeTypeForFile(info.absoluteFilePath()).iconName());
}
if ( name.isEmpty() )
name = info.fileName();
IconButton *entryButton = new IconButton(this, icon, 32, name);
- connect(entryButton, &IconButton::clicked, [this, url]() { popup->close(); new KRun(url, nullptr); });
+ connect(entryButton, &IconButton::clicked, [this, url, popup]() { popup->close(); new KRun(url, nullptr); });
h += entryButton->sizeHint().height();
if ( h >= maxHeight )
{
h = entryButton->sizeHint().height();
col++;
row = 0;
}
static_cast(popup->layout())->addWidget(entryButton, row++, col);
}
-}
-//--------------------------------------------------------------------------------
-
-void AppMenu::showMenu()
-{
popup->exec();
button->setDown(false);
+ delete popup;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
Menu::Menu(QWidget *parent)
: QMenu(parent)
{
QGridLayout *grid = new QGridLayout(this);
grid->setContentsMargins(QMargins());
grid->setSpacing(0);
}
//--------------------------------------------------------------------------------
void Menu::hideEvent(QHideEvent *event)
{
Q_UNUSED(event);
eventLoop.exit();
}
//--------------------------------------------------------------------------------
void Menu::exec()
{
adjustSize();
QPoint p = parentWidget()->mapToGlobal(QPoint(0, 0));
move(p.x(), p.y() - height());
show();
eventLoop.exec();
}
//--------------------------------------------------------------------------------
diff --git a/AppMenu.hxx b/AppMenu.hxx
index 9fd30bf..c426962 100644
--- a/AppMenu.hxx
+++ b/AppMenu.hxx
@@ -1,71 +1,70 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#ifndef _AppMenu_H_
#define _AppMenu_H_
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
class AppMenu : public Launcher
{
Q_OBJECT
public:
AppMenu(DesktopPanel *parent);
private Q_SLOTS:
void adjustIconSize();
void fill() override;
void showMenu();
private:
QToolButton *button;
- class Menu *popup;
};
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
class Menu : public QMenu
{
Q_OBJECT
public:
Menu(QWidget *parent);
void exec();
protected:
void hideEvent(QHideEvent *event) override;
private:
QEventLoop eventLoop;
};
//--------------------------------------------------------------------------------
#endif
diff --git a/ClockWidget.cxx b/ClockWidget.cxx
index a1e5c27..250f3cd 100644
--- a/ClockWidget.cxx
+++ b/ClockWidget.cxx
@@ -1,248 +1,249 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 - 2019 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
CalendarPopup::CalendarPopup(QWidget *parent)
: QFrame(parent)
{
setWindowFlags(windowFlags() | Qt::Popup);
setFrameShape(QFrame::StyledPanel);
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setContentsMargins(QMargins());
cal = new QCalendarWidget;
vbox->addWidget(cal);
QPushButton *today = new QPushButton(QIcon::fromTheme("go-jump-today"), QString());
vbox->addWidget(today);
connect(today, &QPushButton::clicked, this, &CalendarPopup::goToday);
}
//--------------------------------------------------------------------------------
void CalendarPopup::goToday()
{
cal->showToday();
cal->setSelectedDate(QDate::currentDate());
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
ClockWidget::ClockWidget(DesktopPanel *parent)
: QFrame(parent), calendar(nullptr)
{
ensurePolished(); // make sure we already have the css applied
timer = new QTimer(this);
// if seconds are shown, update every second, else less often
if ( timeFormat.contains('s') )
timer->setInterval(1000);
else
timer->setInterval(5000);
timer->start();
connect(timer, &QTimer::timeout, this, &ClockWidget::tick);
connect(parent, &DesktopPanel::rowsChanged, this, &ClockWidget::fill);
timeLabel = new QLabel(this);
dayLabel = new QLabel(this);
dateLabel = new QLabel(this);
timeLabel->setObjectName("time");
dayLabel->setObjectName("day");
dateLabel->setObjectName("date");
timeLabel->setTextFormat(Qt::PlainText);
dayLabel->setTextFormat(Qt::PlainText);
dateLabel->setTextFormat(Qt::PlainText);
timeLabel->setAlignment(Qt::AlignCenter);
dayLabel->setAlignment(Qt::AlignCenter);
dateLabel->setAlignment(Qt::AlignCenter);
QFont f = font();
f.setPointSizeF(fontInfo().pointSizeF() * 1.5);
f.setBold(true);
timeLabel->setFont(f);
fill();
timeLabel->setVisible(!timeFormat.isEmpty());
dayLabel->setVisible(!dayFormat.isEmpty());
dateLabel->setVisible(!dateFormat.isEmpty());
// context menu
QAction *action = new QAction(this);
action->setIcon(QIcon::fromTheme("configure"));
action->setText(i18n("Select Timezones..."));
addAction(action);
connect(action, &QAction::triggered,
[this]()
{
ClockWidgetConfigureDialog dialog(parentWidget(), timeZoneIds);
dialog.setWindowTitle(i18n("Select Timezones"));
dialog.resize(600, 400);
if ( dialog.exec() == QDialog::Accepted )
{
timeZoneIds = dialog.getSelectedTimeZoneIds();
KConfig config;
KConfigGroup group = config.group("ClockWidget");
QStringList list;
for (const QByteArray &id : timeZoneIds)
list.append(id);
group.writeEntry("timeZoneIds", list);
tick(); // update tooltip
}
}
);
action = new QAction(this);
action->setIcon(QIcon::fromTheme("preferences-system-time"));
action->setText(i18n("Configure Date & Time..."));
addAction(action);
connect(action, &QAction::triggered,
[this]()
{
auto dialog = new KCMultiDialog(parentWidget());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowTitle(i18n("Date & Time"));
dialog->addModule("clock");
dialog->adjustSize();
dialog->show();
}
);
setContextMenuPolicy(Qt::ActionsContextMenu);
// load config
KConfig config;
KConfigGroup group = config.group("ClockWidget");
QStringList list;
list = group.readEntry("timeZoneIds", QStringList());
for (const QString &id : list)
timeZoneIds.append(id.toLatin1());
tick();
timeLabel->setFixedHeight(timeLabel->fontMetrics().tightBoundingRect(timeLabel->text()).height() + 2);
dayLabel->setFixedHeight(dayLabel->fontMetrics().tightBoundingRect(dayLabel->text()).height() + 4);
dateLabel->setFixedHeight(dateLabel->fontMetrics().tightBoundingRect(dateLabel->text()).height() + 4);
}
//--------------------------------------------------------------------------------
void ClockWidget::fill()
{
delete layout();
const int MAX_ROWS = qobject_cast(parentWidget())->getRows();
QBoxLayout *box;
if ( MAX_ROWS >= 2 )
{
box = new QVBoxLayout(this);
box->setSpacing(0);
}
else
{
box = new QHBoxLayout(this);
}
box->setContentsMargins(QMargins());
box->addWidget(timeLabel);
box->addWidget(dayLabel);
box->addWidget(dateLabel);
}
//--------------------------------------------------------------------------------
void ClockWidget::tick()
{
QDateTime dateTimeUtc = QDateTime::currentDateTimeUtc();
QDateTime dateTime = dateTimeUtc.toLocalTime();
timeLabel->setText(dateTime.time().toString(timeFormat));
dayLabel->setText(dateTime.date().toString(dayFormat));
dateLabel->setText(dateTime.date().toString(dateFormat));
if ( !timeZoneIds.isEmpty() )
{
QString tip = "";
for (const QByteArray &id : timeZoneIds)
{
QTimeZone timeZone(id);
QDateTime dt = dateTimeUtc;
dateTime = dt.toTimeZone(timeZone);
tip += QString("%1 | %2 | %3 | %4 |
")
.arg(QLatin1String(id))
.arg(dateTime.time().toString(timeFormat))
.arg(dateTime.date().toString(dayFormat))
.arg(dateTime.date().toString(dateFormat));
}
tip += "
";
setToolTip(tip);
}
}
//--------------------------------------------------------------------------------
void ClockWidget::mousePressEvent(QMouseEvent *event)
{
if ( event->button() == Qt::LeftButton )
{
if ( !calendar )
calendar = new CalendarPopup(this);
calendar->goToday();
QPoint point = mapToGlobal(pos());
- QRect screen = QApplication::primaryScreen()->availableGeometry();
+ QRect screen = DesktopWidget::availableGeometry();
point.setX(std::min(point.x(), screen.x() + screen.width() - calendar->sizeHint().width()));
point.setY(point.y() - calendar->sizeHint().height());
calendar->move(point);
calendar->show();
}
}
//--------------------------------------------------------------------------------
diff --git a/DesktopWidget.cxx b/DesktopWidget.cxx
index d5e8d97..4ec52de 100644
--- a/DesktopWidget.cxx
+++ b/DesktopWidget.cxx
@@ -1,399 +1,421 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 - 2019 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
int DesktopWidget::appletNum = 0;
+DesktopWidget *DesktopWidget::instance = nullptr;
//--------------------------------------------------------------------------------
DesktopWidget::DesktopWidget()
: QWidget(nullptr, Qt::WindowDoesNotAcceptFocus)
{
+ instance = this;
+
setAttribute(Qt::WA_AlwaysShowToolTips);
setAutoFillBackground(true);
setFixedSize(QApplication::primaryScreen()->virtualSize());
setWindowIcon(QIcon::fromTheme("liquidshell"));
KWindowSystem::setType(winId(), NET::Desktop);
panel = new DesktopPanel(nullptr);
panel->show();
connect(panel, &DesktopPanel::resized, this, &DesktopWidget::placePanel);
placePanel();
connect(KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, &DesktopWidget::desktopChanged);
connect(KWindowSystem::self(), &KWindowSystem::numberOfDesktopsChanged, this, &DesktopWidget::loadSettings);
setContextMenuPolicy(Qt::ActionsContextMenu);
QAction *action = new QAction(QIcon::fromTheme("configure"), i18n("Configure Wallpaper..."), this);
connect(action, &QAction::triggered, this, &DesktopWidget::configureWallpaper);
addAction(action);
action = new QAction(i18n("Add Applet"), this);
addAction(action);
QMenu *menu = new QMenu;
action->setMenu(menu);
action = menu->addAction(QIcon::fromTheme("weather-clouds"), i18n("Weather"));
connect(action, &QAction::triggered, [this]() { addApplet("Weather"); });
action = menu->addAction(QIcon::fromTheme("drive-harddisk"), i18n("Disk Usage"));
connect(action, &QAction::triggered, [this]() { addApplet("DiskUsage"); });
action = menu->addAction(QIcon::fromTheme("image-x-generix"), i18n("Picture Frame"));
connect(action, &QAction::triggered, [this]() { addApplet("PictureFrame"); });
action = new QAction(QIcon::fromTheme("preferences-desktop-display"), i18n("Configure Display..."), this);
connect(action, &QAction::triggered, this, &DesktopWidget::configureDisplay);
addAction(action);
action = new QAction(QIcon::fromTheme("system-run"), i18n("Run Command..."), this);
connect(action, &QAction::triggered,
[]()
{
QDBusConnection::sessionBus().send(
QDBusMessage::createMethodCall("org.kde.krunner", "/App",
"org.kde.krunner.App", "display"));
});
addAction(action);
action = new QAction(i18n("Help"), this);
KHelpMenu *helpMenu = new KHelpMenu(this);
helpMenu->action(KHelpMenu::menuHelpContents)->setVisible(false); // no handbook
helpMenu->action(KHelpMenu::menuWhatsThis)->setVisible(false);
action->setMenu(helpMenu->menu());
addAction(action);
// restore applets
KConfig config;
KConfigGroup group = config.group("DesktopApplets");
QStringList appletNames = group.readEntry("applets", QStringList());
for (const QString &appletName : appletNames)
{
DesktopApplet *applet = nullptr;
if ( appletName.startsWith("Weather_") )
applet = new WeatherApplet(this, appletName);
else if ( appletName.startsWith("DiskUsage_") )
applet = new DiskUsageApplet(this, appletName);
else if ( appletName.startsWith("PictureFrame_") )
applet = new PictureFrameApplet(this, appletName);
if ( applet )
{
int num = appletName.mid(appletName.indexOf('_') + 1).toInt();
if ( num > appletNum )
appletNum = num;
applet->loadConfig();
applets.append(applet);
connect(applet, &DesktopApplet::removeThis, this, &DesktopWidget::removeApplet);
}
}
loadSettings();
connect(qApp, &QApplication::primaryScreenChanged, this, &DesktopWidget::placePanel);
connect(qApp, &QApplication::screenAdded,
[this]() { setFixedSize(QApplication::primaryScreen()->virtualSize()); desktopChanged(); });
connect(qApp, &QApplication::screenRemoved,
[this]() { setFixedSize(QApplication::primaryScreen()->virtualSize()); desktopChanged(); });
connect(QApplication::primaryScreen(), &QScreen::virtualGeometryChanged,
[this]() { setFixedSize(QApplication::primaryScreen()->virtualSize()); placePanel(); desktopChanged(); });
new OnScreenVolume(this);
}
//--------------------------------------------------------------------------------
void DesktopWidget::loadSettings()
{
// simple check for default files matching current desktop size
QStringList dirNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
"wallpapers", QStandardPaths::LocateDirectory);
QStringList defaultFiles;
const QString geometryString = QString("%1x%2").arg(width()).arg(height());
for (const QString &dirName : dirNames)
{
for (const QString &subdir : QDir(dirName).entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable))
{
QDir dir(dirName + '/' + subdir + "/contents/images");
for (const QString &fileName : dir.entryList(QDir::Files | QDir::Readable))
{
if ( fileName.startsWith(geometryString) )
defaultFiles.append(dir.absoluteFilePath(fileName));
}
}
}
KConfig config;
for (int i = wallpapers.count() + 1; i <= KWindowSystem::numberOfDesktops(); i++)
{
KConfigGroup group = config.group(QString("Desktop_%1").arg(i));
Wallpaper wallpaper;
wallpaper.color = group.readEntry("color", QColor(Qt::black));
wallpaper.mode = group.readEntry("wallpaperMode", QString());
int idx = defaultFiles.count() ? ((i - 1) % defaultFiles.count()) : -1;
wallpaper.fileName = group.readEntry("wallpaper", (idx != -1) ? defaultFiles[idx] : QString());
wallpapers.append(wallpaper);
}
wallpapers.resize(KWindowSystem::numberOfDesktops()); // if we had more before, reduce size
wallpapers.squeeze();
desktopChanged();
}
//--------------------------------------------------------------------------------
void DesktopWidget::configureWallpaper()
{
if ( dialog ) // already open, do nothing
return;
bool showingDesktop = KWindowSystem::showingDesktop();
KWindowSystem::setShowingDesktop(true);
int desktopNum = currentDesktop;
Wallpaper origWallpaper = wallpapers[desktopNum];
dialog = new ConfigureDesktopDialog(nullptr, wallpapers[desktopNum]);
connect(dialog, &ConfigureDesktopDialog::changed,
[=]() { wallpapers[desktopNum] = dialog->getWallpaper(); desktopChanged(); });
connect(dialog, &QDialog::finished,
[=](int result)
{
if ( result == QDialog::Rejected )
{
wallpapers[desktopNum] = origWallpaper;
desktopChanged();
}
else // store in config file
{
const Wallpaper &wallpaper = wallpapers[desktopNum];
KConfig config;
KConfigGroup group = config.group(QString("Desktop_%1").arg(desktopNum + 1));
group.writeEntry("color", wallpaper.color);
group.writeEntry("wallpaper", wallpaper.fileName);
group.writeEntry("wallpaperMode", wallpaper.mode);
}
KWindowSystem::setShowingDesktop(showingDesktop); // restore
dialog->deleteLater();
dialog = nullptr;
});
dialog->show(); // not modal
}
//--------------------------------------------------------------------------------
void DesktopWidget::configureDisplay()
{
KCMultiDialog *dialog = new KCMultiDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->addModule("kcm_kscreen");
dialog->adjustSize();
dialog->setWindowTitle(i18n("Configure Display"));
dialog->show(); // not modal
}
//--------------------------------------------------------------------------------
void DesktopWidget::placePanel()
{
int panelHeight = qMin(panel->sizeHint().height(), panel->height());
QRect r = QApplication::primaryScreen()->geometry();
+ QSize vs = QApplication::primaryScreen()->virtualSize();
panel->setGeometry(r.x(), r.y() + r.height() - panelHeight, r.width(), panelHeight);
- KWindowSystem::setStrut(panel->winId(), 0, 0, 0, panelHeight);
+
+ // struts are to the combined screen geoms, not the single screen
+ KWindowSystem::setStrut(panel->winId(), 0, 0, 0, panelHeight + vs.height() - r.bottom());
+}
+
+//--------------------------------------------------------------------------------
+
+QRect DesktopWidget::availableGeometry()
+{
+ QRect geom = QApplication::primaryScreen()->geometry();
+ geom.setHeight(geom.height() - instance->panel->height());
+ return geom;
+}
+
+//--------------------------------------------------------------------------------
+
+QSize DesktopWidget::availableSize()
+{
+ return availableGeometry().size();
}
//--------------------------------------------------------------------------------
void DesktopWidget::desktopChanged()
{
// free memory in previous shown desktop
if ( (currentDesktop >= 0) && (currentDesktop < wallpapers.count()) )
wallpapers[currentDesktop].pixmaps.clear();
currentDesktop = KWindowSystem::currentDesktop() - 1; // num to index
if ( currentDesktop >= wallpapers.count() )
return;
Wallpaper &wallpaper = wallpapers[currentDesktop];
if ( wallpaper.color.isValid() )
{
QPalette pal = palette();
pal.setColor(backgroundRole(), wallpaper.color);
setPalette(pal);
}
if ( !wallpaper.fileName.isEmpty() )
{
QPixmap pixmap(wallpaper.fileName);
if ( !pixmap.isNull() )
{
for (int i = 0; i < QApplication::screens().count(); i++)
{
QRect r = QApplication::screens().at(i)->geometry();
QPixmap scaledPixmap = pixmap;
if ( wallpaper.mode == "Scaled" )
scaledPixmap = pixmap.scaled(r.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
else if ( wallpaper.mode == "ScaledKeepRatio" )
scaledPixmap = pixmap.scaled(r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
else if ( wallpaper.mode == "ScaledKeepRatioExpand" )
scaledPixmap = pixmap.scaled(r.size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
wallpaper.pixmaps.append(scaledPixmap);
}
}
}
update();
// show applets for new desktop
for (DesktopApplet *applet : applets)
{
if ( !applet->isConfiguring() )
applet->setVisible(applet->isOnDesktop(currentDesktop + 1));
}
}
//--------------------------------------------------------------------------------
void DesktopWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
if ( currentDesktop < wallpapers.count() )
{
Wallpaper &wallpaper = wallpapers[currentDesktop];
QPainter painter(this);
for (int i = 0; i < QApplication::screens().count(); i++)
{
if ( i < wallpaper.pixmaps.count() )
{
QRect r = QApplication::screens().at(i)->geometry();
painter.setClipRect(r);
painter.drawPixmap(r.x() + (r.width() - wallpaper.pixmaps[i].width()) / 2,
r.y() + (r.height() - wallpaper.pixmaps[i].height()) / 2,
wallpaper.pixmaps[i]);
}
}
}
}
//--------------------------------------------------------------------------------
void DesktopWidget::addApplet(const QString &type)
{
DesktopApplet *applet = nullptr;
if ( type == "Weather" )
{
applet = new WeatherApplet(this, QString("%1_%2").arg(type).arg(++appletNum));
}
else if ( type == "DiskUsage" )
{
applet = new DiskUsageApplet(this, QString("%1_%2").arg(type).arg(++appletNum));
}
else if ( type == "PictureFrame" )
{
applet = new PictureFrameApplet(this, QString("%1_%2").arg(type).arg(++appletNum));
}
if ( applet )
{
applets.append(applet);
applet->loadConfig(); // defaults + size
applet->move(QCursor::pos());
applet->saveConfig();
applet->show();
connect(applet, &DesktopApplet::removeThis, this, &DesktopWidget::removeApplet);
}
saveAppletsList();
}
//--------------------------------------------------------------------------------
void DesktopWidget::removeApplet(DesktopApplet *applet)
{
applets.removeOne(applet);
applet->deleteLater();
saveAppletsList();
}
//--------------------------------------------------------------------------------
void DesktopWidget::saveAppletsList()
{
KConfig config;
KConfigGroup group = config.group("DesktopApplets");
QStringList appletNames;
for (DesktopApplet *applet : applets)
appletNames.append(applet->getId());
group.writeEntry("applets", appletNames);
}
//--------------------------------------------------------------------------------
diff --git a/DesktopWidget.hxx b/DesktopWidget.hxx
index 8587dc4..b708faa 100644
--- a/DesktopWidget.hxx
+++ b/DesktopWidget.hxx
@@ -1,71 +1,77 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#ifndef DESKTOP_WIDGET_H
#define DESKTOP_WIDGET_H
#include
#include
#include
class DesktopPanel;
class DesktopApplet;
class ConfigureDesktopDialog;
class DesktopWidget : public QWidget
{
Q_OBJECT
public:
DesktopWidget();
struct Wallpaper
{
QString fileName, mode;
QColor color;
QVector pixmaps; // per screen
};
+ // since on X11 QScreen:available* methods do not deliver the true
+ // values (according to Qt doc: This is a limitation in X11 window manager specification.)
+ static QRect availableGeometry();
+ static QSize availableSize();
+
protected:
void paintEvent(QPaintEvent *event) override;
private Q_SLOTS:
void loadSettings();
void placePanel();
void desktopChanged();
void configureWallpaper();
void configureDisplay();
void addApplet(const QString &type);
void removeApplet(DesktopApplet *applet);
private:
void saveAppletsList();
private:
DesktopPanel *panel = nullptr;
ConfigureDesktopDialog *dialog = nullptr;
QVector wallpapers;
int currentDesktop = -1;
QVector applets;
static int appletNum;
+ static DesktopWidget *instance;
};
#endif
diff --git a/NetworkList.cxx b/NetworkList.cxx
index e9ea86b..34f293f 100644
--- a/NetworkList.cxx
+++ b/NetworkList.cxx
@@ -1,427 +1,430 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
NetworkButton::NetworkButton(NetworkManager::Connection::Ptr c, NetworkManager::Device::Ptr dev,
NetworkManager::AccessPoint::Ptr accessPoint)
: connection(c), device(dev)
{
setCheckable(true);
if ( connection )
{
for (const NetworkManager::ActiveConnection::Ptr &ac : NetworkManager::activeConnections())
{
if ( ac->uuid() == c->uuid() )
{
setChecked(true);
break;
}
}
}
else if ( accessPoint )
{
ssid = accessPoint->ssid();
rawSsid = accessPoint->rawSsid();
wpaFlags = accessPoint->wpaFlags();
}
connect(this, &NetworkButton::toggled, this, &NetworkButton::toggleNetworkStatus);
}
//--------------------------------------------------------------------------------
void NetworkButton::toggleNetworkStatus(bool on)
{
if ( on )
{
if ( !connection ) // no connection yet -> create one
{
// the connMap content was "reverse-engineered" by using qdbusviewer and the result of getting
// GetSettings of one of theSettings.Connection elements
NMVariantMapMap connMap;
QVariantMap map;
map.insert("id", ssid);
// ensure to not need root password by creating only for the current user
struct passwd *pwd = getpwuid(geteuid());
if ( pwd )
map.insert("permissions", QStringList(QString("user:") + QString::fromUtf8(pwd->pw_name)));
connMap.insert("connection", map);
QVariantMap wirelessMap;
wirelessMap.insert("ssid", rawSsid);
if ( wpaFlags )
{
wirelessMap.insert("security", "802-11-wireless-security");
QVariantMap security;
if ( wpaFlags & NetworkManager::AccessPoint::KeyMgmtPsk )
security.insert("key-mgmt", QString("wpa-psk"));
#if (NETWORKMANAGERQT_VERSION >= QT_VERSION_CHECK(5, 63, 0))
else if ( wpaFlags & NetworkManager::AccessPoint::KeyMgmtSAE )
security.insert("key-mgmt", QString("sae"));
#endif
else
{
// TODO: other types - find value names
}
connMap.insert("802-11-wireless-security", security);
}
connMap.insert("802-11-wireless", wirelessMap);
QDBusPendingReply call =
NetworkManager::addAndActivateConnection(connMap, device->uni(), QString());
/*
QDBusPendingCallWatcher *pendingCallWatcher = new QDBusPendingCallWatcher(call, this);
connect(pendingCallWatcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *w)
{
w->deleteLater();
QDBusPendingReply reply = *w;
qDebug() << reply.error();
}
);
*/
// without closing our popup, the user can not enter the password in the password dialog which appears
window()->close();
return;
}
switch ( connection->settings()->connectionType() )
{
case NetworkManager::ConnectionSettings::Wired:
{
NetworkManager::activateConnection(connection->path(), connection->settings()->interfaceName(), QString());
break;
}
case NetworkManager::ConnectionSettings::Wireless:
{
NetworkManager::activateConnection(connection->path(), device->uni(), QString());
break;
}
case NetworkManager::ConnectionSettings::Vpn:
{
NetworkManager::ActiveConnection::Ptr conn(NetworkManager::primaryConnection());
if ( conn && !conn->devices().isEmpty() )
NetworkManager::activateConnection(connection->path(), conn->devices()[0], QString());
break;
}
default: ; // TODO
}
}
else if ( connection )
{
for (const NetworkManager::ActiveConnection::Ptr &ac : NetworkManager::activeConnections())
{
if ( ac->uuid() == connection->uuid() )
{
NetworkManager::deactivateConnection(ac->path());
break;
}
}
}
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
NetworkList::NetworkList(QWidget *parent)
: QFrame(parent)
{
setWindowFlags(windowFlags() | Qt::Popup);
setFrameShape(QFrame::StyledPanel);
QVBoxLayout *vbox = new QVBoxLayout(this);
hbox = new QHBoxLayout;
vbox->addLayout(hbox);
network = new QToolButton;
network->setIcon(QIcon::fromTheme("network-wired"));
+ network->setIconSize(QSize(22, 22));
network->setToolTip(i18n("Enable Networking"));
network->setCheckable(true);
connect(network, &QToolButton::clicked, [](bool on) { NetworkManager::setNetworkingEnabled(on); });
connect(NetworkManager::notifier(), &NetworkManager::Notifier::networkingEnabledChanged, this, &NetworkList::statusUpdate);
hbox->addWidget(network);
wireless = new QToolButton;
wireless->setIcon(QIcon::fromTheme("network-wireless"));
+ wireless->setIconSize(QSize(22, 22));
wireless->setToolTip(i18n("Enable Wireless Networking"));
wireless->setCheckable(true);
connect(wireless, &QToolButton::clicked, [](bool on) { NetworkManager::setWirelessEnabled(on); });
connect(NetworkManager::notifier(), &NetworkManager::Notifier::wirelessEnabledChanged, this, &NetworkList::statusUpdate);
hbox->addWidget(wireless);
statusUpdate();
hbox->addStretch();
QToolButton *configure = new QToolButton;
configure->setIcon(QIcon::fromTheme("configure"));
+ configure->setIconSize(QSize(22, 22));
configure->setToolTip(i18n("Configure Network Connections"));
connect(configure, &QToolButton::clicked, this, &NetworkList::openConfigureDialog);
hbox->addWidget(configure);
// show connections
QWidget *widget = new QWidget;
connectionsVbox = new QVBoxLayout(widget);
connectionsVbox->setContentsMargins(QMargins());
connectionsVbox->setSizeConstraint(QLayout::SetMinAndMaxSize);
scroll = new QScrollArea;
scroll->setWidgetResizable(true);
scroll->setWidget(widget);
vbox->addWidget(scroll);
fillConnections();
QTimer *checkConnectionsTimer = new QTimer(this);
checkConnectionsTimer->setInterval(1000);
connect(checkConnectionsTimer, &QTimer::timeout, this, &NetworkList::fillConnections);
checkConnectionsTimer->start();
}
//--------------------------------------------------------------------------------
void NetworkList::openConfigureDialog()
{
// newer plasma has already a KCM
KService::Ptr service = KService::serviceByDesktopName("kcm_networkmanagement");
if ( service )
KRun::runApplication(*service, QList(), this);
else
KRun::run("kde5-nm-connection-editor", QList(), this);
close();
}
//--------------------------------------------------------------------------------
void NetworkList::statusUpdate()
{
network->setChecked(NetworkManager::isNetworkingEnabled());
wireless->setChecked(NetworkManager::isWirelessEnabled());
}
//--------------------------------------------------------------------------------
void NetworkList::fillConnections()
{
if ( underMouse() ) // avoid to delete the widget we are possibly hovering over
return;
QLayoutItem *child;
while ( (child = connectionsVbox->takeAt(0)) )
{
delete child->widget();
delete child;
}
NetworkManager::Connection::List allConnections = NetworkManager::listConnections();
// show VPN networks on top
for (const NetworkManager::Connection::Ptr c : allConnections)
{
if ( !c->isValid() )
continue;
if ( c->settings()->connectionType() == NetworkManager::ConnectionSettings::Vpn )
{
NetworkButton *vpn = new NetworkButton(c);
vpn->setText(c->name());
vpn->setIcon(QIcon::fromTheme("security-high"));
connectionsVbox->addWidget(vpn);
vpn->show();
}
}
// wired networks
for (const NetworkManager::Connection::Ptr c : allConnections)
{
if ( !c->isValid() )
continue;
if ( (c->settings()->connectionType() == NetworkManager::ConnectionSettings::Wired) &&
!c->uuid().isEmpty() )
{
NetworkButton *net = new NetworkButton(c);
net->setText(c->name());
net->setIcon(QIcon::fromTheme("network-wired"));
connectionsVbox->addWidget(net);
net->show();
}
}
// show available wifi networks
if ( NetworkManager::isWirelessEnabled() )
{
for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces())
{
if ( device->type() != NetworkManager::Device::Wifi )
continue;
NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast();
for (const NetworkManager::WirelessNetwork::Ptr &network : wifiDevice->networks())
{
NetworkManager::AccessPoint::Ptr accessPoint = network->referenceAccessPoint();
if ( !accessPoint )
continue;
// check if we have a connection for this SSID
NetworkManager::Connection::Ptr conn;
for (const NetworkManager::Connection::Ptr c : allConnections)
{
if ( c->isValid() && (c->settings()->connectionType() == NetworkManager::ConnectionSettings::Wireless) )
{
NetworkManager::Setting::Ptr setting = c->settings()->setting(NetworkManager::Setting::Wireless);
NetworkManager::WirelessSetting::Ptr s = setting.staticCast();
if ( s->ssid() == network->ssid() )
{
conn = c;
break;
}
}
}
NetworkButton *net;
if ( conn )
{
net = new NetworkButton(conn, device);
net->setText(QString("%1 (%2%)").arg(conn->name()).arg(network->signalStrength()));
}
else
{
net = new NetworkButton(conn, device, accessPoint);
net->setText(QString("%1 (%2%)").arg(network->ssid()).arg(network->signalStrength()));
}
net->setIcon(QIcon::fromTheme("network-wireless"));
if ( accessPoint->wpaFlags() )
net->setIcon2(QIcon::fromTheme("object-locked"));
else
net->setIcon2(QIcon::fromTheme("object-unlocked"));
connectionsVbox->addWidget(net);
net->show();
}
}
}
#if 0
// TEST
static int count = 15;
for (int i = 0; i < count; i++)
{
NetworkButton *net = new NetworkButton();
net->setText(QString("dummy %1").arg(i));
net->setIcon(QIcon::fromTheme("network-wired"));
connectionsVbox->addWidget(net);
net->show();
}
count -= 3;
if ( count <= 0 ) count = 15;
#endif
connectionsVbox->addStretch();
adjustSize();
emit changed();
}
//--------------------------------------------------------------------------------
QSize NetworkList::sizeHint() const
{
QWidget *w = scroll->widget();
QSize s;
s.setHeight(frameWidth() +
contentsMargins().top() +
layout()->contentsMargins().top() +
hbox->sizeHint().height() +
((layout()->spacing() == -1) ? style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing) : layout()->spacing()) +
scroll->frameWidth() +
scroll->contentsMargins().top() +
w->sizeHint().height() +
scroll->contentsMargins().bottom() +
scroll->frameWidth() +
layout()->contentsMargins().bottom() +
contentsMargins().bottom() +
frameWidth()
);
s.setWidth(frameWidth() +
contentsMargins().left() +
layout()->contentsMargins().left() +
scroll->frameWidth() +
scroll->contentsMargins().left() +
w->sizeHint().width() +
scroll->verticalScrollBar()->sizeHint().width() +
scroll->contentsMargins().right() +
scroll->frameWidth() +
layout()->contentsMargins().right() +
contentsMargins().right() +
frameWidth()
);
return s;
}
//--------------------------------------------------------------------------------
diff --git a/NotificationList.cxx b/NotificationList.cxx
index 828660b..15526d4 100644
--- a/NotificationList.cxx
+++ b/NotificationList.cxx
@@ -1,332 +1,333 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 - 2019 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const Qt::WindowFlags POPUP_FLAGS = Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint;
//--------------------------------------------------------------------------------
NotifyItem::NotifyItem(QWidget *parent, uint theId, const QString &app,
const QString &theSummary, const QString &theBody, const QIcon &icon,
const QStringList &theActions)
: QFrame(parent, POPUP_FLAGS), id(theId), appName(app), summary(theSummary), body(theBody), actions(theActions)
{
setAttribute(Qt::WA_ShowWithoutActivating); // avoid focus stealing
setFrameShape(QFrame::StyledPanel);
QMargins margins = contentsMargins();
margins.setRight(0); // allow the close button to reach to the right screen border - easier to click
setContentsMargins(margins);
QVBoxLayout *vbox = new QVBoxLayout;
timeLabel = new QLabel;
iconLabel = new QLabel;
vbox->addWidget(timeLabel, 0, Qt::AlignTop | Qt::AlignHCenter);
vbox->addWidget(iconLabel, 0, Qt::AlignTop | Qt::AlignHCenter);
QVBoxLayout *centerBox = new QVBoxLayout;
QHBoxLayout *hbox = new QHBoxLayout(this);
margins = hbox->contentsMargins();
margins.setRight(0); // allow the close button to reach to the right screen border - easier to click
hbox->setContentsMargins(margins);
textLabel = new QLabel;
QToolButton *closeButton = new QToolButton;
// easier to click with larger size
closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
closeButton->setMinimumWidth(40);
closeButton->setAutoRaise(true);
closeButton->setIcon(QIcon::fromTheme("window-close"));
connect(closeButton, &QToolButton::clicked, this, &NotifyItem::deleteLater);
textLabel->setWordWrap(true);
textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
textLabel->setOpenExternalLinks(true);
textLabel->setMinimumWidth(300);
textLabel->setMaximumWidth(600);
textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
centerBox->addWidget(textLabel);
if ( actions.count() )
{
QHBoxLayout *actionBox = new QHBoxLayout;
centerBox->addLayout(actionBox);
for (int i = 0; i < actions.count(); i++)
{
if ( ((i % 2) != 0) && !actions[i].isEmpty() ) // id
{
QPushButton *button = new QPushButton;
button->setText(actions[i]);
actionBox->addWidget(button);
QString key = actions[i - 1];
connect(button, &QPushButton::clicked, this,
[this, key]()
{
QDBusMessage msg =
QDBusMessage::createSignal("/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
"ActionInvoked");
msg << id << key;
QDBusConnection::sessionBus().send(msg);
});
}
}
}
hbox->addLayout(vbox);
hbox->addLayout(centerBox);
hbox->addWidget(closeButton);
iconLabel->setFixedSize(32, 32);
iconLabel->setPixmap(icon.pixmap(iconLabel->size()));
QString title = (appName == summary) ? appName : (appName + ": " + summary);
textLabel->setText(QString("%1
%2")
.arg(title)
.arg(body));
timeLabel->setText(QTime::currentTime().toString(Qt::SystemLocaleShortDate));
}
//--------------------------------------------------------------------------------
void NotifyItem::destroySysResources()
{
destroy();
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
NotificationList::NotificationList(QWidget *parent)
: QWidget(parent)
{
setWindowFlags(windowFlags() | Qt::Tool);
setWindowTitle(i18n("Notifications"));
scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
QWidget *listWidget = new QWidget;
listVbox = new QVBoxLayout(listWidget);
listVbox->setContentsMargins(QMargins());
listVbox->addStretch();
scrollArea->setWidget(listWidget);
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setContentsMargins(QMargins());
vbox->addWidget(scrollArea);
QPushButton *clearButton = new QPushButton;
clearButton->setIcon(QIcon::fromTheme("edit-clear-list"));
connect(clearButton, &QPushButton::clicked, this, [this]() { for (NotifyItem *item : items) item->deleteLater(); });
vbox->addWidget(clearButton);
resize(500, 300);
}
//--------------------------------------------------------------------------------
NotificationList::~NotificationList()
{
for (NotifyItem *item : items)
item->disconnect(); // make sure destroyed() is no longer triggered
}
//--------------------------------------------------------------------------------
void NotificationList::addItem(uint id, const QString &appName, const QString &summary, const QString &body,
const QIcon &icon, const QStringList &actions, const QVariantMap &hints, int timeout)
{
QPointer item = new NotifyItem(nullptr, id, appName, summary, body, icon, actions);
item->resize(500, item->sizeHint().height());
KWindowSystem::setState(item->winId(), NET::SkipTaskbar | NET::SkipPager);
items.append(item.data());
placeItems();
int wordCount = body.splitRef(' ', QString::SkipEmptyParts).count();
bool neverExpires = timeout == 0; // according to spec
if ( timeout <= 0 )
{
timeout = 4000 + 250 * wordCount;
if ( actions.count() )
timeout += 15000; // give user more time to think ...
}
// 0=low, 1=normal, 2=critical
if ( hints.contains("urgency") && (hints["urgency"].toInt() == 2) )
{
neverExpires = true;
timeout = 20000; // just show it longer since its urgent
}
bool transient = hints.contains("transient") ? hints["transient"].toBool() : false;
// if there are actions, show it longer
if ( actions.count() || neverExpires )
transient = false;
// exceptions ...
if ( transient && hints.contains("x-kde-eventId") )
{
// I'd like to keep this much longer
if ( hints["x-kde-eventId"].toString() == "new-email" )
transient = false;
}
// I often get the same notification multiple times (e.g. KDE-connect or EWS akonadi resource)
// but I don't want to fill up the screen with useless duplicate information. Therefore
// whenever the same notification is received while another instance of it is still visible,
// only put it in the list-view (it has a different id, therefore still store it), but don't show it as popup
for (NotifyItem *it : items)
{
if ( (it != item) && // not the new item already in items
!it->parentWidget() && // temporary item not in the list-view yet
(appName == it->appName) && (summary == it->summary) &&
(body == it->body) && (actions == it->actions) )
{
timeout = 0;
}
}
numItems++;
emit itemsCountChanged();
connect(item.data(), &NotifyItem::destroyed, this,
[this](QObject *obj)
{
items.removeOne(static_cast(obj));
placeItems();
if ( --numItems == 0 )
{
hide();
emit listNowEmpty();
}
else
emit itemsCountChanged();
}
);
QTimer::singleShot(timeout,
[=]()
{
if ( item )
{
// sometimes there were some leftover/half-functioning windows. This seems to avoid that
item->destroySysResources();
listVbox->insertWidget(listVbox->count() - 1, item); // insert before stretch
item->show();
placeItems(); // reorder the remaining ones
scrollArea->setWidgetResizable(true); // to update scrollbars, else next line does not work
scrollArea->ensureWidgetVisible(item);
if ( !neverExpires )
{
if ( !appTimeouts.contains(appName) )
appTimeouts.insert(appName, 10); // default: 10 minutes lifetime
int expireTimeout;
if ( transient )
expireTimeout = 2 * 60 * 1000; // still keep it a short time
else
expireTimeout = appTimeouts[appName] * 60 * 1000;
QTimer::singleShot(expireTimeout, item.data(), &NotifyItem::deleteLater);
}
}
}
);
}
//--------------------------------------------------------------------------------
void NotificationList::closeItem(uint id)
{
for (NotifyItem *item : items)
{
if ( item->id == id )
{
item->deleteLater();
break;
}
}
}
//--------------------------------------------------------------------------------
void NotificationList::placeItems()
{
- QRect screen = QApplication::primaryScreen()->availableGeometry();
+ QRect screen = DesktopWidget::availableGeometry();
QPoint point = parentWidget()->mapToGlobal(parentWidget()->pos());
int x = point.x();
int y = screen.bottom();
for (NotifyItem *item : items)
{
if ( !item->parentWidget() ) // temporary item not in the list yet
{
y -= item->sizeHint().height();
y -= 5; // a small space
point.setX(std::min(x, screen.x() + screen.width() - item->sizeHint().width()));
point.setY(y);
item->move(point);
item->show();
}
}
}
//--------------------------------------------------------------------------------
diff --git a/PkUpdateList.cxx b/PkUpdateList.cxx
index c202261..85311dc 100644
--- a/PkUpdateList.cxx
+++ b/PkUpdateList.cxx
@@ -1,656 +1,657 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 - 2019 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define TEST_LOGOUT
//#define TEST_REBOOT
//--------------------------------------------------------------------------------
PkUpdateListItem::PkUpdateListItem(QWidget *parent, PackageKit::Transaction::Info info, const PkUpdates::PackageData &data)
: QWidget(parent), package(data)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QGridLayout *grid = new QGridLayout(this);
grid->setContentsMargins(QMargins());
grid->setSpacing(0);
checkBox = new QCheckBox;
checkBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
grid->addWidget(checkBox, 0, 0);
QString icon;
switch ( info )
{
case PackageKit::Transaction::InfoSecurity: icon = "update-high"; break;
case PackageKit::Transaction::InfoImportant: icon = "update-medium"; break;
case PackageKit::Transaction::InfoBugfix: icon = "update-low"; break;
default: ;
}
QLabel *iconLabel = new QLabel;
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
iconLabel->setPixmap(QIcon::fromTheme(icon).pixmap(16));
grid->addWidget(iconLabel, 0, 1);
checkBox->setChecked(!icon.isEmpty()); // auto-check the important ones
connect(checkBox, &QCheckBox::toggled, this, &PkUpdateListItem::toggled);
label = new QToolButton;
label->setAutoRaise(true);
label->setText(package.summary + " [" +
PackageKit::Daemon::packageName(package.id) + ", " +
PackageKit::Daemon::packageVersion(package.id) + ']');
grid->addWidget(label, 0, 2, Qt::AlignLeft);
detailsLabel = new QLabel;
detailsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
detailsLabel->setIndent(30);
detailsLabel->setAlignment(Qt::AlignLeft);
grid->addWidget(detailsLabel, 1, 2);
detailsLabel->hide();
connect(label, &QToolButton::clicked, this, &PkUpdateListItem::getUpdateDetails);
{
QHBoxLayout *progressHbox = new QHBoxLayout;
progressHbox->setSpacing(10);
progress = new QProgressBar;
progress->setFixedWidth(300);
progressHbox->addWidget(progress);
errorLabel = new QLabel;
errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
errorLabel->hide();
progressHbox->addWidget(errorLabel);
packageLabel = new QLabel;
packageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
packageLabel->hide();
progressHbox->addWidget(packageLabel);
grid->addLayout(progressHbox, 2, 2, Qt::AlignLeft);
showProgress(false);
}
}
//--------------------------------------------------------------------------------
void PkUpdateListItem::showProgress(bool yes)
{
progress->setValue(0);
progress->setVisible(yes);
}
//--------------------------------------------------------------------------------
void PkUpdateListItem::getUpdateDetails()
{
if ( !detailsLabel->isHidden() )
{
detailsLabel->hide();
return;
}
//qDebug() << "getUpdateDetails" << package.id;
PackageKit::Transaction *transaction = PackageKit::Daemon::getUpdateDetail(package.id);
detailsLabel->setText(i18n("Getting details ..."));
detailsLabel->show();
connect(transaction, &PackageKit::Transaction::updateDetail, this, &PkUpdateListItem::updateDetail);
connect(transaction, &PackageKit::Transaction::errorCode, this,
[this](PackageKit::Transaction::Error error, const QString &details)
{
Q_UNUSED(error)
detailsLabel->setText(details);
});
}
//--------------------------------------------------------------------------------
void PkUpdateListItem::updateDetail(const QString &packageID,
const QStringList &updates,
const QStringList &obsoletes,
const QStringList &vendorUrls,
const QStringList &bugzillaUrls,
const QStringList &cveUrls,
PackageKit::Transaction::Restart restart,
const QString &updateText,
const QString &changelog,
PackageKit::Transaction::UpdateState state,
const QDateTime &issued,
const QDateTime &updated)
{
Q_UNUSED(packageID)
Q_UNUSED(updates)
Q_UNUSED(obsoletes)
Q_UNUSED(vendorUrls)
Q_UNUSED(bugzillaUrls)
Q_UNUSED(cveUrls)
Q_UNUSED(changelog)
Q_UNUSED(state)
Q_UNUSED(issued)
Q_UNUSED(updated)
QString text;
if ( restart == PackageKit::Transaction::RestartSession )
text = i18n("Session restart required
");
else if ( restart == PackageKit::Transaction::RestartSystem )
text = i18n("Reboot required
");
text += updateText.trimmed();
text.replace("\n", "
");
detailsLabel->setText(text);
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
PkUpdateList::PkUpdateList(QWidget *parent)
: QWidget(parent), restart(PackageKit::Transaction::RestartNone)
{
setWindowFlags(windowFlags() | Qt::Tool);
setWindowTitle(i18n("Software Updates"));
vbox = new QVBoxLayout(this);
vbox->setContentsMargins(QMargins(0, -1, 0, 0));
// action buttons
QHBoxLayout *hbox = new QHBoxLayout;
checkAllBox = new QCheckBox(i18n("All"));
connect(checkAllBox, &QCheckBox::toggled, this, &PkUpdateList::checkAll);
filterEdit = new QLineEdit;
filterEdit->setPlaceholderText(i18n("Filter"));
filterEdit->setClearButtonEnabled(true);
connect(filterEdit, &QLineEdit::textEdited, this, &PkUpdateList::filterChanged);
// create "busy" indicator
progressBar = new QProgressBar;
installButton = new QPushButton(i18n("Install"));
installButton->setEnabled(false);
connect(installButton, &QPushButton::clicked, this, &PkUpdateList::install);
refreshButton = new QPushButton(i18n("Refresh"));
connect(refreshButton, &QPushButton::clicked, this, [this]() { setPackages(PkUpdates::PackageList()); emit refreshRequested(); });
hbox->addWidget(checkAllBox);
hbox->addWidget(filterEdit);
hbox->addWidget(progressBar);
hbox->addWidget(installButton);
hbox->addWidget(refreshButton);
vbox->addLayout(hbox);
// list of items in the order: security, important, bugfix, others
scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
vbox->addWidget(scrollArea);
QWidget *w = new QWidget;
vbox = new QVBoxLayout(w);
itemsLayout = new QVBoxLayout;
itemsLayout->setSpacing(0);
vbox->addLayout(itemsLayout);
vbox->addStretch();
scrollArea->setWidget(w);
}
//--------------------------------------------------------------------------------
QSize PkUpdateList::sizeHint() const
{
QSize s = scrollArea->widget()->sizeHint() + QSize(2 * scrollArea->frameWidth(), 2 * scrollArea->frameWidth());
s.setWidth(s.width() + scrollArea->verticalScrollBar()->sizeHint().width());
s.setHeight(layout()->contentsMargins().top() + installButton->sizeHint().height() +
((layout()->spacing() == -1) ? style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing) : layout()->spacing()) +
s.height() + layout()->contentsMargins().bottom());
s = s.expandedTo(QSize(500, 300));
// Qt doc: The size of top-level widgets are constrained to 2/3 of the desktop's height and width.
- QSize screen = QApplication::primaryScreen()->availableSize();
+ QSize screen = DesktopWidget::availableSize();
s = s.boundedTo(screen * 2 / 3);
return s;
}
//--------------------------------------------------------------------------------
void PkUpdateList::setRefreshProgress(int progress)
{
if ( progress >= 100 )
{
filterEdit->show();
progressBar->hide();
refreshButton->setEnabled(true);
}
else if ( progress == 0 )
{
// we don't know if there will be a value between 0..100
// Until the first value arrives, use a busy indicator
progressBar->setMinimum(0);
progressBar->setMaximum(0);
progressBar->setValue(0);
progressBar->show();
filterEdit->hide();
refreshButton->setEnabled(false);
}
else
{
progressBar->setMinimum(0);
progressBar->setMaximum(100);
progressBar->setValue(progress);
}
}
//--------------------------------------------------------------------------------
void PkUpdateList::setPackages(const PkUpdates::PackageList &packages)
{
QLayoutItem *child;
while ( (child = itemsLayout->takeAt(0)) )
{
delete child->widget();
delete child;
}
checkAllBox->setChecked(false);
QList list = packages.values(PackageKit::Transaction::InfoSecurity);
for (const PkUpdates::PackageData &data : list)
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), PackageKit::Transaction::InfoSecurity, data);
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
list = packages.values(PackageKit::Transaction::InfoImportant);
for (const PkUpdates::PackageData &data : list)
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), PackageKit::Transaction::InfoImportant, data);
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
list = packages.values(PackageKit::Transaction::InfoBugfix);
for (const PkUpdates::PackageData &data : list)
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), PackageKit::Transaction::InfoBugfix, data);
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
// all the others
for (PkUpdates::PackageList::const_iterator it = packages.constBegin(); it != packages.constEnd(); ++it)
{
if ( (it.key() != PackageKit::Transaction::InfoSecurity) &&
(it.key() != PackageKit::Transaction::InfoImportant) &&
(it.key() != PackageKit::Transaction::InfoBugfix) )
{
auto *item = new PkUpdateListItem(itemsLayout->parentWidget(), it.key(), it.value());
connect(item, &PkUpdateListItem::toggled, this, &PkUpdateList::countChecked);
itemsLayout->addWidget(item);
item->show();
}
}
filterChanged(filterEdit->text());
countChecked();
}
//--------------------------------------------------------------------------------
void PkUpdateList::checkAll(bool on)
{
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( item && !item->isHidden() )
{
item->checkBox->blockSignals(true);
item->checkBox->setChecked(on);
item->checkBox->blockSignals(false);
}
}
countChecked();
}
//--------------------------------------------------------------------------------
void PkUpdateList::countChecked()
{
int count = 0;
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( item && !item->isHidden() && item->checkBox->isChecked() )
count++;
}
if ( count )
{
installButton->setText(i18np("Install %1 package", "Install %1 packages", count));
installButton->setEnabled(true);
}
else
{
installButton->setText(i18n("Install"));
installButton->setEnabled(false);
}
installButton->setIcon(QIcon());
refreshButton->setEnabled(true);
}
//--------------------------------------------------------------------------------
void PkUpdateList::filterChanged(const QString &text)
{
itemsLayout->parentWidget()->layout()->setEnabled(false);
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
item->setVisible(text.isEmpty() || (item->label->text().indexOf(text, 0, Qt::CaseInsensitive) != -1));
}
itemsLayout->parentWidget()->layout()->setEnabled(true);
countChecked();
}
//--------------------------------------------------------------------------------
void PkUpdateList::install()
{
if ( !installQ.isEmpty() ) // installation in progress; cancel it
{
QPointer currentItem = installQ.head();
if ( transaction )
{
QDBusPendingReply<> reply = transaction->cancel();
reply.waitForFinished();
if ( reply.isError() && currentItem )
{
currentItem->errorLabel->setText(reply.error().message());
currentItem->errorLabel->show();
}
}
for (int i = 0; i < itemsLayout->count(); i++)
{
PkUpdateListItem *item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( !transaction || (item != currentItem) )
{
item->showProgress(false);
item->errorLabel->hide();
}
}
installQ.clear();
emit packageCountToInstall(0);
countChecked();
return;
}
restart = PackageKit::Transaction::RestartNone;
for (int i = 0; i < itemsLayout->count(); i++)
{
QPointer item = qobject_cast(itemsLayout->itemAt(i)->widget());
if ( item && !item->isHidden() && item->checkBox->isChecked() )
{
item->showProgress(true);
item->errorLabel->hide();
item->detailsLabel->hide();
installQ.enqueue(item);
}
}
installOne();
}
//--------------------------------------------------------------------------------
void PkUpdateList::installOne()
{
emit packageCountToInstall(installQ.count());
if ( installQ.isEmpty() ) // installation finished
{
if ( restart != PackageKit::Transaction::RestartNone )
{
QString text;
if ( (restart == PackageKit::Transaction::RestartSystem) ||
(restart == PackageKit::Transaction::RestartSecuritySystem) )
{
KNotification *notif = new KNotification("restart needed", parentWidget(), KNotification::Persistent);
notif->setTitle(i18n("System Reboot Required"));
notif->setText(i18n("One of the installed packages requires a system reboot"));
notif->setActions(QStringList() << i18n("Reboot System"));
connect(notif, &KNotification::action1Activated, this,
[]()
{
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.ksmserver", "/KSMServer",
"org.kde.KSMServerInterface", "logout");
msg << 0/*no confirm*/ << 1/*reboot*/ << 0; // plasma-workspace/libkworkspace/kworkspace.h
QDBusConnection::sessionBus().send(msg);
});
notif->sendEvent();
}
else if ( (restart == PackageKit::Transaction::RestartSession) ||
(restart == PackageKit::Transaction::RestartSecuritySession) )
{
KNotification *notif = new KNotification("restart needed", parentWidget(), KNotification::Persistent);
notif->setTitle(i18n("Session Restart Required"));
notif->setText(i18n("One of the installed packages requires you to logout"));
notif->setActions(QStringList() << i18n("Logout"));
connect(notif, &KNotification::action1Activated, this,
[]()
{
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.ksmserver", "/KSMServer",
"org.kde.KSMServerInterface", "logout");
msg << 0/*no confirm*/ << 0/*logout*/ << 0; // plasma-workspace/libkworkspace/kworkspace.h
QDBusConnection::sessionBus().send(msg);
});
notif->sendEvent();
}
}
countChecked();
return;
}
QPointer item = installQ.head();
if ( !item )
return;
installButton->setText(i18n("Cancel Installation"));
installButton->setIcon(QIcon::fromTheme("process-stop"));
refreshButton->setEnabled(false);
PackageKit::Transaction::TransactionFlag flag = PackageKit::Transaction::TransactionFlagOnlyTrusted;
#ifdef TEST_LOGOUT
flag |= PackageKit::Transaction::TransactionFlagSimulate;
restart = PackageKit::Transaction::RestartSession;
#elif defined TEST_REBOOT
flag |= PackageKit::Transaction::TransactionFlagSimulate;
restart = PackageKit::Transaction::RestartSystem;
#endif
transaction = PackageKit::Daemon::updatePackage(item->package.id, flag);
packageNoLongerAvailable = false;
//qDebug() << "installing" << item->package.id;
connect(transaction.data(), &PackageKit::Transaction::statusChanged, this,
[item, this]()
{
if ( !item ) // already deleted
return;
//qDebug() << "status" << QMetaEnum::fromType().valueToKey(transaction->status());
QString text;
switch ( transaction->status() )
{
case PackageKit::Transaction::StatusWait: text = i18n("Waiting"); break;
case PackageKit::Transaction::StatusWaitingForAuth: text = i18n("Waiting for authentication"); break;
case PackageKit::Transaction::StatusDepResolve: text = i18n("Resolving dependencies"); break;
case PackageKit::Transaction::StatusUpdate: text = i18n("Updating"); break;
case PackageKit::Transaction::StatusInstall: text = i18n("Installing"); break;
case PackageKit::Transaction::StatusDownload: text = i18n("Downloading"); break;
case PackageKit::Transaction::StatusCancel: text = i18n("Canceling"); break;
case PackageKit::Transaction::StatusFinished: return; // don't hide error label
default: return;
}
if ( text.isEmpty() )
item->errorLabel->hide();
else
{
item->errorLabel->setText(text);
item->errorLabel->show();
}
});
connect(transaction.data(), &PackageKit::Transaction::itemProgress, this,
[item](const QString &itemID, PackageKit::Transaction::Status status, uint percentage)
{
Q_UNUSED(status)
if ( !item ) // already deleted
return;
item->packageLabel->setText(PackageKit::Daemon::packageName(itemID));
item->packageLabel->show();
if ( percentage <= 100 ) // 101 .. unknown
item->progress->setValue(percentage);
});
connect(transaction.data(), &PackageKit::Transaction::requireRestart, this,
[this](PackageKit::Transaction::Restart type, const QString &/*packageID*/)
{
// keep most important restart type: System, Session
if ( (type == PackageKit::Transaction::RestartSystem) ||
(type == PackageKit::Transaction::RestartSecuritySystem) ||
(((type == PackageKit::Transaction::RestartSession) ||
(type == PackageKit::Transaction::RestartSecuritySession)) &&
(restart != PackageKit::Transaction::RestartSystem) &&
(restart != PackageKit::Transaction::RestartSecuritySystem)) )
restart = type;
});
connect(transaction.data(), &PackageKit::Transaction::errorCode, this,
[item, this](PackageKit::Transaction::Error error, const QString &details)
{
if ( !item )
return;
item->showProgress(false);
item->errorLabel->setText(details);
item->errorLabel->show();
//qDebug() << "errorCode" << details << QMetaEnum::fromType().valueToKey(error);
if ( (error == PackageKit::Transaction::ErrorDepResolutionFailed) &&
(transaction->status() == PackageKit::Transaction::StatusSetup) )
{
// when a package was already installed due to another dependency, then it's no more an update candidate.
// Still the finished() signal will arrive which will do cleanup (delete item, dequeue, etc.).
packageNoLongerAvailable = true;
}
});
connect(transaction.data(), &PackageKit::Transaction::finished, this,
[item, this](PackageKit::Transaction::Exit status, uint runtime)
{
Q_UNUSED(runtime)
if ( !item )
return;
if ( (status == PackageKit::Transaction::ExitSuccess) || packageNoLongerAvailable )
{
item->hide();
item->deleteLater();
emit packageInstalled(item->package.id);
}
else
{
item->showProgress(false);
item->detailsLabel->setText(i18n("Update failed"));
item->detailsLabel->show();
}
if ( !installQ.isEmpty() ) // might have been cancelled, q cleared
installQ.dequeue();
installOne();
});
}
//--------------------------------------------------------------------------------
diff --git a/SysTrayItem.cxx b/SysTrayItem.cxx
index 770ae89..8de37a7 100644
--- a/SysTrayItem.cxx
+++ b/SysTrayItem.cxx
@@ -1,91 +1,92 @@
// SPDX-License-Identifier: GPL-3.0-or-later
/*
- Copyright 2017 - 2019 Martin Koller, kollix@aon.at
+ Copyright 2017 - 2020 Martin Koller, kollix@aon.at
This file is part of liquidshell.
liquidshell is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
liquidshell is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with liquidshell. If not, see .
*/
#include
+#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
SysTrayItem::SysTrayItem(QWidget *parent, const QString &icon)
: QLabel(parent), iconName(icon)
{
setFixedSize(QSize(22, 22));
if ( !iconName.isEmpty() )
{
setPixmap(QIcon::fromTheme(iconName).pixmap(size()));
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this,
[this]() { setPixmap(QIcon::fromTheme(iconName).pixmap(size())); });
}
}
//--------------------------------------------------------------------------------
void SysTrayItem::mousePressEvent(QMouseEvent *event)
{
if ( event->button() != Qt::LeftButton )
return;
toggleDetailsList();
}
//--------------------------------------------------------------------------------
void SysTrayItem::showDetailsList()
{
QWidget *detailsList = getDetailsList();
if ( !detailsList )
return;
QPoint point = mapToGlobal(pos());
- QRect screen = QApplication::primaryScreen()->availableGeometry();
+ QRect screen = DesktopWidget::availableGeometry();
QSize size = detailsList->frameSize();
point.setX(std::min(point.x(), screen.x() + screen.width() - size.width()));
point.setY(screen.bottom() - size.height());
detailsList->move(point);
detailsList->show();
KWindowSystem::raiseWindow(detailsList->winId());
}
//--------------------------------------------------------------------------------
void SysTrayItem::toggleDetailsList()
{
QWidget *detailsList = getDetailsList();
if ( !detailsList )
return;
if ( detailsList->isVisible() )
detailsList->close();
else
showDetailsList();
}
//--------------------------------------------------------------------------------