diff --git a/AppMenu.cxx b/AppMenu.cxx
index f9e2c34..cf1fe29 100644
--- a/AppMenu.cxx
+++ b/AppMenu.cxx
@@ -1,194 +1,195 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
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::desktop(), &QDesktopWidget::resized, 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()
{
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;
}
QDir dir(dirPath);
QFileInfoList entries = dir.entryInfoList(QDir::AllEntries | QDir::NoDotDot);
int row = 0, col = 0, h = 0;
const int maxHeight = QApplication::desktop()->height() - parentWidget()->sizeHint().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); });
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);
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
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 9bfb17d..9fd30bf 100644
--- a/AppMenu.hxx
+++ b/AppMenu.hxx
@@ -1,70 +1,71 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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/Battery.cxx b/Battery.cxx
index f218ac6..e35e8ff 100644
--- a/Battery.cxx
+++ b/Battery.cxx
@@ -1,206 +1,207 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
Battery::Battery(QWidget *parent)
: SysTrayItem(parent)
{
QList devices = Solid::Device::listFromType(Solid::DeviceInterface::Battery);
for (Solid::Device dev : devices)
{
if ( dev.is() && (dev.as()->type() == Solid::Battery::PrimaryBattery) )
{
device = dev;
break;
}
}
if ( !device.isValid() )
hide();
else
{
QDBusConnection::systemBus()
.connect("org.freedesktop.UPower",
"/org/freedesktop/UPower",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
this,
SLOT(upowerPropertiesChanged(QString, QVariantMap, QStringList)));
QDBusMessage msg =
QDBusMessage::createMethodCall("org.freedesktop.UPower",
"/org/freedesktop/UPower",
"org.freedesktop.DBus.Properties",
"Get");
msg << QLatin1String("org.freedesktop.UPower") << QLatin1String("OnBattery");
QDBusConnection::systemBus().callWithCallback(msg, this, SLOT(onBatteryReply(QDBusMessage)), nullptr);
connect(device.as(), &Solid::Battery::chargePercentChanged, this, &Battery::changed);
connect(device.as(), &Solid::Battery::chargeStateChanged, this, &Battery::changed);
connect(device.as(), &Solid::Battery::timeToFullChanged, this, &Battery::changed);
connect(device.as(), &Solid::Battery::timeToEmptyChanged, this, &Battery::changed);
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &Battery::changed);
changed();
}
}
//--------------------------------------------------------------------------------
QString Battery::secsToHM(int secs) const
{
int h = secs / 3600;
int m = (secs % 3600) / 60;
QString hStr = i18np("%1 hour", "%1 hours", h);
QString mStr = i18np("%1 minute", "%1 minutes", m);
QString result;
if ( h )
result = hStr;
if ( m )
{
if ( h )
result += ", ";
result += mStr;
}
return result;
}
//--------------------------------------------------------------------------------
void Battery::onBatteryReply(const QDBusMessage &msg)
{
setVisible(msg.arguments()[0].value().variant().toBool());
}
//--------------------------------------------------------------------------------
void Battery::upowerPropertiesChanged(const QString &interface,
const QVariantMap &properties,
const QStringList &invalidated)
{
Q_UNUSED(interface)
Q_UNUSED(invalidated)
if ( properties.contains("OnBattery") )
{
setVisible(properties.value("OnBattery").toBool());
changed();
}
}
//--------------------------------------------------------------------------------
QIcon Battery::getStatusIcon(int charge, bool isCharging)
{
QString iconName;
int p = qRound(charge / 20.0) * 20;
if ( p < 20 )
iconName = "caution";
else if ( p < 40 )
iconName = "low";
else
iconName = QString("%1").arg(p, 3, 10, QLatin1Char('0'));
iconName = QString("battery%1-%2").arg(isCharging ? "-charging" : "").arg(iconName);
return QIcon::fromTheme(iconName);
}
//--------------------------------------------------------------------------------
void Battery::changed()
{
Solid::Battery *battery = device.as();
QString tip;
switch ( battery->chargeState() )
{
case Solid::Battery::NoCharge: tip = i18n("Not Charging"); break;
case Solid::Battery::FullyCharged: tip = i18n("Fully Charged"); break;
case Solid::Battery::Charging:
case Solid::Battery::Discharging:
{
if ( battery->chargeState() == Solid::Battery::Charging )
{
tip = i18n("Charging at %1%", battery->chargePercent());
if ( battery->timeToFull() ) // it can be 0, so we don't know
tip += '\n' + i18n("Time until full: ") + secsToHM(battery->timeToFull());
}
else
{
tip = i18n("Discharging at %1%", battery->chargePercent());
if ( battery->timeToEmpty() ) // it can be 0, so we don't know
tip += '\n' + i18n("Remaining Time: ") + secsToHM(battery->timeToEmpty());
}
break;
}
}
setPixmap(getStatusIcon(battery->chargePercent(),
battery->chargeState() == Solid::Battery::Charging).pixmap(size()));
setToolTip(tip);
}
//--------------------------------------------------------------------------------
QWidget *Battery::getDetailsList()
{
if ( !dialog )
{
dialog = new KCMultiDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->addModule("powerdevilglobalconfig");
dialog->addModule("powerdevilprofilesconfig");
dialog->adjustSize();
dialog->setWindowTitle(i18n("Power Management"));
}
return dialog;
}
//--------------------------------------------------------------------------------
diff --git a/Battery.hxx b/Battery.hxx
index 38e81bb..fae61f3 100644
--- a/Battery.hxx
+++ b/Battery.hxx
@@ -1,54 +1,55 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _Battery_H_
#define _Battery_H_
#include
#include
#include
#include
class QDBusMessage;
class Battery : public SysTrayItem
{
Q_OBJECT
public:
Battery(QWidget *parent);
static QIcon getStatusIcon(int charge, bool isCharging);
protected:
QWidget *getDetailsList() override;
private Q_SLOTS:
void onBatteryReply(const QDBusMessage &msg);
void upowerPropertiesChanged(const QString &interface, const QVariantMap &properties, const QStringList &invalidated);
void changed();
private:
QString secsToHM(int secs) const;
private:
Solid::Device device;
QPointer dialog;
};
#endif
diff --git a/Bluetooth.cxx b/Bluetooth.cxx
index 1920a8b..5ad0359 100644
--- a/Bluetooth.cxx
+++ b/Bluetooth.cxx
@@ -1,101 +1,102 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
Bluetooth::Bluetooth(QWidget *parent)
: SysTrayItem(parent)
{
manager = new BluezQt::Manager(this);
job = manager->init();
job->start();
connect(job, &BluezQt::InitManagerJob::result, this, &Bluetooth::changed);
connect(manager, &BluezQt::Manager::adapterAdded, this, &Bluetooth::changed);
connect(manager, &BluezQt::Manager::adapterRemoved, this, &Bluetooth::changed);
connect(manager, &BluezQt::Manager::allAdaptersRemoved, this, &Bluetooth::changed);
connect(manager, &BluezQt::Manager::bluetoothOperationalChanged, this, &Bluetooth::changed);
connect(manager, &BluezQt::Manager::operationalChanged, this, &Bluetooth::changed);
connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &Bluetooth::changed);
}
//--------------------------------------------------------------------------------
Bluetooth::~Bluetooth()
{
if ( job )
job->kill();
}
//--------------------------------------------------------------------------------
void Bluetooth::changed()
{
job = nullptr;
if ( manager->adapters().isEmpty() || !manager->isOperational() )
{
hide(); // no BT
return;
}
show();
if ( manager->isBluetoothOperational() )
{
setPixmap(QIcon::fromTheme("preferences-system-bluetooth.png").pixmap(size()));
setToolTip(i18n("Bluetooth is operational"));
}
else
{
setPixmap(QIcon::fromTheme("preferences-system-bluetooth-inactive.png").pixmap(size()));
setToolTip(i18n("Bluetooth is not operational"));
}
}
//--------------------------------------------------------------------------------
QWidget *Bluetooth::getDetailsList()
{
if ( !dialog )
{
dialog = new KCMultiDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->addModule("bluedevilglobal");
dialog->addModule("bluedeviladapters");
dialog->addModule("bluedevildevices");
dialog->adjustSize();
dialog->setWindowTitle(i18n("Bluetooth"));
}
return dialog;
}
//--------------------------------------------------------------------------------
diff --git a/Bluetooth.hxx b/Bluetooth.hxx
index 7caaf9a..23619dd 100644
--- a/Bluetooth.hxx
+++ b/Bluetooth.hxx
@@ -1,49 +1,50 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _Bluetooth_H_
#define _Bluetooth_H_
#include
#include
#include
#include
class Bluetooth : public SysTrayItem
{
Q_OBJECT
public:
Bluetooth(QWidget *parent);
~Bluetooth() override;
protected:
QWidget *getDetailsList() override;
private Q_SLOTS:
void changed();
private:
BluezQt::Manager *manager;
BluezQt::InitManagerJob *job;
QPointer dialog;
};
#endif
diff --git a/ClockWidget.cxx b/ClockWidget.cxx
index 6b23f80..052209b 100644
--- a/ClockWidget.cxx
+++ b/ClockWidget.cxx
@@ -1,243 +1,244 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017,2018 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
//--------------------------------------------------------------------------------
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->setContentsMargins(QMargins(0, -5, 0, -5));
dayLabel->setContentsMargins(QMargins(0, -5, 0, -5));
dateLabel->setContentsMargins(QMargins(0, -5, 0, -5));
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();
}
//--------------------------------------------------------------------------------
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::desktop()->availableGeometry(this);
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/ClockWidget.hxx b/ClockWidget.hxx
index 6ce9dfb..25a43f9 100644
--- a/ClockWidget.hxx
+++ b/ClockWidget.hxx
@@ -1,75 +1,76 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _ClockWidget_H_
#define _ClockWidget_H_
#include
#include
#include
#include
#include
class CalendarPopup : public QFrame
{
Q_OBJECT
public:
CalendarPopup(QWidget *parent);
public Q_SLOTS:
void goToday();
private:
QCalendarWidget *cal;
};
//--------------------------------------------------------------------------------
class ClockWidget : public QFrame
{
Q_OBJECT
Q_PROPERTY(QString timeFormat MEMBER timeFormat)
Q_PROPERTY(QString dayFormat MEMBER dayFormat)
Q_PROPERTY(QString dateFormat MEMBER dateFormat)
public:
ClockWidget(DesktopPanel *parent);
protected:
void mousePressEvent(QMouseEvent *event) override;
private Q_SLOTS:
void fill();
void tick();
private:
QTimer *timer;
QLabel *timeLabel, *dayLabel, *dateLabel;
CalendarPopup *calendar;
QString timeFormat = QStringLiteral("HH:mm");
QString dayFormat = QStringLiteral("ddd");
QString dateFormat = QStringLiteral("d.MMM yyyy");
QVector timeZoneIds;
};
#endif
diff --git a/ClockWidgetConfigureDialog.cxx b/ClockWidgetConfigureDialog.cxx
index 3829a81..4ecfe1a 100644
--- a/ClockWidgetConfigureDialog.cxx
+++ b/ClockWidgetConfigureDialog.cxx
@@ -1,101 +1,102 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
ClockWidgetConfigureDialog::ClockWidgetConfigureDialog(QWidget *parent, const QVector &timeZoneIds)
: QDialog(parent)
{
tree = new QTreeWidget;
QLineEdit *filter = new KTreeWidgetSearchLine(this, tree);
filter->setClearButtonEnabled(true);
filter->setPlaceholderText(i18n("Filter"));
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->addWidget(filter);
vbox->addWidget(tree);
vbox->addWidget(buttons);
// fill tree
tree->setHeaderLabels(QStringList() << i18n("Timezone") << i18n("Country"));
tree->setRootIsDecorated(false);
QList zones = QTimeZone::availableTimeZoneIds();
for (const QByteArray &zone : zones)
{
QTimeZone timeZone(zone);
QTreeWidgetItem *item = new QTreeWidgetItem(tree);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable |
Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
item->setCheckState(0, timeZoneIds.contains(zone) ? Qt::Checked : Qt::Unchecked);
item->setText(0, timeZone.id());
item->setText(1, QLocale::countryToString(timeZone.country()));
// Locate the flag from share/kf5/locale/countries/%1/flag.png
QList matchingLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, timeZone.country());
if ( !matchingLocales.isEmpty() )
{
QString flag = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
QString("kf5/locale/countries/%1/flag.png").arg(matchingLocales[0].name().mid(3).toLower()));
if ( !flag.isEmpty() )
item->setIcon(1, QPixmap(flag));
}
}
tree->setSortingEnabled(true);
tree->sortByColumn(0, Qt::AscendingOrder);
tree->header()->resizeSections(QHeaderView::ResizeToContents);
}
//--------------------------------------------------------------------------------
QVector ClockWidgetConfigureDialog::getSelectedTimeZoneIds() const
{
QVector timeZoneIds;
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
QTreeWidgetItem *item = tree->topLevelItem(i);
if ( item->checkState(0) == Qt::Checked )
timeZoneIds.append(item->text(0).toUtf8());
}
return timeZoneIds;
}
//--------------------------------------------------------------------------------
diff --git a/ClockWidgetConfigureDialog.hxx b/ClockWidgetConfigureDialog.hxx
index da2f86e..58fcaa3 100644
--- a/ClockWidgetConfigureDialog.hxx
+++ b/ClockWidgetConfigureDialog.hxx
@@ -1,39 +1,40 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _ClockWidgetConfigureDialog_H_
#define _ClockWidgetConfigureDialog_H_
#include
class QTreeWidget;
class ClockWidgetConfigureDialog : public QDialog
{
Q_OBJECT
public:
ClockWidgetConfigureDialog(QWidget *parent, const QVector &timeZoneIds);
QVector getSelectedTimeZoneIds() const;
private:
QTreeWidget *tree;
};
#endif
diff --git a/ConfigureDesktopDialog.cxx b/ConfigureDesktopDialog.cxx
index 9161e0e..7924dfe 100644
--- a/ConfigureDesktopDialog.cxx
+++ b/ConfigureDesktopDialog.cxx
@@ -1,177 +1,178 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
ConfigureDesktopDialog::ConfigureDesktopDialog(QWidget *parent, const DesktopWidget::Wallpaper &wp)
: QDialog(parent), wallpaper(wp)
{
ui.setupUi(this);
ui.iconView->setIconSize(QSize(200, 200));
connect(ui.iconView, &QListWidget::itemClicked,
[this](QListWidgetItem *item)
{
ui.kurlrequester->setUrl(QUrl::fromLocalFile(item->data(Qt::UserRole).toString()));
wallpaper.fileName = ui.kurlrequester->url().toLocalFile();
emit changed();
});
showImages();
QPushButton *newstuff = ui.buttonBox->addButton(i18n("Get New Wallpapers..."), QDialogButtonBox::ActionRole);
newstuff->setIcon(QIcon::fromTheme("get-hot-new-stuff"));
connect(newstuff, &QPushButton::clicked,
[this]()
{
KNS3::DownloadDialog dialog("wallpaper.knsrc", this);
dialog.setTitle(i18n("Download Wallpapers"));
dialog.exec();
if ( dialog.changedEntries().count() )
showImages();
});
ui.kcolorcombo->setColor(wallpaper.color);
ui.kurlrequester->setUrl(QUrl::fromLocalFile(wallpaper.fileName));
connect(ui.kcolorcombo, &KColorCombo::activated,
[this](const QColor &col) { wallpaper.color = col; emit changed(); });
connect(ui.kurlrequester, &KUrlRequester::urlSelected,
[this](const QUrl &url) { wallpaper.fileName = url.toLocalFile(); emit changed(); });
// older compiler can't use this
//connect(ui.kurlrequester, QOverload::of(&KUrlRequester::returnPressed),
// [this](const QString &text) { wallpaper.fileName = text; emit changed(); });
connect(ui.kurlrequester, SIGNAL(returnPressed(QString)), this, SLOT(returnPressed(QString)));
if ( wallpaper.mode == "Scaled" )
ui.scaledIgnoreRatioButton->setChecked(true);
else if ( wallpaper.mode == "ScaledKeepRatio" )
ui.scaledKeepRatioButton->setChecked(true);
else if ( wallpaper.mode == "ScaledKeepRatioExpand" )
ui.scaledKeepRatioClipButton->setChecked(true);
else
ui.origSizeButton->setChecked(true);
buttonGroup.addButton(ui.origSizeButton);
buttonGroup.addButton(ui.scaledIgnoreRatioButton);
buttonGroup.addButton(ui.scaledKeepRatioButton);
buttonGroup.addButton(ui.scaledKeepRatioClipButton);
connect(&buttonGroup, SIGNAL(buttonClicked(QAbstractButton *)),
this, SLOT(buttonClicked(QAbstractButton *)));
}
//--------------------------------------------------------------------------------
void ConfigureDesktopDialog::returnPressed(const QString &text)
{
wallpaper.fileName = text;
emit changed();
}
//--------------------------------------------------------------------------------
void ConfigureDesktopDialog::buttonClicked(QAbstractButton *button)
{
if ( button == ui.origSizeButton )
wallpaper.mode = "";
else if ( button == ui.scaledIgnoreRatioButton )
wallpaper.mode = "Scaled";
else if ( button == ui.scaledKeepRatioButton )
wallpaper.mode = "ScaledKeepRatio";
else if ( button == ui.scaledKeepRatioClipButton )
wallpaper.mode = "ScaledKeepRatioExpand";
emit changed();
}
//--------------------------------------------------------------------------------
void ConfigureDesktopDialog::showImages()
{
// create filter (list of patterns) for image files we can read
QMimeDatabase db;
QStringList filterList;
foreach (const QByteArray &type, QImageReader::supportedMimeTypes())
{
QMimeType mime(db.mimeTypeForName(QString::fromLatin1(type)));
if ( mime.isValid() )
{
foreach (const QString &pattern, mime.globPatterns())
filterList << pattern;
}
}
ui.iconView->clear();
QStringList dirNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
"wallpapers", QStandardPaths::LocateDirectory);
const QString geometryString = QString("%1x%2")
.arg(QApplication::desktop()->width())
.arg(QApplication::desktop()->height());
for (const QString &dirName : dirNames)
{
// check for file directly in this folder
QStringList fileNames = QDir(dirName).entryList(filterList, QDir::Files | QDir::Readable);
for (const QString &fileName : fileNames)
{
QPixmap pixmap(dirName + '/' + fileName);
QListWidgetItem *item = new QListWidgetItem(pixmap, fileName, ui.iconView);
item->setData(Qt::UserRole, dirName + '/' + fileName);
item->setToolTip(QString("%1 (%2x%3)").arg(fileName).arg(pixmap.width()).arg(pixmap.height()));
}
// check for files in the special subdirs
for (const QString &subdir : QDir(dirName).entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable))
{
QDir dir(dirName + '/' + subdir + "/contents/images");
QString chosenFilePath;
for (const QString &fileName : dir.entryList(filterList, QDir::Files | QDir::Readable))
{
chosenFilePath = dir.absoluteFilePath(fileName);
if ( fileName.startsWith(geometryString) )
break; // just take one
}
if ( !chosenFilePath.isEmpty() )
{
QPixmap pixmap(chosenFilePath);
QListWidgetItem *item = new QListWidgetItem(pixmap, subdir, ui.iconView);
item->setData(Qt::UserRole, chosenFilePath);
item->setToolTip(QString("%1 (%2x%3)").arg(subdir).arg(pixmap.width()).arg(pixmap.height()));
}
}
}
}
//--------------------------------------------------------------------------------
diff --git a/ConfigureDesktopDialog.hxx b/ConfigureDesktopDialog.hxx
index b763599..0c853ff 100644
--- a/ConfigureDesktopDialog.hxx
+++ b/ConfigureDesktopDialog.hxx
@@ -1,54 +1,55 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _ConfigureDesktopDialog_H_
#define _ConfigureDesktopDialog_H_
#include
#include
#include
#include
class ConfigureDesktopDialog : public QDialog
{
Q_OBJECT
public:
ConfigureDesktopDialog(QWidget *parent, const DesktopWidget::Wallpaper &wp);
const DesktopWidget::Wallpaper &getWallpaper() const { return wallpaper; }
Q_SIGNALS:
void changed();
private Q_SLOTS:
void returnPressed(const QString &text);
void buttonClicked(QAbstractButton *button);
private:
void showImages();
private:
Ui::ConfigureDesktopDialog ui;
QButtonGroup buttonGroup;
DesktopWidget::Wallpaper wallpaper;
};
#endif
diff --git a/DBusTypes.cxx b/DBusTypes.cxx
index 1b9e698..c64a23c 100644
--- a/DBusTypes.cxx
+++ b/DBusTypes.cxx
@@ -1,138 +1,139 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageStruct &pixmap)
{
pixmap = QPixmap();
if ( argument.currentType() == QDBusArgument::StructureType )
{
qint32 w = 0, h = 0;
QByteArray data;
argument.beginStructure();
argument >> w;
argument >> h;
argument >> data;
argument.endStructure();
// convert data to QPixmap
if ( w && h && !data.isEmpty() )
{
if ( QSysInfo::ByteOrder == QSysInfo::LittleEndian )
{
quint32 *uintBuf = reinterpret_cast(data.data());
for (uint i = 0; i < data.size() / sizeof(quint32); ++i)
{
*uintBuf = qToBigEndian(*uintBuf);
++uintBuf;
}
}
pixmap = QPixmap::fromImage(QImage(reinterpret_cast(data.constData()), w, h, QImage::Format_ARGB32));
}
}
return argument;
}
//--------------------------------------------------------------------------------
const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageVector &icon)
{
icon = QIcon();
if ( argument.currentType() == QDBusArgument::ArrayType )
{
argument.beginArray();
while ( !argument.atEnd() )
{
KDbusImageStruct pixmap;
argument >> pixmap;
if ( !pixmap.isNull() )
icon.addPixmap(pixmap);
}
argument.endArray();
}
return argument;
}
//--------------------------------------------------------------------------------
const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusToolTipStruct &tip)
{
tip = KDbusToolTipStruct();
if ( argument.currentType() == QDBusArgument::StructureType )
{
argument.beginStructure();
argument >> tip.icon;
argument >> tip.image;
argument >> tip.title;
argument >> tip.subTitle;
argument.endStructure();
}
return argument;
}
//--------------------------------------------------------------------------------
// add "dummy" operators we never need since we'll never send icons, but qDBusRegisterMetaType needs them
// But we must correctly create the structure information
QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageStruct &pixmap)
{
argument.beginStructure();
argument << qint32(pixmap.width());
argument << qint32(pixmap.height());
argument << QByteArray();
argument.endStructure();
return argument;
}
//--------------------------------------------------------------------------------
QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageVector &)
{
argument.beginArray(qMetaTypeId());
argument.endArray();
return argument;
}
//--------------------------------------------------------------------------------
QDBusArgument &operator<<(QDBusArgument &argument, const KDbusToolTipStruct &tip)
{
argument.beginStructure();
argument << tip.icon;
argument << tip.image;
argument << tip.title;
argument << tip.subTitle;
argument.endStructure();
return argument;
}
//--------------------------------------------------------------------------------
diff --git a/DBusTypes.hxx b/DBusTypes.hxx
index 61e9d2d..aae6092 100644
--- a/DBusTypes.hxx
+++ b/DBusTypes.hxx
@@ -1,51 +1,52 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 DBusTypes_h
#define DBusTypes_h
// define types used in kf5_org.kde.StatusNotifierItem.xml
#include
#include
#include
#include
#include
typedef QPixmap KDbusImageStruct;
typedef QIcon KDbusImageVector;
struct KDbusToolTipStruct
{
QString icon;
KDbusImageVector image;
QString title;
QString subTitle;
};
Q_DECLARE_METATYPE(KDbusToolTipStruct)
const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageStruct &pixmap);
const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageVector &icon);
const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusToolTipStruct &tip);
QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageStruct &);
QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageVector &);
QDBusArgument &operator<<(QDBusArgument &argument, const KDbusToolTipStruct &);
#endif
diff --git a/DesktopApplet.cxx b/DesktopApplet.cxx
index ca09043..e477e73 100644
--- a/DesktopApplet.cxx
+++ b/DesktopApplet.cxx
@@ -1,154 +1,155 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
DesktopApplet::DesktopApplet(QWidget *parent, const QString &theId, bool addConfigureAction)
: QFrame(parent), id(theId)
{
setFrameShape(QFrame::NoFrame);
setContextMenuPolicy(Qt::ActionsContextMenu);
if ( addConfigureAction )
{
QAction *action = new QAction(this);
action->setText(i18n("Configure..."));
action->setIcon(QIcon::fromTheme("configure"));
addAction(action);
connect(action, &QAction::triggered, this, &DesktopApplet::configure);
}
QAction *action = new QAction(this);
action->setIcon(QIcon::fromTheme("preferences-system-windows-move"));
action->setText(i18n("Change Size && Position"));
addAction(action);
connect(action, &QAction::triggered, this, &DesktopApplet::startGeometryChange);
action = new QAction(this);
action->setIcon(QIcon::fromTheme("window-close"));
action->setText(i18n("Remove this Applet"));
addAction(action);
connect(action, &QAction::triggered, this, &DesktopApplet::removeThisApplet);
buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttons->adjustSize();
buttons->hide();
connect(buttons, &QDialogButtonBox::clicked, this, &DesktopApplet::finishGeometryChange);
}
//--------------------------------------------------------------------------------
void DesktopApplet::loadConfig()
{
KConfig config;
KConfigGroup group = config.group(id);
setGeometry(group.readEntry("rect", QRect(QPoint(30, 30), sizeHint())));
onDesktop = group.readEntry("onDesktop", int(NET::OnAllDesktops));
QColor textCol = group.readEntry("textCol", QColor(Qt::white));
QColor backCol = group.readEntry("backCol", QColor(32, 56, 92, 190));
QPalette pal = palette();
pal.setColor(foregroundRole(), textCol);
pal.setColor(backgroundRole(), backCol);
setPalette(pal);
}
//--------------------------------------------------------------------------------
void DesktopApplet::saveConfig()
{
KConfig config;
KConfigGroup group = config.group(id);
group.writeEntry("rect", geometry());
group.writeEntry("onDesktop", onDesktop);
group.writeEntry("textCol", palette().color(foregroundRole()));
group.writeEntry("backCol", palette().color(backgroundRole()));
}
//--------------------------------------------------------------------------------
void DesktopApplet::startGeometryChange()
{
buttons->raise();
buttons->show();
oldRect = geometry();
setWindowFlags(Qt::Window);
setWindowTitle(i18n("%1: Change Size & Position", id));
if ( onDesktop == NET::OnAllDesktops )
KWindowSystem::setOnAllDesktops(winId(), true);
show();
setGeometry(oldRect);
}
//--------------------------------------------------------------------------------
void DesktopApplet::finishGeometryChange(QAbstractButton *clicked)
{
KWindowInfo info(winId(), NET::WMDesktop);
if ( info.valid() )
onDesktop = info.desktop();
buttons->hide();
QRect rect = geometry();
setWindowFlags(Qt::Widget);
show();
if ( buttons->buttonRole(clicked) == QDialogButtonBox::AcceptRole )
{
setGeometry(rect);
KConfig config;
KConfigGroup group = config.group(id);
group.writeEntry("rect", rect);
group.writeEntry("onDesktop", onDesktop);
}
else
{
setGeometry(oldRect);
}
}
//--------------------------------------------------------------------------------
void DesktopApplet::removeThisApplet()
{
if ( QMessageBox::question(this, i18n("Remove Applet"),
i18n("Really remove this applet?")) == QMessageBox::Yes )
{
KConfig config;
config.deleteGroup(id);
emit removeThis(this);
}
}
//--------------------------------------------------------------------------------
diff --git a/DesktopApplet.hxx b/DesktopApplet.hxx
index 8c6c081..853ba5d 100644
--- a/DesktopApplet.hxx
+++ b/DesktopApplet.hxx
@@ -1,62 +1,63 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _DesktopApplet_H_
#define _DesktopApplet_H_
#include
#include
#include
class DesktopApplet : public QFrame
{
Q_OBJECT
public:
DesktopApplet(QWidget *parent, const QString &theId, bool addConfigureAction = true);
virtual void loadConfig();
bool isConfiguring() const { return buttons->isVisible(); }
const QString &getId() const { return id; }
int isOnDesktop(int d) const { return (onDesktop == NET::OnAllDesktops) || (onDesktop == d); }
public Q_SLOTS:
virtual void configure() { }
virtual void saveConfig();
Q_SIGNALS:
void removeThis(DesktopApplet *);
protected:
QString id;
private Q_SLOTS:
void startGeometryChange();
void finishGeometryChange(QAbstractButton *clicked);
void removeThisApplet();
private:
QDialogButtonBox *buttons = nullptr;
QRect oldRect;
int onDesktop = NET::OnAllDesktops;
};
#endif
diff --git a/DesktopPanel.cxx b/DesktopPanel.cxx
index 4e58ba3..1a90df9 100644
--- a/DesktopPanel.cxx
+++ b/DesktopPanel.cxx
@@ -1,117 +1,118 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
//--------------------------------------------------------------------------------
DesktopPanel::DesktopPanel(QWidget *parent)
: QFrame(parent, Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus)
{
setAttribute(Qt::WA_AlwaysShowToolTips);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setWindowIcon(QIcon::fromTheme("liquidshell"));
KWindowSystem::setState(winId(), NET::KeepAbove);
KWindowSystem::setOnAllDesktops(winId(), true);
KWindowSystem::setType(winId(), NET::Dock);
setFrameShape(QFrame::StyledPanel);
QHBoxLayout *hboxLayout = new QHBoxLayout(this);
hboxLayout->setContentsMargins(QMargins());
hboxLayout->setSpacing(2);
hboxLayout->addWidget(new StartMenu(this));
hboxLayout->addWidget(new QuickLaunch(this));
hboxLayout->addWidget(new AppMenu(this));
hboxLayout->addWidget(new Pager(this));
hboxLayout->addWidget(new WindowList(this));
hboxLayout->addWidget(new TaskBar(this));
hboxLayout->addWidget(new LockLogout(this));
hboxLayout->addWidget(new SysLoad(this));
hboxLayout->addWidget(new SysTray(this));
hboxLayout->addWidget(new ClockWidget(this));
// to get notified about num-of-rows changed
updateRowCount();
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.connect(QString(), "/KWin", "org.kde.KWin", "reloadConfig", this, SLOT(updateRowCount()));
connect(KWindowSystem::self(), &KWindowSystem::numberOfDesktopsChanged, this, &DesktopPanel::updateRowCount);
}
//--------------------------------------------------------------------------------
void DesktopPanel::updateRowCount()
{
NETRootInfo ri(QX11Info::connection(), 0, NET::WM2DesktopLayout);
int newRows = std::max(1, ri.desktopLayoutColumnsRows().height());
if ( newRows != rows )
{
rows = newRows;
emit rowsChanged(rows);
}
}
//--------------------------------------------------------------------------------
bool DesktopPanel::event(QEvent *ev)
{
bool ret = QFrame::event(ev);
if ( ev->type() == QEvent::LayoutRequest )
{
if ( sizeHint().height() != height() )
emit resized();
}
return ret;
}
//--------------------------------------------------------------------------------
void DesktopPanel::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
emit resized();
}
//--------------------------------------------------------------------------------
diff --git a/DesktopPanel.hxx b/DesktopPanel.hxx
index 9d52df3..f80e6e4 100644
--- a/DesktopPanel.hxx
+++ b/DesktopPanel.hxx
@@ -1,49 +1,50 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _DesktopPanel_H_
#define _DesktopPanel_H_
#include
class DesktopPanel : public QFrame
{
Q_OBJECT
public:
DesktopPanel(QWidget *parent);
int getRows() const { return rows; }
Q_SIGNALS:
void resized();
void rowsChanged(int rows);
protected:
bool event(QEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private Q_SLOTS:
void updateRowCount();
private:
int rows = 2;
};
#endif
diff --git a/DesktopWidget.cxx b/DesktopWidget.cxx
index 7f8c741..4a1d6c0 100644
--- a/DesktopWidget.cxx
+++ b/DesktopWidget.cxx
@@ -1,395 +1,396 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017, 2018 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()
: QWidget(nullptr, Qt::WindowDoesNotAcceptFocus)
{
setAttribute(Qt::WA_AlwaysShowToolTips);
setAutoFillBackground(true);
setFixedSize(QApplication::desktop()->size());
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(QApplication::desktop(), &QDesktopWidget::primaryScreenChanged, this, &DesktopWidget::placePanel);
connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged,
[this]() { setFixedSize(QApplication::desktop()->size()); desktopChanged(); });
connect(QApplication::desktop(), &QDesktopWidget::resized,
[this]() { setFixedSize(QApplication::desktop()->size()); 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::desktop()->screenGeometry();
panel->setGeometry(r.x(), r.y() + r.height() - panelHeight, r.width(), panelHeight);
KWindowSystem::setStrut(panel->winId(), 0, 0, 0, panelHeight);
}
//--------------------------------------------------------------------------------
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::desktop()->screenCount(); i++)
{
QRect r = QApplication::desktop()->screenGeometry(i);
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::desktop()->screenCount(); i++)
{
if ( i < wallpaper.pixmaps.count() )
{
QRect r = QApplication::desktop()->screenGeometry(i);
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 98b17f7..84cf505 100644
--- a/DesktopWidget.hxx
+++ b/DesktopWidget.hxx
@@ -1,70 +1,71 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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
};
protected:
virtual void paintEvent(QPaintEvent *event);
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;
};
#endif
diff --git a/DeviceList.cxx b/DeviceList.cxx
index bccf817..e9a249f 100644
--- a/DeviceList.cxx
+++ b/DeviceList.cxx
@@ -1,567 +1,568 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017, 2019 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
//--------------------------------------------------------------------------------
DeviceItem::DeviceItem(Solid::Device dev, const QVector &deviceActions)
: device(dev)
{
setFrameShape(QFrame::StyledPanel);
QVBoxLayout *vbox = new QVBoxLayout(this);
QHBoxLayout *hbox = new QHBoxLayout;
vbox->addLayout(hbox);
QLabel *iconLabel = new QLabel;
iconLabel->setPixmap(QIcon::fromTheme(device.icon()).pixmap(32));
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
newFlagLabel = new QLabel;
newFlagLabel->setPixmap(QIcon::fromTheme("emblem-important").pixmap(16));
newFlagLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
newFlagLabel->hide();
textLabel = new QLabel;
textLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
hbox->addWidget(iconLabel, 0, Qt::AlignLeft | Qt::AlignVCenter);
hbox->addWidget(newFlagLabel, 0, Qt::AlignCenter);
hbox->addWidget(textLabel, 0, Qt::AlignVCenter);
Solid::StorageAccess *storage = device.as();
if ( storage )
{
connect(storage, &Solid::StorageAccess::teardownDone, this, &DeviceItem::teardownDone);
connect(storage, &Solid::StorageAccess::setupDone, this, &DeviceItem::setupDone);
mountBusyTimer.setInterval(500);
connect(&mountBusyTimer, &QTimer::timeout, this,
[this]() { mountButton->setVisible(!mountButton->isVisible()); });
mountButton = new QToolButton;
mountButton->setIconSize(QSize(32, 32));
connect(mountButton, &QToolButton::clicked, mountButton,
[this]()
{
statusLabel->hide();
mountButton->setEnabled(false);
mountBusyTimer.start();
Solid::StorageAccess *storage = device.as();
if ( storage->isAccessible() ) // mounted -> unmount it
storage->teardown();
else
storage->setup();
}
);
hbox->addWidget(mountButton);
}
statusLabel = new QLabel;
statusLabel->setWordWrap(true);
statusLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
statusLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
vbox->addWidget(statusLabel);
statusLabel->hide();
statusTimer.setSingleShot(true);
statusTimer.setInterval(60000);
connect(&statusTimer, &QTimer::timeout, statusLabel, &QLabel::hide);
fillData();
// append actions
for (const DeviceAction &action : deviceActions)
{
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addSpacing(iconLabel->sizeHint().width());
IconButton *button = new IconButton;
button->setIcon(QIcon::fromTheme(action.action.icon()));
button->setText(action.action.text() + " (" + QFileInfo(action.path).baseName() + ")");
connect(button, &IconButton::clicked, button,
[action, this]()
{
QString command = action.action.exec();
Solid::StorageAccess *storage = device.as();
if ( storage )
{
if ( !storage->isAccessible() )
{
statusLabel->hide();
storage->setup();
return;
}
command.replace("%f", storage->filePath());
}
if ( device.is() )
command.replace("%d", device.as()->device());
command.replace("%i", device.udi());
KRun::runCommand(command, this);
window()->hide();
}
);
hbox->addWidget(button);
vbox->addLayout(hbox);
}
}
//--------------------------------------------------------------------------------
DeviceItem::DeviceItem(const KdeConnect::Device &dev)
{
setFrameShape(QFrame::StyledPanel);
QVBoxLayout *vbox = new QVBoxLayout(this);
QHBoxLayout *hbox = new QHBoxLayout;
vbox->addLayout(hbox);
QLabel *iconLabel = new QLabel;
iconLabel->setPixmap(dev->icon.pixmap(32));
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
newFlagLabel = new QLabel;
newFlagLabel->setPixmap(QIcon::fromTheme("emblem-important").pixmap(16));
newFlagLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
newFlagLabel->hide();
textLabel = new QLabel;
textLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
hbox->addWidget(iconLabel, 0, Qt::AlignLeft | Qt::AlignVCenter);
hbox->addWidget(newFlagLabel, 0, Qt::AlignCenter);
hbox->addWidget(textLabel, 0, Qt::AlignVCenter);
statusLabel = new QLabel;
hbox->addWidget(statusLabel, 0, Qt::AlignVCenter);
QLabel *chargeIcon = new QLabel;
hbox->addWidget(chargeIcon, 0, Qt::AlignVCenter);
if ( dev->charge >= 0 )
{
statusLabel->setText(QString::number(dev->charge) + '%');
chargeIcon->setPixmap(dev->chargeIcon.pixmap(22));
}
connect(dev.data(), &KdeConnectDevice::changed, this,
[this, chargeIcon, dev]()
{
textLabel->setText(dev->name);
if ( dev->charge >= 0 )
{
statusLabel->setText(QString::number(dev->charge) + '%');
chargeIcon->setPixmap(dev->chargeIcon.pixmap(22));
}
});
if ( dev->plugins.contains("kdeconnect_findmyphone") )
{
QToolButton *ringButton = new QToolButton;
ringButton->setIcon(QIcon::fromTheme("preferences-desktop-notification-bell"));
connect(ringButton, &QToolButton::clicked, dev.data(), [dev]() { dev->ringPhone(); });
hbox->addWidget(ringButton, 0, Qt::AlignVCenter);
}
QToolButton *configure = new QToolButton;
configure->setIcon(QIcon::fromTheme("configure"));
connect(configure, &QToolButton::clicked, configure,
[this]()
{
if ( !dialog )
{
dialog = new KCMultiDialog(nullptr);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->addModule("kcm_kdeconnect");
dialog->setWindowTitle(i18n("KDE Connect"));
dialog->adjustSize();
}
dialog->show();
});
hbox->addWidget(configure, 0, Qt::AlignVCenter);
textLabel->setText(dev->name);
if ( dev->plugins.contains("kdeconnect_sftp") )
{
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addSpacing(iconLabel->sizeHint().width());
IconButton *button = new IconButton;
button->setIcon(QIcon::fromTheme("system-file-manager"));
button->setText(i18n("Open with File Manager"));
connect(button, &IconButton::clicked,
[dev]() { new KRun(QUrl(QLatin1String("kdeconnect://") + dev->id), nullptr); });
hbox->addWidget(button);
vbox->addLayout(hbox);
}
}
//--------------------------------------------------------------------------------
void DeviceItem::markAsNew()
{
newFlagLabel->show();
QTimer::singleShot(5000, newFlagLabel, &QLabel::hide);
}
//--------------------------------------------------------------------------------
void DeviceItem::fillData()
{
Solid::StorageAccess *storage = device.as();
QString text = device.description();
if ( !device.product().isEmpty() && (device.product() != text) )
text += " (" + device.product() + ")";
else if ( !device.vendor().isEmpty() && (device.vendor() != text) )
text += " (" + device.vendor() + ")";
Solid::StorageVolume *volume = device.as();
if ( volume )
text += " " + KIO::convertSize(volume->size());
if ( storage && !storage->filePath().isEmpty() )
text += '\n' + storage->filePath();
textLabel->setText(text);
if ( mountButton )
{
if ( storage && storage->isAccessible() )
mountButton->setIcon(QIcon::fromTheme("media-eject"));
else
{
if ( device.emblems().count() )
mountButton->setIcon(QIcon::fromTheme(device.emblems()[0]));
else
mountButton->setIcon(QIcon::fromTheme("emblem-unmounted"));
}
if ( storage )
{
mountButton->setToolTip(storage->isAccessible() ?
i18n("Device is mounted.\nClick to unmount/eject") :
i18n("Device is unmounted.\nClick to mount"));
}
}
}
//--------------------------------------------------------------------------------
QString DeviceItem::errorToString(Solid::ErrorType error)
{
switch ( error )
{
case Solid::UnauthorizedOperation: return i18n("Unauthorized Operation");
case Solid::DeviceBusy: return i18n("Device Busy");
case Solid::OperationFailed: return i18n("Operation Failed");
case Solid::UserCanceled: return i18n("User Canceled");
case Solid::InvalidOption: return i18n("Invalid Option");
case Solid::MissingDriver: return i18n("Missing Driver");
default: return QString();
}
}
//--------------------------------------------------------------------------------
void DeviceItem::mountDone(Action action, Solid::ErrorType error, QVariant errorData, const QString &udi)
{
Q_UNUSED(udi)
mountBusyTimer.stop();
mountButton->setEnabled(true);
mountButton->setVisible(true);
if ( error == Solid::NoError )
{
fillData();
if ( action == Unmount )
{
statusLabel->setText(i18n("The device can now be safely removed"));
statusLabel->show();
statusTimer.start();
}
}
else
{
QString text = (action == Mount) ? i18n("Mount failed:") : i18n("Unmount failed:");
statusLabel->setText("" + text + "" + errorToString(error) + "
" + errorData.toString());
statusLabel->show();
statusTimer.start();
}
}
//--------------------------------------------------------------------------------
void DeviceItem::teardownDone(Solid::ErrorType error, QVariant errorData, const QString &udi)
{
mountDone(Unmount, error, errorData, udi);
}
//--------------------------------------------------------------------------------
void DeviceItem::setupDone(Solid::ErrorType error, QVariant errorData, const QString &udi)
{
mountDone(Mount, error, errorData, udi);
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
DeviceList::DeviceList(QWidget *parent)
: QScrollArea(parent)
{
setWindowFlags(windowFlags() | Qt::Tool);
setFrameShape(QFrame::StyledPanel);
setAttribute(Qt::WA_AlwaysShowToolTips);
setWidgetResizable(true);
loadActions();
QWidget *widget = new QWidget;
vbox = new QVBoxLayout(widget);
vbox->setContentsMargins(QMargins());
vbox->addStretch();
vbox->setSizeConstraint(QLayout::SetMinAndMaxSize);
setWidget(widget);
predicate = Solid::Predicate(Solid::DeviceInterface::StorageAccess);
predicate |= Solid::Predicate(Solid::DeviceInterface::StorageDrive);
predicate |= Solid::Predicate(Solid::DeviceInterface::StorageVolume);
predicate |= Solid::Predicate(Solid::DeviceInterface::OpticalDrive);
predicate |= Solid::Predicate(Solid::DeviceInterface::OpticalDisc);
predicate |= Solid::Predicate(Solid::DeviceInterface::PortableMediaPlayer);
predicate |= Solid::Predicate(Solid::DeviceInterface::Camera);
QList devices = Solid::Device::listFromQuery(predicate);
for (Solid::Device device : devices)
addDevice(device);
connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded,
this, &DeviceList::deviceAdded);
connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved,
this, &DeviceList::deviceRemoved);
connect(&kdeConnect, &KdeConnect::deviceAdded, this, &DeviceList::kdeConnectDeviceAdded);
connect(&kdeConnect, &KdeConnect::deviceRemoved, this, &DeviceList::deviceRemoved);
}
//--------------------------------------------------------------------------------
QSize DeviceList::sizeHint() const
{
// avoid horizontal scrollbar when the list is higher than 2/3 of the screen
// where a vertical scrollbar will be shown, reducing the available width
// leading to also getting a horizontal scrollbar
QSize s = widget()->sizeHint() + QSize(2 * frameWidth(), 2 * frameWidth());
s.setWidth(s.width() + verticalScrollBar()->sizeHint().width());
return s;
}
//--------------------------------------------------------------------------------
void DeviceList::loadActions()
{
actions.clear();
const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "solid/actions", QStandardPaths::LocateDirectory);
for (const QString &dirPath : dirs)
{
QDir dir(dirPath);
for (const QString &file : dir.entryList(QStringList(QLatin1String("*.desktop")), QDir::Files))
{
QString path = dir.absoluteFilePath(file);
KDesktopFile cfg(path);
const QString predicateString = cfg.desktopGroup().readEntry("X-KDE-Solid-Predicate");
QList actionList = KDesktopFileActions::userDefinedServices(path, true);
if ( !actionList.isEmpty() && !predicateString.isEmpty() )
actions.append(DeviceAction(path, Solid::Predicate::fromString(predicateString), actionList[0]));
}
}
}
//--------------------------------------------------------------------------------
DeviceItem *DeviceList::addDevice(Solid::Device device)
{
if ( items.contains(device.udi()) )
{
//qDebug() << device.udi() << "already known";
return nullptr;
}
QVector deviceActions;
for (const DeviceAction &action : actions)
{
if ( action.predicate.matches(device) )
deviceActions.append(action);
}
Solid::StorageVolume *storage = device.as();
if ( !storage ) // storage can at least be mounted; others need some specific actions
{
if ( deviceActions.isEmpty() )
{
//qDebug() << device.udi() << "no action found";
return nullptr;
}
}
else if ( storage->usage() != Solid::StorageVolume::FileSystem )
{
//qDebug() << device.udi() << "storage no filesystem";
return nullptr;
}
// show only removable devices
if ( device.is() &&
!device.as()->isRemovable() )
{
//qDebug() << device.udi() << "not Removable";
return nullptr;
}
// show only removable devices
if ( device.parent().is() &&
!device.parent().as()->isRemovable() )
{
//qDebug() << device.parent().udi() << "parent() not Removable";
return nullptr;
}
DeviceItem *item = new DeviceItem(device, deviceActions);
vbox->insertWidget(vbox->count() - 1, item); // insert before stretch
items.insert(device.udi(), item);
return item;
}
//--------------------------------------------------------------------------------
void DeviceList::deviceAdded(const QString &dev)
{
Solid::Device device(dev);
if ( !predicate.matches(device) )
return;
DeviceItem *item = addDevice(device);
// when we added a new device, make sure the DeviceNotifier shows and places this window
if ( item )
{
item->markAsNew();
item->show();
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
emit deviceWasAdded();
}
}
//--------------------------------------------------------------------------------
void DeviceList::deviceRemoved(const QString &dev)
{
if ( items.contains(dev) )
{
delete items.take(dev);
emit deviceWasRemoved();
}
}
//--------------------------------------------------------------------------------
void DeviceList::kdeConnectDeviceAdded(const KdeConnect::Device &device)
{
if ( items.contains(device->id) )
{
//qDebug() << device->id << "already known";
return;
}
DeviceItem *item = new DeviceItem(device);
vbox->insertWidget(vbox->count() - 1, item); // insert before stretch
items.insert(device->id, item);
item->markAsNew();
item->show();
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
// when we added a new device, make sure the DeviceNotifier shows and places this window
emit deviceWasAdded();
}
//--------------------------------------------------------------------------------
diff --git a/DeviceList.hxx b/DeviceList.hxx
index dc37f10..7175636 100644
--- a/DeviceList.hxx
+++ b/DeviceList.hxx
@@ -1,119 +1,120 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017, 2019 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 _DeviceList_H_
#define _DeviceList_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------
struct DeviceAction
{
DeviceAction() { }
DeviceAction(const QString &filePath, Solid::Predicate p, KServiceAction a)
: path(filePath), predicate(p), action(a) { }
QString path;
Solid::Predicate predicate;
KServiceAction action;
};
//--------------------------------------------------------------------------------
class DeviceItem : public QFrame
{
Q_OBJECT
public:
DeviceItem(Solid::Device dev, const QVector &deviceActions);
DeviceItem(const KdeConnect::Device &dev);
void markAsNew();
private:
static QString errorToString(Solid::ErrorType error);
void fillData();
enum Action { Mount, Unmount };
void mountDone(Action action, Solid::ErrorType error, QVariant errorData, const QString &udi);
private Q_SLOTS:
void teardownDone(Solid::ErrorType error, QVariant errorData, const QString &udi);
void setupDone(Solid::ErrorType error, QVariant errorData, const QString &udi);
private:
Solid::Device device;
QToolButton *mountButton = nullptr;
QLabel *textLabel = nullptr, *statusLabel = nullptr, *newFlagLabel = nullptr;
QTimer statusTimer, mountBusyTimer;
QPointer dialog;
};
//--------------------------------------------------------------------------------
class DeviceList : public QScrollArea
{
Q_OBJECT
public:
DeviceList(QWidget *parent);
bool isEmpty() const { return items.isEmpty(); }
QSize sizeHint() const override;
Q_SIGNALS:
void deviceWasAdded();
void deviceWasRemoved();
private Q_SLOTS:
void deviceAdded(const QString &dev);
void deviceRemoved(const QString &dev);
void kdeConnectDeviceAdded(const KdeConnect::Device &dev);
private:
void loadActions();
DeviceItem *addDevice(Solid::Device device);
private:
QVBoxLayout *vbox;
QMap items;
Solid::Predicate predicate;
QVector actions;
KdeConnect kdeConnect;
};
#endif
diff --git a/DeviceNotifier.cxx b/DeviceNotifier.cxx
index 6bcdd38..e0c1178 100644
--- a/DeviceNotifier.cxx
+++ b/DeviceNotifier.cxx
@@ -1,94 +1,95 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017, 2019 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
//--------------------------------------------------------------------------------
DeviceNotifier::DeviceNotifier(QWidget *parent)
: SysTrayItem(parent, "device-notifier")
{
setToolTip(i18n("Device Notifier"));
deviceList = new DeviceList(this);
deviceList->setWindowTitle(i18n("Device List"));
if ( deviceList->isEmpty() )
hide();
connect(deviceList, &DeviceList::deviceWasRemoved, this, &DeviceNotifier::checkDeviceList);
connect(deviceList, &DeviceList::deviceWasAdded,
[this]()
{
if ( !deviceList->isVisible() )
timer.start(); // auto-hide
showDetailsList();
});
// if the user did not activate the device list window, auto-hide it
timer.setInterval(4000);
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, deviceList, &DeviceList::hide);
deviceList->installEventFilter(this);
}
//--------------------------------------------------------------------------------
QWidget *DeviceNotifier::getDetailsList()
{
deviceList->adjustSize();
deviceList->resize(deviceList->size().expandedTo(QSize(300, 100)));
setVisible(!deviceList->isEmpty());
return deviceList;
}
//--------------------------------------------------------------------------------
void DeviceNotifier::checkDeviceList()
{
if ( deviceList->isEmpty() )
{
deviceList->hide();
hide();
}
else if ( deviceList->isVisible() )
showDetailsList(); // reposition
}
//--------------------------------------------------------------------------------
bool DeviceNotifier::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched)
if ( event->type() == QEvent::WindowActivate )
timer.stop();
return false;
}
//--------------------------------------------------------------------------------
diff --git a/DeviceNotifier.hxx b/DeviceNotifier.hxx
index dc81001..6076871 100644
--- a/DeviceNotifier.hxx
+++ b/DeviceNotifier.hxx
@@ -1,46 +1,47 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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 _DeviceNotifier_H_
#define _DeviceNotifier_H_
#include
#include
class DeviceList;
class DeviceNotifier : public SysTrayItem
{
Q_OBJECT
public:
DeviceNotifier(QWidget *parent);
protected:
QWidget *getDetailsList() override;
bool eventFilter(QObject *watched, QEvent *event) override;
private Q_SLOTS:
void checkDeviceList();
private:
DeviceList *deviceList = nullptr;
QTimer timer;
};
#endif
diff --git a/DiskUsageApplet.cxx b/DiskUsageApplet.cxx
index 972b165..238d1ee 100644
--- a/DiskUsageApplet.cxx
+++ b/DiskUsageApplet.cxx
@@ -1,153 +1,154 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
Copyright 2017 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