diff --git a/AppMenu.cxx b/AppMenu.cxx index 9e50994..f9e2c34 100644 --- a/AppMenu.cxx +++ b/AppMenu.cxx @@ -1,224 +1,194 @@ /* 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(); - AppButton *entryButton = new AppButton(this, icon, name); - entryButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - connect(entryButton, &AppButton::clicked, [this, url]() { popup->close(); new KRun(url, nullptr); }); + 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(); } //-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- -// helper class to ensure the positioning of the icon and text independent of the style - -#include - -AppButton::AppButton(QWidget *parent, const QIcon &icon, const QString &name) - : QToolButton(parent) -{ - setAutoRaise(true); - QHBoxLayout *hbox = new QHBoxLayout(this); - - QLabel *iconLabel = new QLabel; - iconLabel->setContextMenuPolicy(Qt::PreventContextMenu); - iconLabel->setFixedSize(32, 32); - iconLabel->setPixmap(icon.pixmap(QSize(32, 32))); - hbox->addWidget(iconLabel); - - QLabel *textLabel = new QLabel(name); - hbox->addWidget(textLabel); -} - -//-------------------------------------------------------------------------------- - -QSize AppButton::sizeHint() const -{ - return layout()->sizeHint(); -} - -//-------------------------------------------------------------------------------- diff --git a/AppMenu.hxx b/AppMenu.hxx index 5bd2c2b..9bfb17d 100644 --- a/AppMenu.hxx +++ b/AppMenu.hxx @@ -1,81 +1,70 @@ /* 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; }; -//-------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------- - -class AppButton : public QToolButton -{ - public: - AppButton(QWidget *parent, const QIcon &icon, const QString &name); - - QSize sizeHint() const override; -}; - //-------------------------------------------------------------------------------- #endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 8911248..3694d53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,130 +1,131 @@ cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) project(liquidshell) find_package(ECM 1.3.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) find_package(Qt5 5.6 CONFIG REQUIRED COMPONENTS Core Widgets DBus X11Extras ) find_package(KF5 REQUIRED COMPONENTS WindowSystem WidgetsAddons ConfigWidgets Config KIO IconThemes ItemViews Archive Notifications I18n NetworkManagerQt Service Solid BluezQt KCMUtils Crash DBusAddons NewStuff ) find_package(packagekitqt5 REQUIRED) set(SOURCES desktop.cxx DesktopWidget.cxx DesktopPanel.cxx OnScreenVolume.cxx ConfigureDesktopDialog.cxx StartMenu.cxx Launcher.cxx QuickLaunch.cxx + IconButton.cxx AppMenu.cxx Pager.cxx PagerButton.cxx WindowList.cxx ClockWidget.cxx ClockWidgetConfigureDialog.cxx TaskBar.cxx TaskBarButton.cxx LockLogout.cxx SysTray.cxx SysTrayItem.cxx SysTrayNotifyItem.cxx DBusTypes.cxx SysLoad.cxx NotificationServer.cxx NotificationList.cxx Network.cxx NetworkList.cxx DeviceNotifier.cxx DeviceList.cxx Battery.cxx Bluetooth.cxx PopupMenu.cxx PkUpdates.cxx PkUpdateList.cxx KdeConnect.cxx DesktopApplet.cxx WeatherApplet.cxx WeatherAppletConfigureDialog.cxx DiskUsageApplet.cxx DiskUsageAppletConfigureDialog.cxx ) # e.g. on openSuse Leap 42.3 compile fails as a GLib header included uses signals as var add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) ki18n_wrap_ui(UI_FILES ConfigureDesktopDialog.ui WeatherAppletConfigureDialog.ui DiskUsageAppletConfigureDialog.ui ) set(statusnotifieritem_xml ${KNOTIFICATIONS_DBUS_INTERFACES_DIR}/kf5_org.kde.StatusNotifierItem.xml) set_source_files_properties(${statusnotifieritem_xml} PROPERTIES INCLUDE "DBusTypes.hxx" CLASSNAME OrgKdeStatusNotifierItem ) qt5_add_dbus_interface(SOURCES ${statusnotifieritem_xml} statusnotifieritem_interface) qt5_add_dbus_adaptor(SOURCES org.freedesktop.Notifications.xml NotificationServer.hxx NotificationServer) set(TARGET liquidshell) add_executable(${TARGET} ${SOURCES} ${UI_FILES}) set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 11) target_link_libraries(${TARGET} Qt5::Core Qt5::Widgets Qt5::DBus Qt5::X11Extras KF5::WindowSystem KF5::WidgetsAddons KF5::ConfigWidgets KF5::ConfigCore KF5::KIOCore KF5::KIOWidgets KF5::IconThemes KF5::Notifications KF5::I18n KF5::NetworkManagerQt KF5::Service KF5::Solid KF5::BluezQt KF5::KCMUtils KF5::Crash KF5::DBusAddons KF5::ItemViews KF5::Archive KF5::NewStuff PK::packagekitqt5 ) install(TARGETS ${TARGET} ${INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS org.kde.${TARGET}.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES liquidshell.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) ecm_install_icons(ICONS 48-apps-liquidshell.png DESTINATION ${ICON_INSTALL_DIR} THEME hicolor) diff --git a/DeviceList.cxx b/DeviceList.cxx index ce0070c..8862d3c 100644 --- a/DeviceList.cxx +++ b/DeviceList.cxx @@ -1,539 +1,534 @@ /* 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 //-------------------------------------------------------------------------------- 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); mountButton = new QToolButton; mountButton->setIconSize(QSize(32, 32)); connect(mountButton, &QToolButton::clicked, mountButton, [this]() { statusLabel->hide(); 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()); - QToolButton *button = new QToolButton; - button->setAutoRaise(true); - button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - button->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + IconButton *button = new IconButton; button->setIcon(QIcon::fromTheme(action.action.icon())); button->setText(action.action.text() + " (" + QFileInfo(action.path).baseName() + ")"); - connect(button, &QToolButton::clicked, button, + 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()); - QToolButton *button = new QToolButton; - button->setAutoRaise(true); - button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - button->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + IconButton *button = new IconButton; button->setIcon(QIcon::fromTheme("system-file-manager")); button->setText(i18n("Open with File Manager")); - connect(button, &QToolButton::clicked, + 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) 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) : QFrame(parent) { setWindowFlags(windowFlags() | Qt::Tool); setFrameShape(QFrame::StyledPanel); setAttribute(Qt::WA_AlwaysShowToolTips); loadActions(); vbox = new QVBoxLayout(this); vbox->setContentsMargins(QMargins()); vbox->addStretch(); 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); } //-------------------------------------------------------------------------------- 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(); 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(); // when we added a new device, make sure the DeviceNotifier shows and places this window emit deviceWasAdded(); } //-------------------------------------------------------------------------------- diff --git a/IconButton.cxx b/IconButton.cxx new file mode 100644 index 0000000..198671c --- /dev/null +++ b/IconButton.cxx @@ -0,0 +1,59 @@ +#include + +#include + +//-------------------------------------------------------------------------------- + +IconButton::IconButton(QWidget *parent) + : QToolButton(parent) +{ + setAutoRaise(true); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + + QHBoxLayout *hbox = new QHBoxLayout(this); + + iconLabel = new QLabel; + iconLabel->setFixedSize(iconSize()); + iconLabel->setContextMenuPolicy(Qt::PreventContextMenu); + hbox->addWidget(iconLabel); + + textLabel = new QLabel; + hbox->addWidget(textLabel); +} + +//-------------------------------------------------------------------------------- + +IconButton::IconButton(QWidget *parent, const QIcon &icon, int theIconSize, const QString &name) + : IconButton(parent) +{ + if ( theIconSize != -1 ) + setIconSize(QSize(theIconSize, theIconSize)); + + iconLabel->setFixedSize(iconSize()); + iconLabel->setPixmap(icon.pixmap(iconSize())); + + textLabel->setText(name); +} + +//-------------------------------------------------------------------------------- + +void IconButton::setText(const QString &txt) +{ + textLabel->setText(txt); +} + +//-------------------------------------------------------------------------------- + +void IconButton::setIcon(const QIcon &icon) +{ + iconLabel->setPixmap(icon.pixmap(iconSize())); +} + +//-------------------------------------------------------------------------------- + +QSize IconButton::sizeHint() const +{ + return layout()->sizeHint(); +} + +//-------------------------------------------------------------------------------- diff --git a/IconButton.hxx b/IconButton.hxx new file mode 100644 index 0000000..873508e --- /dev/null +++ b/IconButton.hxx @@ -0,0 +1,27 @@ +#ifndef _IconButton_H_ +#define _IconButton_H_ + +#include +#include + +// button class to ensure the positioning of the icon and text independent of the style + +class IconButton : public QToolButton +{ + Q_OBJECT + + public: + IconButton(QWidget *parent = nullptr); + IconButton(QWidget *parent, const QIcon &icon, int iconSize, const QString &name); + + void setText(const QString &txt); + void setIcon(const QIcon &icon); + + QSize sizeHint() const override; + + private: + QLabel *iconLabel = nullptr; + QLabel *textLabel = nullptr; +}; + +#endif diff --git a/NetworkList.cxx b/NetworkList.cxx index ec12a7a..946502e 100644 --- a/NetworkList.cxx +++ b/NetworkList.cxx @@ -1,289 +1,286 @@ /* 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 //-------------------------------------------------------------------------------- NetworkButton::NetworkButton(NetworkManager::Connection::Ptr c, NetworkManager::Device::Ptr dev) : connection(c), device(dev) { - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - setAutoRaise(true); setCheckable(true); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); if ( connection ) { for (const NetworkManager::ActiveConnection::Ptr &ac : NetworkManager::activeConnections()) { if ( ac->uuid() == c->uuid() ) { setChecked(true); break; } } connect(this, &NetworkButton::toggled, this, &NetworkButton::toggleNetworkStatus); } else setEnabled(false); } //-------------------------------------------------------------------------------- void NetworkButton::toggleNetworkStatus(bool on) { if ( on ) { 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 { 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); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); network = new QToolButton; network->setIcon(QIcon::fromTheme("network-wired")); network->setCheckable(true); connect(network, &QToolButton::toggled, [](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->setCheckable(true); connect(wireless, &QToolButton::toggled, [](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")); connect(configure, &QToolButton::clicked, this, &NetworkList::openConfigureDialog); hbox->addWidget(configure); // show connections connectionsVbox = new QVBoxLayout; connectionsVbox->setContentsMargins(QMargins()); vbox->addLayout(connectionsVbox); fillConnections(); QTimer *checkConnectionsTimer = new QTimer(this); checkConnectionsTimer->setInterval(1000); connect(checkConnectionsTimer, &QTimer::timeout, this, &NetworkList::fillConnections); checkConnectionsTimer->start(); } //-------------------------------------------------------------------------------- void NetworkList::openConfigureDialog() { hide(); // 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); } //-------------------------------------------------------------------------------- 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(); for (const NetworkManager::Connection::Ptr c : allConnections) { if ( !c->isValid() ) continue; if ( (c->settings()->connectionType() == NetworkManager::ConnectionSettings::Wired) && !c->uuid().isEmpty() ) { - QToolButton *net = new NetworkButton(c); + NetworkButton *net = new NetworkButton(c); net->setText(c->name()); net->setIcon(QIcon::fromTheme("network-wired")); connectionsVbox->addWidget(net); } else if ( c->settings()->connectionType() == NetworkManager::ConnectionSettings::Vpn ) { - QToolButton *vpn = new NetworkButton(c); + NetworkButton *vpn = new NetworkButton(c); vpn->setText(c->name()); vpn->setIcon(QIcon::fromTheme("security-high")); connectionsVbox->addWidget(vpn); } } // 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 bool haveConnection = false; 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() ) { haveConnection = true; conn = c; break; } } } if ( haveConnection ) { - QToolButton *net = new NetworkButton(conn, device); + NetworkButton *net = new NetworkButton(conn, device); net->setText(QString("%1 (%2%)").arg(network->ssid()).arg(network->signalStrength())); net->setIcon(QIcon::fromTheme("network-wireless")); connectionsVbox->addWidget(net); } else { - QToolButton *net = new NetworkButton; + NetworkButton *net = new NetworkButton; net->setText(QString("%1 (%2%)").arg(network->ssid()).arg(network->signalStrength())); net->setIcon(QIcon::fromTheme("network-wireless")); net->setEnabled(false); // TODO: allow to add a new connection. See NetworkManager::addAndActivateConnection connectionsVbox->addWidget(net); } /* NetworkManager::WirelessSecurityType security = NetworkManager::findBestWirelessSecurity(wifiDevice->wirelessCapabilities(), true, wifiDevice->mode() == NetworkManager::WirelessDevice::Adhoc, accessPoint->capabilities(), accessPoint->wpaFlags(), accessPoint->rsnFlags()); if ( (security != NetworkManager::UnknownSecurity) && (security != NetworkManager::NoneSecurity) ) net->setIcon(QIcon::fromTheme("object-locked")); else net->setIcon(QIcon::fromTheme("object-unlocked")); */ } } } connectionsVbox->addStretch(); adjustSize(); emit changed(); } //-------------------------------------------------------------------------------- diff --git a/NetworkList.hxx b/NetworkList.hxx index 924ba36..e16ec3a 100644 --- a/NetworkList.hxx +++ b/NetworkList.hxx @@ -1,67 +1,68 @@ /* 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 _NetworkList_H_ #define _NetworkList_H_ #include #include #include class NetworkList : public QFrame { Q_OBJECT public: NetworkList(QWidget *parent); Q_SIGNALS: void changed(); private Q_SLOTS: void statusUpdate(); void fillConnections(); void openConfigureDialog(); private: QToolButton *network, *wireless; QVBoxLayout *connectionsVbox; }; //-------------------------------------------------------------------------------- #include +#include -class NetworkButton : public QToolButton +class NetworkButton : public IconButton { Q_OBJECT public: NetworkButton(NetworkManager::Connection::Ptr c = NetworkManager::Connection::Ptr(), NetworkManager::Device::Ptr dev = NetworkManager::Device::Ptr()); private Q_SLOTS: void toggleNetworkStatus(bool on); private: NetworkManager::Connection::Ptr connection; NetworkManager::Device::Ptr device; }; #endif