From 25b2e77aa2395ba9143683a5ce1a27b99ee7a211 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Wed, 4 Jan 2017 16:44:55 -0500 Subject: Create a new "lumina-desktop-unified" core subproject (DO NOT USE) This is just a staging area for the merging of the desktop, window manager, etc.. into a single unified application. It is highly fragmented right now and will not build *AT ALL* for a while. --- .../core/lumina-desktop-unified/src-DE/AppMenu.cpp | 183 ++++ .../core/lumina-desktop-unified/src-DE/AppMenu.h | 58 + .../core/lumina-desktop-unified/src-DE/JsonMenu.h | 79 ++ .../lumina-desktop-unified/src-DE/LDesktop.cpp | 553 ++++++++++ .../core/lumina-desktop-unified/src-DE/LDesktop.h | 107 ++ .../src-DE/LDesktopBackground.cpp | 90 ++ .../src-DE/LDesktopBackground.h | 27 + .../src-DE/LDesktopPluginSpace.cpp | 333 ++++++ .../src-DE/LDesktopPluginSpace.h | 303 ++++++ .../core/lumina-desktop-unified/src-DE/LPanel.cpp | 399 +++++++ .../core/lumina-desktop-unified/src-DE/LPanel.h | 81 ++ .../lumina-desktop-unified/src-DE/LSession.cpp | 798 ++++++++++++++ .../core/lumina-desktop-unified/src-DE/LSession.h | 193 ++++ .../lumina-desktop-unified/src-DE/LWinInfo.cpp | 48 + .../core/lumina-desktop-unified/src-DE/LWinInfo.h | 50 + .../src-DE/LXcbEventFilter.cpp | 118 ++ .../src-DE/LXcbEventFilter.h | 104 ++ .../lumina-desktop-unified/src-DE/SettingsMenu.cpp | 67 ++ .../lumina-desktop-unified/src-DE/SettingsMenu.h | 28 + .../lumina-desktop-unified/src-DE/SystemWindow.cpp | 104 ++ .../lumina-desktop-unified/src-DE/SystemWindow.h | 46 + .../lumina-desktop-unified/src-DE/SystemWindow.ui | 194 ++++ .../src-DE/desktop-plugins/LDPlugin.cpp | 63 ++ .../src-DE/desktop-plugins/LDPlugin.h | 156 +++ .../src-DE/desktop-plugins/NewDP.h | 63 ++ .../src-DE/desktop-plugins/SamplePlugin.h | 38 + .../applauncher/AppLauncherPlugin.cpp | 145 +++ .../applauncher/AppLauncherPlugin.h | 59 + .../applauncher/OutlineToolButton.h | 99 ++ .../desktop-plugins/audioplayer/PlayerWidget.cpp | 271 +++++ .../desktop-plugins/audioplayer/PlayerWidget.h | 84 ++ .../desktop-plugins/audioplayer/PlayerWidget.ui | 182 ++++ .../desktop-plugins/calendar/CalendarPlugin.h | 59 + .../src-DE/desktop-plugins/desktop-plugins.pri | 24 + .../desktopview/DesktopViewPlugin.cpp | 214 ++++ .../desktopview/DesktopViewPlugin.h | 55 + .../messagecenter/LXDG-DBusNotifier.h | 17 + .../messagecenter/MessageCenter.cpp | 90 ++ .../desktop-plugins/messagecenter/MessageCenter.h | 48 + .../desktop-plugins/notepad/NotepadPlugin.cpp | 330 ++++++ .../src-DE/desktop-plugins/notepad/NotepadPlugin.h | 66 ++ .../desktop-plugins/quickcontainer/QuickDPlugin.h | 51 + .../desktop-plugins/rssreader/RSSFeedPlugin.cpp | 363 +++++++ .../desktop-plugins/rssreader/RSSFeedPlugin.h | 72 ++ .../desktop-plugins/rssreader/RSSFeedPlugin.ui | 552 ++++++++++ .../desktop-plugins/rssreader/RSSObjects.cpp | 287 +++++ .../src-DE/desktop-plugins/rssreader/RSSObjects.h | 105 ++ .../systemmonitor/MonitorWidget.cpp | 63 ++ .../desktop-plugins/systemmonitor/MonitorWidget.h | 62 ++ .../desktop-plugins/systemmonitor/MonitorWidget.ui | 143 +++ .../src-DE/panel-plugins/LPPlugin.h | 77 ++ .../src-DE/panel-plugins/LTBWidget.h | 71 ++ .../src-DE/panel-plugins/NewPP.h | 82 ++ .../src-DE/panel-plugins/RotateToolButton.h | 58 + .../panel-plugins/applauncher/AppLaunchButton.cpp | 77 ++ .../panel-plugins/applauncher/AppLaunchButton.h | 63 ++ .../panel-plugins/appmenu/LAppMenuPlugin.cpp | 142 +++ .../src-DE/panel-plugins/appmenu/LAppMenuPlugin.h | 64 ++ .../panel-plugins/audioplayer/LPAudioPlayer.cpp | 30 + .../panel-plugins/audioplayer/LPAudioPlayer.h | 49 + .../panel-plugins/audioplayer/PPlayerWidget.cpp | 258 +++++ .../panel-plugins/audioplayer/PPlayerWidget.h | 59 + .../panel-plugins/audioplayer/PPlayerWidget.ui | 182 ++++ .../src-DE/panel-plugins/battery/LBattery.cpp | 115 ++ .../src-DE/panel-plugins/battery/LBattery.h | 53 + .../src-DE/panel-plugins/battery/NOTES | 49 + .../src-DE/panel-plugins/clock/LClock.cpp | 230 ++++ .../src-DE/panel-plugins/clock/LClock.h | 58 + .../src-DE/panel-plugins/desktopbar/LDeskBar.cpp | 207 ++++ .../src-DE/panel-plugins/desktopbar/LDeskBar.h | 88 ++ .../desktopswitcher/LDesktopSwitcher.cpp | 142 +++ .../desktopswitcher/LDesktopSwitcher.h | 72 ++ .../src-DE/panel-plugins/jsonmenu/PPJsonMenu.cpp | 35 + .../src-DE/panel-plugins/jsonmenu/PPJsonMenu.h | 42 + .../src-DE/panel-plugins/line/LLine.h | 40 + .../src-DE/panel-plugins/panel-plugins.pri | 57 + .../panel-plugins/quickcontainer/QuickPPlugin.h | 43 + .../panel-plugins/showdesktop/LHomeButton.cpp | 43 + .../src-DE/panel-plugins/showdesktop/LHomeButton.h | 62 ++ .../src-DE/panel-plugins/spacer/LSpacer.h | 34 + .../systemdashboard/LSysDashboard.cpp | 91 ++ .../panel-plugins/systemdashboard/LSysDashboard.h | 76 ++ .../panel-plugins/systemdashboard/SysMenuQuick.cpp | 211 ++++ .../panel-plugins/systemdashboard/SysMenuQuick.h | 54 + .../panel-plugins/systemdashboard/SysMenuQuick.ui | 400 +++++++ .../panel-plugins/systemstart/ItemWidget.cpp | 279 +++++ .../src-DE/panel-plugins/systemstart/ItemWidget.h | 98 ++ .../panel-plugins/systemstart/LStartButton.cpp | 137 +++ .../panel-plugins/systemstart/LStartButton.h | 113 ++ .../src-DE/panel-plugins/systemstart/StartMenu.cpp | 720 ++++++++++++ .../src-DE/panel-plugins/systemstart/StartMenu.h | 105 ++ .../src-DE/panel-plugins/systemstart/StartMenu.ui | 1148 ++++++++++++++++++++ .../src-DE/panel-plugins/systemtray/LSysTray.cpp | 167 +++ .../src-DE/panel-plugins/systemtray/LSysTray.h | 74 ++ .../src-DE/panel-plugins/systemtray/TrayIcon.cpp | 128 +++ .../src-DE/panel-plugins/systemtray/TrayIcon.h | 55 + .../panel-plugins/taskmanager/LTaskButton.cpp | 271 +++++ .../src-DE/panel-plugins/taskmanager/LTaskButton.h | 73 ++ .../taskmanager/LTaskManagerPlugin.cpp | 141 +++ .../panel-plugins/taskmanager/LTaskManagerPlugin.h | 71 ++ .../panel-plugins/userbutton/LUserButton.cpp | 67 ++ .../src-DE/panel-plugins/userbutton/LUserButton.h | 75 ++ .../panel-plugins/userbutton/UserItemWidget.cpp | 205 ++++ .../panel-plugins/userbutton/UserItemWidget.h | 72 ++ .../src-DE/panel-plugins/userbutton/UserWidget.cpp | 393 +++++++ .../src-DE/panel-plugins/userbutton/UserWidget.h | 101 ++ .../src-DE/panel-plugins/userbutton/UserWidget.ui | 593 ++++++++++ 107 files changed, 16224 insertions(+) create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/JsonMenu.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LPanel.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LPanel.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LSession.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LSession.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/NewDP.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/SamplePlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/OutlineToolButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/calendar/CalendarPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktop-plugins.pri create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/LXDG-DBusNotifier.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/quickcontainer/QuickDPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSObjects.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSObjects.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LPPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LTBWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/NewPP.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/RotateToolButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/NOTES create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/line/LLine.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/panel-plugins.pri create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/quickcontainer/QuickPPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/spacer/LSpacer.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.ui create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.cpp create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.h create mode 100644 src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.ui (limited to 'src-qt5/core/lumina-desktop-unified/src-DE') diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.cpp new file mode 100644 index 00000000..798d8b6d --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.cpp @@ -0,0 +1,183 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "AppMenu.h" +#include "LSession.h" +#include + +AppMenu::AppMenu(QWidget* parent) : QMenu(parent){ + appstorelink = LOS::AppStoreShortcut(); //Default application "store" to display (AppCafe in TrueOS) + controlpanellink = LOS::ControlPanelShortcut(); //Default control panel + sysApps = new XDGDesktopList(this, true); //have this one automatically keep in sync + APPS.clear(); + //watcher = new QFileSystemWatcher(this); + //connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherUpdate()) ); + //QTimer::singleShot(200, this, SLOT(start()) ); //Now start filling the menu + start(); //do the initial run during session init so things are responsive immediately. + connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(watcherUpdate()) ); + connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(watcherUpdate()) ); +} + +AppMenu::~AppMenu(){ + +} + +QHash >* AppMenu::currentAppHash(){ + return &APPS; +} + +//=========== +// PRIVATE +//=========== +void AppMenu::updateAppList(){ + //watcher->removePaths(watcher->directories()); + //Make sure the title/icon are updated as well (in case of locale/icon change) + this->setTitle(tr("Applications")); + this->setIcon( LXDG::findIcon("system-run","") ); + //Now update the lists + this->clear(); + APPS.clear(); //NOTE: Don't delete these pointers - the pointers are managed by the sysApps class and these are just references to them + //qDebug() << "New Apps List:"; + if(LSession::handle()->sessionSettings()->value("AutomaticDesktopAppLinks",true).toBool() && !lastHashUpdate.isNull() ){ + QString desktop = QDir::homePath()+"/"+tr("Desktop")+"/"; //translated desktop folder + if(!QFile::exists(desktop)){ + desktop = QDir::homePath()+"/Desktop/"; //desktop folder + if(!QFile::exists(desktop)){ + desktop = QDir::homePath()+"/desktop/"; //lowercase desktop folder + if(!QFile::exists(desktop)){ desktop.clear(); } + } + } + //qDebug() << "Update Desktop Folder:" << desktop << sysApps->removedApps << sysApps->newApps; + QStringList tmp = sysApps->removedApps; + for(int i=0; inewApps; + for(int i=0; ifiles.value(tmp[i]); + if(desk->isHidden || !desk->isValid(false) ){ continue; } //skip this one + //qDebug() << "New App: " << tmp[i] << desk.filePath << "Hidden:" << desk.isHidden; + //Create a new symlink for this file if one does not exist + QString filename = tmp[i].section("/",-1); + //qDebug() << "Check for symlink:" << filename; + if(!QFile::exists(desktop+filename) ){ QFile::link(tmp[i], desktop+filename); } + } + } + QList allfiles = sysApps->apps(false,false); //only valid, non-hidden apps + APPS = LXDG::sortDesktopCats(allfiles); + APPS.insert("All", LXDG::sortDesktopNames(allfiles)); + lastHashUpdate = QDateTime::currentDateTime(); + //Now fill the menu + //Add link to the file manager + //this->addAction( LXDG::findIcon("user-home", ""), tr("Browse Files"), this, SLOT(launchFileManager()) ); + //--Look for the app store + XDGDesktop store(appstorelink); + if(store.isValid()){ + this->addAction( LXDG::findIcon(store.icon, ""), tr("Manage Applications"), this, SLOT(launchStore()) ); + } + //--Look for the control panel + XDGDesktop controlp(controlpanellink); + if(controlp.isValid()){ + this->addAction( LXDG::findIcon(controlp.icon, ""), tr("Control Panel"), this, SLOT(launchControlPanel()) ); + } + this->addSeparator(); + //--Now create the sub-menus + QStringList cats = APPS.keys(); + cats.sort(); //make sure they are alphabetical + for(int i=0; isetIcon(LXDG::findIcon(icon,"")); + connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(launchApp(QAction*)) ); + QList appL = APPS.value(cats[i]); + for( int a=0; aactions.isEmpty()){ + //Just a single entry point - no extra actions + QAction *act = new QAction(LXDG::findIcon(appL[a]->icon, ""), appL[a]->name, this); + act->setToolTip(appL[a]->comment); + act->setWhatsThis(appL[a]->filePath); + menu->addAction(act); + }else{ + //This app has additional actions - make this a sub menu + // - first the main menu/action + QMenu *submenu = new QMenu(appL[a]->name, this); + submenu->setIcon( LXDG::findIcon(appL[a]->icon,"") ); + //This is the normal behavior - not a special sub-action (although it needs to be at the top of the new menu) + QAction *act = new QAction(LXDG::findIcon(appL[a]->icon, ""), appL[a]->name, this); + act->setToolTip(appL[a]->comment); + act->setWhatsThis(appL[a]->filePath); + submenu->addAction(act); + //Now add entries for every sub-action listed + for(int sa=0; saactions.length(); sa++){ + QAction *sact = new QAction(LXDG::findIcon(appL[a]->actions[sa].icon, appL[a]->icon), appL[a]->actions[sa].name, this); + sact->setToolTip(appL[a]->comment); + sact->setWhatsThis("-action \""+appL[a]->actions[sa].ID+"\" \""+appL[a]->filePath+"\""); + submenu->addAction(sact); + } + menu->addMenu(submenu); + } + } + this->addMenu(menu); + } + // watcher->addPaths(LXDG::systemApplicationDirs()); + emit AppMenuUpdated(); +} + +//================= +// PRIVATE SLOTS +//================= +void AppMenu::start(){ + //Setup the watcher + connect(sysApps, SIGNAL(appsUpdated()), this, SLOT(watcherUpdate()) ); + sysApps->updateList(); + //Now fill the menu the first time + updateAppList(); +} + +void AppMenu::watcherUpdate(){ + updateAppList(); //Update the menu listings +} + +void AppMenu::launchStore(){ + LSession::LaunchApplication("lumina-open \""+appstorelink+"\""); +} + +void AppMenu::launchControlPanel(){ + LSession::LaunchApplication("lumina-open \""+controlpanellink+"\""); +} + +void AppMenu::launchFileManager(){ + QString fm = "lumina-open \""+QDir::homePath()+"\""; + LSession::LaunchApplication(fm); +} + +void AppMenu::launchApp(QAction *act){ + QString appFile = act->whatsThis(); + if(appFile.startsWith("-action")){ + LSession::LaunchApplication("lumina-open "+appFile); //already has quotes put in place properly + }else{ + LSession::LaunchApplication("lumina-open \""+appFile+"\""); + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.h b/src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.h new file mode 100644 index 00000000..5baaab7a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/AppMenu.h @@ -0,0 +1,58 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This menu auto-updates to keep the list of available applications +// current at all times - and can launch them as necessary +//=========================================== +#ifndef _LUMINA_DESKTOP_APP_MENU_H +#define _LUMINA_DESKTOP_APP_MENU_H + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +// libLumina includes +#include + +class AppMenu : public QMenu{ + Q_OBJECT +public: + AppMenu(QWidget *parent = 0); + ~AppMenu(); + + QHash > *currentAppHash(); + QDateTime lastHashUpdate; + +private: + //QFileSystemWatcher *watcher; + QString appstorelink, controlpanellink; + QList MLIST; + XDGDesktopList *sysApps; + QHash > APPS; + + void updateAppList(); //completely update the menu lists + +private slots: + void start(); //This is called in a new thread after initialization + void watcherUpdate(); + void launchStore(); + void launchControlPanel(); + void launchFileManager(); + void launchApp(QAction *act); + +signals: + void AppMenuUpdated(); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/JsonMenu.h b/src-qt5/core/lumina-desktop-unified/src-DE/JsonMenu.h new file mode 100644 index 00000000..5a6b2237 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/JsonMenu.h @@ -0,0 +1,79 @@ +//=========================================== +// Lumina Desktop source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This menu is used to automatically generate menu contents +// based on the JSON output of an external script/utility +//=========================================== +#ifndef _LUMINA_DESKTOP_JSON_MENU_H +#define _LUMINA_DESKTOP_JSON_MENU_H + +#include +#include +#include +#include +#include + +#include +#include +#include "LSession.h" + +class JsonMenu : public QMenu{ + Q_OBJECT +private: + QString exec; + +public: + JsonMenu(QString execpath, QWidget *parent = 0) : QMenu(parent){ + exec = execpath; + connect(this, SIGNAL(aboutToShow()), this, SLOT(updateMenu()) ); + connect(this, SIGNAL(triggered(QAction*)), this, SLOT(itemTriggered(QAction*)) ); + } + +private slots: + void parseObject(QString label, QJsonObject obj){ + if( label.isEmpty() || !obj.contains("type") ){ return; } + QString type = obj.value("type").toString(); + if(type.toLower()=="item"){ + QAction *act = this->addAction(label); + if(obj.contains("icon")){ act->setIcon( LXDG::findIcon(obj.value("icon").toString(),"") ); } + if(obj.contains("action")){ act->setWhatsThis( obj.value("action").toString() ); } + else{ act->setEnabled(false); } //not interactive + }else if(type.toLower()=="menu"){ + + }else if(type.toLower()=="jsonmenu"){ + //This is a recursive JSON menu object + if(!obj.contains("exec")){ return; } + JsonMenu *menu = new JsonMenu(obj.value("exec").toString(), this); + menu->setTitle(label); + if(obj.contains("icon")){ menu->setIcon(LXDG::findIcon(obj.value("icon").toString(),"") ); } + this->addMenu(menu); + } + } + + void updateMenu(){ + this->clear(); + QJsonDocument doc = QJsonDocument::fromJson( LUtils::getCmdOutput(exec).join(" ").toLocal8Bit() ); + if(doc.isNull() || !doc.isObject()){ + this->addAction( QString(tr("Error parsing script output: %1")).arg("\n"+exec) )->setEnabled(false); + }else{ + QStringList keys = doc.object().keys(); + for(int i=0; iparent()!=this || act->whatsThis().isEmpty() ){ return; } //only handle direct child actions - needed for recursive nature of menu + QString cmd = act->whatsThis(); + QString bin = cmd.section(" ",0,0); + if( !LUtils::isValidBinary(bin) ){ cmd.prepend("lumina-open "); } + LSession::handle()->LaunchApplication(cmd); + } +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.cpp new file mode 100644 index 00000000..de7d086e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.cpp @@ -0,0 +1,553 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LDesktop.h" +#include "LSession.h" + +#include +#include +#include "LWinInfo.h" +#include "JsonMenu.h" + +#include + +#define DEBUG 0 + +LDesktop::LDesktop(int deskNum, bool setdefault) : QObject(){ + screenID = QApplication::screens().at(deskNum)->name(); + DPREFIX = "desktop-"+screenID+"/"; + //desktopnumber = deskNum; + //desktop = QApplication::desktop(); + defaultdesktop = setdefault; //(desktop->screenGeometry(desktopnumber).x()==0); + //desktoplocked = true; + issyncing = bgupdating = false; + usewinmenu=false; + + //Setup the internal variables + settings = new QSettings(QSettings::UserScope, "lumina-desktop","desktopsettings", this); + //qDebug() << " - Desktop Settings File:" << settings->fileName(); + if(!QFile::exists(settings->fileName())){ settings->setValue(DPREFIX+"background/filelist",QStringList()<<"default"); settings->sync(); } + //bgWindow = 0; + bgDesktop = 0; + QTimer::singleShot(1,this, SLOT(InitDesktop()) ); + +} + +LDesktop::~LDesktop(){ + delete deskMenu; + delete winMenu; + //delete bgWindow; + delete workspacelabel; + delete wkspaceact; +} + +int LDesktop::Screen(){ + QList scrns = QApplication::screens(); + for(int i=0; iname()==screenID){ return i; } + } + return -1; +} + +void LDesktop::show(){ + //if(bgWindow!=0){ bgWindow->show(); } + if(bgDesktop!=0){ bgDesktop->show(); } + for(int i=0; ishow(); } +} + +void LDesktop::hide(){ + //if(bgWindow!=0){ bgWindow->hide(); } + if(bgDesktop!=0){ bgDesktop->hide(); } + for(int i=0; ihide(); } +} + +void LDesktop::prepareToClose(){ + //Get any panels ready to close + issyncing = true; //Stop handling any watcher events + for(int i=0; iprepareToClose(); PANELS.takeAt(i)->deleteLater(); i--; } + //Now close down any desktop plugins + //desktoplocked = true; //make sure that plugin settings are preserved during removal + //Remove all the current containers + bgDesktop->cleanup(); +} + +WId LDesktop::backgroundID(){ + if(bgDesktop!=0){ return bgDesktop->winId(); } + else{ return QX11Info::appRootWindow(); } +} + +QRect LDesktop::availableScreenGeom(){ + //Return a QRect containing the (global) screen area that is available (not under any panels) + if(bgDesktop!=0){ + return globalWorkRect; //saved from previous calculations + }else{ + return LSession::handle()->screenGeom( Screen() ); + } +} + +void LDesktop::UpdateGeometry(){ + //First make sure there is something different about the geometry + //if(desktop->screenGeometry(Screen())==bgWindow->geometry()){ return; } + //Now update the screen + // NOTE: This functionality is highly event-driven based on X changes - so we need to keep things in order (no signals/slots) + //qDebug() << "Changing Desktop Geom:" << Screen(); + //bgWindow->setGeometry(desktop->screenGeometry(Screen())); + /*for(int i=0; iUpdatePanel(true); //geom only updates - do this before adjusting the background + }*/ + //qDebug() << " - Update Desktop Plugin Area"; + UpdateDesktopPluginArea(); + //qDebug() << " - Done With Desktop Geom Updates"; + QTimer::singleShot(0, this, SLOT(UpdatePanels())); +} + +void LDesktop::SystemLock(){ + QProcess::startDetached("xscreensaver-command -lock"); +} + +void LDesktop::SystemLogout(){ + LSession::handle()->systemWindow(); +} + +void LDesktop::SystemTerminal(){ + LSession::handle()->sessionSettings()->sync(); //make sure it is up to date + QString term = LXDG::findDefaultAppForMime("application/terminal"); //LSession::handle()->sessionSettings()->value("default-terminal","xterm").toString(); + if(term.isEmpty() ||(!term.endsWith(".desktop") && !LUtils::isValidBinary(term)) ){ term = "xterm"; } + LSession::LaunchApplication("lumina-open \""+term+"\""); +} + +void LDesktop::SystemFileManager(){ + //Just open the home directory + QString fm = "lumina-open \""+QDir::homePath()+"\""; + LSession::LaunchApplication(fm); +} + +void LDesktop::SystemApplication(QAction* act){ + if(!act->whatsThis().isEmpty() && act->parent()==deskMenu){ + LSession::LaunchApplication("lumina-open \""+act->whatsThis()+"\""); + } +} + +void LDesktop::checkResolution(){ + //Compare the current screen resolution with the last one used/saved and adjust config values *only* + //NOTE: This is only run the first time this desktop is created (before loading all the interface) - not on each desktop change + int oldWidth = settings->value(DPREFIX+"screen/lastWidth",-1).toInt(); + int oldHeight = settings->value(DPREFIX+"screen/lastHeight",-1).toInt(); + QRect scrn = LSession::handle()->screenGeom( Screen() ); + if(scrn.isNull()){ return; } + issyncing = true; + settings->setValue(DPREFIX+"screen/lastWidth",scrn.width()); + settings->setValue(DPREFIX+"screen/lastHeight",scrn.height()); + + if(oldWidth<1 || oldHeight<1 || scrn.width()<1 || scrn.height()<1){ + //nothing to do - something invalid + }else if(scrn.width()==oldWidth && scrn.height()==oldHeight){ + //nothing to do - same as before + }else{ + //Calculate the scale factor between the old/new sizes in each dimension + // and forward that on to all the interface elements + double xscale = scrn.width()/((double) oldWidth); + double yscale = scrn.height()/((double) oldHeight); + if(DEBUG){ + qDebug() << "Screen Resolution Changed:" << screenID; + qDebug() << " - Old:" << QString::number(oldWidth)+"x"+QString::number(oldHeight); + qDebug() << " - New:" << QString::number(scrn.width())+"x"+QString::number(scrn.height()); + qDebug() << " - Scale Factors:" << xscale << yscale; + } + //Update any panels in the config file + for(int i=0; i<4; i++){ + QString PPREFIX = "panel"+QString::number(Screen())+"."+QString::number(i)+"/"; + int ht = settings->value(PPREFIX+"height",-1).toInt(); + if(ht<1){ continue; } //no panel height defined + QString loc = settings->value(PPREFIX+"location","top").toString().toLower(); + if(loc=="top" || loc=="bottom"){ + settings->setValue(PPREFIX+"height", (int) ht*yscale); //vertical dimension + }else{ + settings->setValue(PPREFIX+"height", (int) ht*xscale); //horizontal dimension + } + } + //Update any desktop plugins + QStringList plugs = settings->value(DPREFIX+"pluginlist").toStringList(); + QFileInfoList files = LSession::handle()->DesktopFiles(); + for(int i=0; iDesktopPluginSettings(); + QStringList keys = DP->allKeys(); + for(int i=0; isetValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); } + if(filter[j].endsWith("location/width")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*xscale) ); } + if(filter[j].endsWith("location/x")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*xscale) ); } + if(filter[j].endsWith("location/y")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); } + if(filter[j].endsWith("IconSize")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); } + if(filter[j].endsWith("iconsize")){ DP->setValue( filter[j], qRound(DP->value(filter[j]).toInt()*yscale) ); } + } + } + DP->sync(); //make sure it gets saved to disk right away + + } + issyncing = false; +} + +// ===================== +// PRIVATE SLOTS +// ===================== +void LDesktop::InitDesktop(){ + //This is called *once* during the main initialization routines + checkResolution(); //Adjust the desktop config file first (if necessary) + if(DEBUG){ qDebug() << "Init Desktop:" << Screen(); } + //connect(desktop, SIGNAL(resized(int)), this, SLOT(UpdateGeometry(int))); + if(DEBUG){ qDebug() << "Desktop #"< "<< LSession::desktop()->screenGeometry(Screen()) << LSession::handle()->screenGeom(Screen()); } + deskMenu = new QMenu(0); + connect(deskMenu, SIGNAL(triggered(QAction*)), this, SLOT(SystemApplication(QAction*)) ); + winMenu = new QMenu(0); + winMenu->setTitle(tr("Window List")); + winMenu->setIcon( LXDG::findIcon("preferences-system-windows","") ); + connect(winMenu, SIGNAL(triggered(QAction*)), this, SLOT(winClicked(QAction*)) ); + workspacelabel = new QLabel(0); + workspacelabel->setAlignment(Qt::AlignCenter); + wkspaceact = new QWidgetAction(0); + wkspaceact->setDefaultWidget(workspacelabel); + bgtimer = new QTimer(this); + bgtimer->setSingleShot(true); + connect(bgtimer, SIGNAL(timeout()), this, SLOT(UpdateBackground()) ); + + connect(QApplication::instance(), SIGNAL(DesktopConfigChanged()), this, SLOT(SettingsChanged()) ); + connect(QApplication::instance(), SIGNAL(DesktopFilesChanged()), this, SLOT(UpdateDesktop()) ); + connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(LocaleChanged()) ); + connect(QApplication::instance(), SIGNAL(WorkspaceChanged()), this, SLOT(UpdateBackground()) ); + //if(DEBUG){ qDebug() << "Create bgWindow"; } + /*bgWindow = new QWidget(); //LDesktopBackground(); + bgWindow->setObjectName("bgWindow"); + bgWindow->setContextMenuPolicy(Qt::CustomContextMenu); + bgWindow->setFocusPolicy(Qt::StrongFocus); + bgWindow->setWindowFlags(Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::FramelessWindowHint); + LSession::handle()->XCB->SetAsDesktop(bgWindow->winId()); + bgWindow->setGeometry(LSession::handle()->screenGeom(Screen())); + bgWindow->setWindowOpacity(0.0); + connect(bgWindow, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowMenu()) );*/ + if(DEBUG){ qDebug() << "Create bgDesktop"; } + bgDesktop = new LDesktopPluginSpace(); + int grid = settings->value(DPREFIX+"GridSize",-1).toInt(); + if(grid<0 && LSession::desktop()->screenGeometry(Screen()).height() > 2000){ grid = 200; } + else if(grid<0){ grid = 100; } + bgDesktop->SetIconSize( grid ); + bgDesktop->setContextMenuPolicy(Qt::CustomContextMenu); + //LSession::handle()->XCB->SetAsDesktop(bgDesktop->winId()); + connect(bgDesktop, SIGNAL(PluginRemovedByUser(QString)), this, SLOT(RemoveDeskPlugin(QString)) ); + connect(bgDesktop, SIGNAL(IncreaseIcons()), this, SLOT(IncreaseDesktopPluginIcons()) ); + connect(bgDesktop, SIGNAL(DecreaseIcons()), this, SLOT(DecreaseDesktopPluginIcons()) ); + connect(bgDesktop, SIGNAL(HideDesktopMenu()), deskMenu, SLOT(hide())); + connect(bgDesktop, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowMenu()) ); + if(DEBUG){ qDebug() << " - Desktop Init Done:" << screenID; } + //Start the update processes + QTimer::singleShot(10,this, SLOT(UpdateMenu()) ); + QTimer::singleShot(0,this, SLOT(UpdateBackground()) ); + QTimer::singleShot(1,this, SLOT(UpdateDesktop()) ); + QTimer::singleShot(2,this, SLOT(UpdatePanels()) ); +} + +void LDesktop::SettingsChanged(){ + if(issyncing){ return; } //don't refresh for internal modifications to the + issyncing = true; + qDebug() << "Found Settings Change:" << screenID; + settings->sync(); //make sure to sync with external settings changes + UpdateBackground(); + UpdateDesktop(); + UpdatePanels(); + UpdateMenu(); + issyncing = false; + QTimer::singleShot(100, this, SLOT(UnlockSettings()) ); //give it a few moments to settle before performing another sync +} + +void LDesktop::LocaleChanged(){ + //Update any elements which require a re-translation + UpdateMenu(false); //do the full menu refresh +} + +void LDesktop::UpdateMenu(bool fast){ + if(DEBUG){ qDebug() << " - Update Menu:" << screenID; } + //Put a label at the top + int num = LSession::handle()->XCB->CurrentWorkspace(); //LX11::GetCurrentDesktop(); + if(DEBUG){ qDebug() << "Found workspace number:" << num; } + if(num < 0){ workspacelabel->setText( ""+tr("Lumina Desktop")+""); } + else{ workspacelabel->setText( ""+QString(tr("Workspace %1")).arg(QString::number(num+1))+""); } + if(fast && usewinmenu){ UpdateWinMenu(); } + if(fast){ return; } //already done + deskMenu->clear(); //clear it for refresh + deskMenu->addAction(wkspaceact); + deskMenu->addSeparator(); + //Now load the user's menu setup and fill the menu + QStringList items = settings->value("menu/itemlist", QStringList()<< "terminal" << "filemanager" <<"applications" << "line" << "settings" ).toStringList(); + usewinmenu=false; + for(int i=0; iaddAction(LXDG::findIcon("utilities-terminal",""), tr("Terminal"), this, SLOT(SystemTerminal()) ); } + else if(items[i]=="lockdesktop"){ deskMenu->addAction(LXDG::findIcon("system-lock-screen",""), tr("Lock Session"), this, SLOT(SystemLock()) ); } + else if(items[i]=="filemanager"){ deskMenu->addAction( LXDG::findIcon("user-home",""), tr("Browse Files"), this, SLOT(SystemFileManager()) ); } + else if(items[i]=="applications"){ deskMenu->addMenu( LSession::handle()->applicationMenu() ); } + else if(items[i]=="line"){ deskMenu->addSeparator(); } + else if(items[i]=="settings"){ deskMenu->addMenu( LSession::handle()->settingsMenu() ); } + else if(items[i]=="windowlist"){ deskMenu->addMenu( winMenu); usewinmenu=true;} + else if(items[i].startsWith("app::::") && items[i].endsWith(".desktop")){ + //Custom *.desktop application + QString file = items[i].section("::::",1,1).simplified(); + XDGDesktop xdgf(file);// = LXDG::loadDesktopFile(file, ok); + if(xdgf.type!=XDGDesktop::BAD){ + deskMenu->addAction( LXDG::findIcon(xdgf.icon,""), xdgf.name)->setWhatsThis(file); + }else{ + qDebug() << "Could not load application file:" << file; + } + }else if(items[i].startsWith("jsonmenu::::")){ + //Custom JSON menu system (populated on demand via external scripts/tools + QStringList info = items[i].split("::::"); //FORMAT:[ "jsonmenu",exec,name, icon(optional)] + if(info.length()>=3){ + qDebug() << "Custom JSON Menu Loaded:" << info; + JsonMenu *tmp = new JsonMenu(info[1], deskMenu); + tmp->setTitle(info[2]); + connect(tmp, SIGNAL(triggered(QAction*)), this, SLOT(SystemApplication(QAction*)) ); + if(info.length()>=4){ tmp->setIcon( LXDG::findIcon(info[3],"") ); } + deskMenu->addMenu(tmp); + } + } + } + //Now add the system quit options + deskMenu->addSeparator(); + deskMenu->addAction(LXDG::findIcon("system-log-out",""), tr("Leave"), this, SLOT(SystemLogout()) ); +} + +void LDesktop::UpdateWinMenu(){ + winMenu->clear(); + //Get the current list of windows + QList wins = LSession::handle()->XCB->WindowList(); + //Now add them to the menu + for(int i=0; iaddAction( info.icon(junk), info.text() ); + act->setData( QString::number(wins[i]) ); + } +} + +void LDesktop::winClicked(QAction* act){ + LSession::handle()->XCB->ActivateWindow( act->data().toString().toULong() ); +} + +void LDesktop::UpdateDesktop(){ + if(DEBUG){ qDebug() << " - Update Desktop Plugins for screen:" << screenID; } + QStringList plugins = settings->value(DPREFIX+"pluginlist", QStringList()).toStringList(); + if(defaultdesktop && plugins.isEmpty()){ + //plugins << "sample" << "sample" << "sample"; + } + bool changed=false; //in case the plugin list needs to be changed + //First make sure all the plugin names are unique + for(int i=0; isetValue(DPREFIX+"pluginlist", plugins); + settings->sync(); + QTimer::singleShot(200, this, SLOT(UnlockSettings()) ); + } + //If generating desktop file launchers, add those in + QStringList filelist; + if(settings->value(DPREFIX+"generateDesktopIcons",false).toBool()){ + QFileInfoList files = LSession::handle()->DesktopFiles(); + for(int i=0; iLoadItems(plugins, filelist); +} + +void LDesktop::RemoveDeskPlugin(QString ID){ + //This is called after a plugin is manually removed by the user + // just need to ensure that the plugin is also removed from the settings file + QStringList plugs = settings->value(DPREFIX+"pluginlist", QStringList()).toStringList(); + if(plugs.contains(ID)){ + plugs.removeAll(ID); + issyncing=true; //don't let the change cause a refresh + settings->setValue(DPREFIX+"pluginlist", plugs); + settings->sync(); + QTimer::singleShot(200, this, SLOT(UnlockSettings()) ); + } +} + +void LDesktop::IncreaseDesktopPluginIcons(){ + int cur = settings->value(DPREFIX+"GridSize",-1).toInt(); + if(cur<0 &&LSession::desktop()->screenGeometry(Screen()).height() > 2000){ cur = 200; } + else if(cur<0){ cur = 100; } + cur+=16; + issyncing=true; //don't let the change cause a refresh + settings->setValue(DPREFIX+"GridSize",cur); + settings->sync(); + QTimer::singleShot(200, this, SLOT(UnlockSettings()) ); + bgDesktop->SetIconSize(cur); +} + +void LDesktop::DecreaseDesktopPluginIcons(){ + int cur = settings->value(DPREFIX+"GridSize",-1).toInt(); + if(cur<0 &&LSession::desktop()->screenGeometry(Screen()).height() > 2000){ cur = 200; } + else if(cur<0){ cur = 100; } + if(cur<32){ return; } //cannot get smaller than 16x16 + cur-=16; + issyncing=true; //don't let the change cause a refresh + settings->setValue(DPREFIX+"GridSize",cur); + settings->sync(); + QTimer::singleShot(200, this, SLOT(UnlockSettings()) ); + bgDesktop->SetIconSize(cur); +} + +void LDesktop::UpdatePanels(){ + if(DEBUG){ qDebug() << " - Update Panels For Screen:" << Screen(); } + int panels = settings->value(DPREFIX+"panels", -1).toInt(); + //if(panels==-1 && defaultdesktop){ panels=1; } //need at least 1 panel on the primary desktop + //Remove all extra panels + for(int i=0; inumber()){ + if(DEBUG){ qDebug() << " -- Remove Panel:" << PANELS[i]->number(); } + PANELS[i]->prepareToClose(); + PANELS.takeAt(i)->deleteLater(); + i--; + } + } + for(int i=0; inumber() == i){ + found = true; + if(DEBUG){ qDebug() << " -- Update panel "<< i; } + //panel already exists - just update it + QTimer::singleShot(0, PANELS[p], SLOT(UpdatePanel()) ); + } + } + if(!found){ + if(DEBUG){ qDebug() << " -- Create panel "<< i; } + //New panel + LPanel *pan = new LPanel(settings, screenID, i, bgDesktop); + PANELS << pan; + pan->show(); + } + } + //Give it a 1/2 second before ensuring that the visible desktop area is correct + QTimer::singleShot(500, this, SLOT(UpdateDesktopPluginArea()) ); +} + +void LDesktop::UpdateDesktopPluginArea(){ + QRegion visReg(LSession::desktop()->screenGeometry(Screen()) ); //visible region (not hidden behind a panel) + QRect rawRect = visReg.boundingRect(); //initial value (screen size) + //qDebug() << "Update Desktop Plugin Area:" << bgWindow->geometry(); + for(int i=0; ivalue(PANELS[i]->prefix()+"location","top").toString().toLower(); + int vis = PANELS[i]->visibleWidth(); + if(loc=="top"){ + if(!shifted.contains(QRect(rawRect.x(), rawRect.y(), rawRect.width(), vis))){ continue; } + shifted.translate(0, (rawRect.top()+vis)-shifted.boundingRect().top() ); + }else if(loc=="bottom"){ + if(!shifted.contains(QRect(rawRect.x(), rawRect.bottom()-vis, rawRect.width(), vis))){ continue; } + shifted.translate(0, (rawRect.bottom()-vis)-shifted.boundingRect().bottom()); + }else if(loc=="left"){ + if( !shifted.contains(QRect(rawRect.x(), rawRect.y(), vis,rawRect.height())) ){ continue; } + shifted.translate((rawRect.left()+vis)-shifted.boundingRect().left() ,0); + }else{ //right + if(!shifted.contains(QRect(rawRect.right()-vis, rawRect.y(), vis,rawRect.height())) ){ continue; } + shifted.translate((rawRect.right()-vis)-shifted.boundingRect().right(),0); + } + visReg = visReg.intersected( shifted ); + } + //Now make sure the desktop plugin area is only the visible area + QRect rec = visReg.boundingRect(); + //qDebug() << " - DPArea: Panel-Adjusted rectangle:" << rec; + //LSession::handle()->XCB->SetScreenWorkArea((unsigned int) Screen(), rec); + //Now remove the X offset to place it on the current screen (needs widget-coords, not global) + globalWorkRect = rec; //save this for later + rec.moveTopLeft( QPoint( rec.x()-LSession::desktop()->screenGeometry(Screen()).x() , rec.y()-LSession::desktop()->screenGeometry(Screen()).y() ) ); + //qDebug() << "DPlug Area:" << rec << bgDesktop->geometry() << LSession::handle()->desktop()->availableGeometry(bgDesktop); + if(rec.size().isNull() || rec == bgDesktop->geometry()){return; } //nothing changed + bgDesktop->setGeometry( LSession::desktop()->screenGeometry(Screen())); + bgDesktop->setDesktopArea( rec ); + bgDesktop->UpdateGeom(); //just in case the plugin space itself needs to do anything + QTimer::singleShot(10, this, SLOT(UpdateBackground()) ); + //Re-paint the panels (just in case a plugin was underneath it and the panel is transparent) + //for(int i=0; iupdate(); } + //Make sure to re-disable any WM control flags + LSession::handle()->XCB->SetDisableWMActions(bgDesktop->winId()); +} + +void LDesktop::UpdateBackground(){ + //Get the current Background + if(bgupdating || bgDesktop==0){ return; } //prevent multiple calls to this at the same time + bgupdating = true; + if(DEBUG){ qDebug() << " - Update Desktop Background for screen:" << Screen(); } + //Get the list of background(s) to show + QStringList bgL = settings->value(DPREFIX+"background/filelist-workspace-"+QString::number( LSession::handle()->XCB->CurrentWorkspace()), QStringList()).toStringList(); + if(bgL.isEmpty()){ bgL = settings->value(DPREFIX+"background/filelist", QStringList()).toStringList(); } + + //qDebug() << " - List:" << bgL << CBG; + //Remove any invalid files + for(int i=0; iisActive(); + if(bgL==oldBGL && !CBG.isEmpty() && bgtimer->isActive()){ + //No background change scheduled - just update the widget + bgDesktop->update(); + bgupdating=false; + return; + } + oldBGL = bgL; //save this for later + //Determine which background to use next + int index ( qrand() % bgL.length() ); + if(index== bgL.indexOf(CBG)){ //if the current wallpaper was selected by the randomization again + //Go to the next in the list + if(index < 0 || index >= bgL.length()-1){ index = 0; } //if invalid or last item in the list - go to first + else{ index++; } //go to next + } + QString bgFile = bgL[index]; + //Save this file as the current background + CBG = bgFile; + //qDebug() << " - Set Background to:" << CBG << index << bgL; + if( (bgFile.toLower()=="default")){ bgFile = LOS::LuminaShare()+"desktop-background.jpg"; } + //Now set this file as the current background + QString format = settings->value(DPREFIX+"background/format","stretch").toString(); + //bgWindow->setBackground(bgFile, format); + QPixmap backPix = LDesktopBackground::setBackground(bgFile, format, LSession::handle()->screenGeom(Screen())); + bgDesktop->setBackground(backPix); + //Now reset the timer for the next change (if appropriate) + if(bgtimer->isActive()){ bgtimer->stop(); } + if(bgL.length() > 1){ + //get the length of the timer (in minutes) + int min = settings->value(DPREFIX+"background/minutesToChange",5).toInt(); + //restart the internal timer + if(min > 0){ + bgtimer->start(min*60000); //convert from minutes to milliseconds + } + } + //Now update the panel backgrounds + for(int i=0; iupdate(); + PANELS[i]->show(); + } + bgupdating=false; +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.h b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.h new file mode 100644 index 00000000..b6034c18 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktop.h @@ -0,0 +1,107 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_LDESKTOP_H +#define _LUMINA_DESKTOP_LDESKTOP_H + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include "LPanel.h" +//#include "Globals.h" +#include "AppMenu.h" +#include "LDesktopPluginSpace.h" +#include "desktop-plugins/LDPlugin.h" +//#include "desktop-plugins/NewDP.h" +#include "LDesktopBackground.h" + +class LDesktop : public QObject{ + Q_OBJECT +public: + LDesktop(int deskNum=0, bool setdefault = false); + ~LDesktop(); + + int Screen(); //return the screen number this object is managing + void show(); + void hide(); + void prepareToClose(); + + WId backgroundID(); + QRect availableScreenGeom(); + + void UpdateGeometry(); + +public slots: + void SystemLock(); + void SystemLogout(); + void SystemTerminal(); + void SystemFileManager(); + void SystemApplication(QAction*); + + void checkResolution(); + +private: + QSettings *settings; + QTimer *bgtimer; + //QDesktopWidget *desktop; + QString DPREFIX, screenID; + //int desktopnumber; + QRegion availDPArea; + bool defaultdesktop, issyncing, usewinmenu, bgupdating; + QStringList oldBGL; + QList PANELS; + LDesktopPluginSpace *bgDesktop; //desktop plugin area + //QWidget *bgWindow; //full screen background + QMenu *deskMenu, *winMenu; + QLabel *workspacelabel; + QWidgetAction *wkspaceact; + QList PLUGINS; + QString CBG; //current background + QRect globalWorkRect; + +private slots: + void InitDesktop(); + void SettingsChanged(); + void UnlockSettings(){ issyncing=false; } + void LocaleChanged(); + + //Menu functions + void UpdateMenu(bool fast = false); + void ShowMenu(){ + UpdateMenu(true); //run the fast version + deskMenu->popup(QCursor::pos()); //} + } + void UpdateWinMenu(); + void winClicked(QAction*); + + //Desktop plugin system functions + void UpdateDesktop(); + void RemoveDeskPlugin(QString); + void IncreaseDesktopPluginIcons(); + void DecreaseDesktopPluginIcons(); + + void UpdatePanels(); + + void UpdateDesktopPluginArea(); //make sure the area is not underneath any panels + + void UpdateBackground(); +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.cpp new file mode 100644 index 00000000..6b458c24 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.cpp @@ -0,0 +1,90 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Henry Hu +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LDesktopBackground.h" + +#include +#include +#include + +#include "LSession.h" + +void LDesktopBackground::paintEvent(QPaintEvent *ev) { + //return; //do nothing - always invisible + if (bgPixmap != NULL) { + //qDebug() << "Wallpaper paint Event:" << ev->rect(); + //QPainter painter(this); + //painter.setBrush(*bgPixmap); + //painter.drawRect(ev->rect().adjusted(-1,-1,2,2)); + }else{ + QWidget::paintEvent(ev); + } +} + +QPixmap LDesktopBackground::setBackground(const QString& bgFile, const QString& format, QRect geom) { + //if (bgPixmap != NULL) delete bgPixmap; + QPixmap bgPixmap(geom.size());// = new QPixmap(size()); + + if (bgFile.startsWith("rgb(")) { + QStringList colors = bgFile.section(")",0,0).section("(",1,1).split(","); + QColor color = QColor(colors[0].toInt(), colors[1].toInt(), colors[2].toInt()); + bgPixmap.fill(color); + } else { + bgPixmap.fill(Qt::black); + + // Load the background file and scale + QPixmap bgImage(bgFile); + if (format == "stretch" || format == "full" || format == "fit") { + Qt::AspectRatioMode mode; + if (format == "stretch") { + mode = Qt::IgnoreAspectRatio; + } else if (format == "full") { + mode = Qt::KeepAspectRatioByExpanding; + } else { + mode = Qt::KeepAspectRatio; + } + if(bgImage.height() != geom.height() && bgImage.width() != geom.width() ){ bgImage = bgImage.scaled(geom.size(), mode); } + //bgImage = bgImage.scaled(size(), mode); + } + + // Calculate the offset + int dx = 0, dy = 0; + int drawWidth = bgImage.width(), drawHeight = bgImage.height(); + if (format == "fit" || format == "center" || format == "full") { + dx = (geom.width() - bgImage.width()) / 2; + dy = (geom.height() - bgImage.height()) / 2; + } else if (format == "tile") { + drawWidth = geom.width(); + drawHeight = geom.height(); + } else { + if (format.endsWith("right")) { + dx = geom.width() - bgImage.width(); + } + if (format.startsWith("bottom")) { + dy = geom.height() - bgImage.height(); + } + } + + // Draw the background image + QPainter painter(&bgPixmap); + painter.setBrush(bgImage); + painter.setBrushOrigin(dx, dy); + painter.drawRect(dx, dy, drawWidth, drawHeight); + } + //this->repaint(); //make sure the entire thing gets repainted right away + //LSession::handle()->XCB->paintRoot(geom, &bgPixmap); + return bgPixmap; + //show(); +} + +LDesktopBackground::LDesktopBackground() : QWidget() { + bgPixmap = NULL; + this->setWindowOpacity(0); +} + +LDesktopBackground::~LDesktopBackground() { + if (bgPixmap != NULL) delete bgPixmap; +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.h b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.h new file mode 100644 index 00000000..a2e46748 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopBackground.h @@ -0,0 +1,27 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Henry Hu +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_LDESKTOPBACKGROUND_H_ +#define _LUMINA_DESKTOP_LDESKTOPBACKGROUND_H_ + +#include +#include +#include + +class LDesktopBackground: public QWidget { + Q_OBJECT +public: + LDesktopBackground(); + virtual ~LDesktopBackground(); + + virtual void paintEvent(QPaintEvent*); + static QPixmap setBackground(const QString&, const QString&, QRect geom); + +private: + QPixmap *bgPixmap; +}; + +#endif // _LUMINA_DESKTOP_LDESKTOPBACKGROUND_H_ diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.cpp new file mode 100644 index 00000000..18126dfa --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.cpp @@ -0,0 +1,333 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LDesktopPluginSpace.h" +#include "LSession.h" +#include "desktop-plugins/NewDP.h" + +#include +#include + +#define DEBUG 0 + +// =================== +// PUBLIC +// =================== +LDesktopPluginSpace::LDesktopPluginSpace() : QWidget(){ + this->setObjectName("LuminaDesktopPluginSpace"); + this->setAttribute(Qt::WA_TranslucentBackground); + //this->setAttribute(Qt::WA_NoSystemBackground); + this->setAutoFillBackground(false); + this->setStyleSheet("QWidget#LuminaDesktopPluginSpace{ border: none; background: transparent; }"); + this->setWindowFlags(Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::FramelessWindowHint); + this->setAcceptDrops(true); + this->setContextMenuPolicy(Qt::NoContextMenu); + this->setMouseTracking(true); + TopToBottom = true; + GRIDSIZE = 100.0; //default value if not set + plugsettings = LSession::handle()->DesktopPluginSettings(); + LSession::handle()->XCB->SetAsDesktop(this->winId()); + //this->setWindowOpacity(0.0); +} + +LDesktopPluginSpace::~LDesktopPluginSpace(){ + +} + +void LDesktopPluginSpace::LoadItems(QStringList plugs, QStringList files){ + if(DEBUG){ qDebug() << "Loading Desktop Items:" << plugs << files << "Area:" << this->size() << GRIDSIZE; } + bool changes = false; + if(plugs != plugins){ plugins = plugs; changes = true; } + if(files != deskitems){ deskitems = files; changes = true; } + if(changes){ QTimer::singleShot(0,this, SLOT(reloadPlugins())); } + this->show(); +} + +void LDesktopPluginSpace::SetIconSize(int size){ + if(DEBUG){ qDebug() << "Set Desktop Icon Size:" << size; } + //QSize newsize = calculateItemSize(size); + int oldsize = GRIDSIZE; + GRIDSIZE = size; //turn the int into a float; + //itemSize = QSize(1,1); //save this for all the later icons which are generated (grid size) + UpdateGeom(oldsize); + //Now re-set the item icon size + //reloadPlugins(true); +} + +void LDesktopPluginSpace::cleanup(){ + //Perform any final cleanup actions here + for(int i=0; ideleteLater(); + i--; + } + plugins.clear(); + deskitems.clear(); + this->hide(); +} + +void LDesktopPluginSpace::setBackground(QPixmap pix){ + wallpaper = pix; + this->repaint(); +} + +void LDesktopPluginSpace::setDesktopArea(QRect area){ + desktopRect = area; +} + +// =================== +// PUBLIC SLOTS +// =================== +void LDesktopPluginSpace::UpdateGeom(int oldgrid){ + if(DEBUG){ qDebug() << "Updated Desktop Geom:" << desktopRect.size() << GRIDSIZE << desktopRect.size()/GRIDSIZE; } + //Go through and check the locations/sizes of all items (particularly the ones on the bottom/right edges) + //bool reload = false; + for(int i=0; igeometry(), oldgrid); + if(DEBUG){ qDebug() << " - Check Plugin:" << ITEMS[i]->whatsThis() << grid; } + if( !ValidGrid(grid) ){ + //This plugin is too far out of the screen - find new location for it + if(DEBUG){ qDebug() << " -- Out of bounds - Find a new spot"; } + grid = findOpenSpot(grid, ITEMS[i]->whatsThis(), true); //Reverse lookup spot + } + if(!ValidGrid(grid)){ + qDebug() << "No Place for plugin:" << ITEMS[i]->whatsThis(); + qDebug() << " - Removing it for now..."; + ITEMS.takeAt(i)->deleteLater(); + i--; + }else{ + //NOTE: We are not doing the ValidGeometry() checks because we are only resizing existing plugin with pre-set & valid grid positions + grid = gridToGeom(grid); //convert to pixels before saving/sizing + MovePlugin(ITEMS[i], grid); + /*ITEMS[i]->setGeometry( grid ); + ITEMS[i]->setFixedSize(grid.size()); + ITEMS[i]->savePluginGeometry(grid);*/ + } + } + //if(reload){ QTimer::singleShot(0,this, SLOT(reloadPlugins())); } +} + +// =================== +// PRIVATE +// =================== +void LDesktopPluginSpace::addDesktopItem(QString filepath){ + addDesktopPlugin("applauncher::"+filepath+"---dlink"+QString::number(LSession::handle()->desktop()->screenNumber(this)) ); +} + +void LDesktopPluginSpace::addDesktopPlugin(QString plugID){ + //This is used for generic plugins (QWidget-based) + if(DEBUG){ qDebug() << "Adding Desktop Plugin:" << plugID; } + LDPlugin *plug = NewDP::createPlugin(plugID, this); + if(plug==0){ return; } //invalid plugin + //plug->setAttribute(Qt::WA_TranslucentBackground); + plug->setWhatsThis(plugID); + //Now get the geometry for the plugin + QRect geom = plug->loadPluginGeometry(); //in pixel coords + if(!geom.isNull()){ geom = geomToGrid(geom); } //convert to grid coordinates + if(geom.isNull()){ + //No previous location - need to calculate initial geom + QSize sz = plug->defaultPluginSize(); //in grid coordinates + geom.setSize(sz); + //if an applauncher - add from top-left, otherwise add in from bottom-right + if(plugID.startsWith("applauncher")){ geom = findOpenSpot(geom.width(), geom.height() ); } + else{ geom = findOpenSpot(geom.width(), geom.height(), RoundUp(this->height()/GRIDSIZE), RoundUp(this->width()/GRIDSIZE), true); } + }else if(!ValidGeometry(plugID, gridToGeom(geom)) ){ + //Find a new location for the plugin (saved location is invalid) + geom = findOpenSpot(geom.width(), geom.height(), geom.y(), geom.x(), false); //try to get it within the same general area first + } + if(geom.x() < 0 || geom.y() < 0){ + qDebug() << "No available space for desktop plugin:" << plugID << " - IGNORING"; + delete plug; + }else{ + if(DEBUG){ qDebug() << " - New Plugin Geometry (grid):" << geom; } + //Now place the item in the proper spot/size + MovePlugin(plug, gridToGeom(geom)); + //plug->setGeometry( gridToGeom(geom) ); + plug->show(); + if(DEBUG){ qDebug() << " - New Plugin Geometry (px):" << plug->geometry(); } + ITEMS << plug; + connect(plug, SIGNAL(StartMoving(QString)), this, SLOT(StartItemMove(QString)) ); + connect(plug, SIGNAL(StartResizing(QString)), this, SLOT(StartItemResize(QString)) ); + connect(plug, SIGNAL(RemovePlugin(QString)), this, SLOT(RemoveItem(QString)) ); + connect(plug, SIGNAL(IncreaseIconSize()), this, SIGNAL(IncreaseIcons()) ); + connect(plug, SIGNAL(DecreaseIconSize()), this, SIGNAL(DecreaseIcons()) ); + connect(plug, SIGNAL(CloseDesktopMenu()), this, SIGNAL(HideDesktopMenu()) ); + } +} + +QRect LDesktopPluginSpace::findOpenSpot(int gridwidth, int gridheight, int startRow, int startCol, bool reversed, QString plugID){ + //Note about the return QPoint: x() is the column number, y() is the row number + QPoint pt(0,0); + //qDebug() << "FIND OPEN SPOT:" << gridwidth << gridheight << startRow << startCol << reversed; + int row = startRow; int col = startCol; + if(row<0){ row = 0; } //just in case - since this can be recursively called + if(col<0){ col = 0; } //just in case - since this can be recursively called + bool found = false; + int rowCount, colCount; + rowCount = RoundUp(desktopRect.height()/GRIDSIZE); + colCount = RoundUp(desktopRect.width()/GRIDSIZE); + if( (row+gridheight)>rowCount){ row = rowCount-gridheight; startRow = row; } + if( (col+gridwidth)>colCount){ col = colCount-gridwidth; startCol = col; } + QRect geom(0, 0, gridwidth*GRIDSIZE, gridheight*GRIDSIZE); //origin point will be adjusted in a moment + if(DEBUG){ qDebug() << "Search for plugin space:" << rowCount << colCount << gridheight << gridwidth << this->size(); } + if(TopToBottom && reversed && (startRow>0 || startCol>0) ){ + //Arrange Top->Bottom (work backwards) + //qDebug() << "Search backwards for space:" << rowCount << colCount << startRow << startCol << gridheight << gridwidth; + while(col>=0 && !found){ + while(row>=0 && !found){ + bool ok = true; + geom.moveTo(col*GRIDSIZE, row*GRIDSIZE); + //qDebug() << " - Check Geom:" << geom << col << row; + //Check all the existing items to ensure no overlap + for(int i=0; iwhatsThis()==plugID){ continue; } //same plugin - this is not a conflict + if(geom.intersects(ITEMS[i]->geometry())){ + //Collision - move the next searchable row/column index + ok = false; + //qDebug() << "Collision:" << col << row; + row = ((ITEMS[i]->geometry().y()-GRIDSIZE/2)/GRIDSIZE) -gridheight; //use top edge for next search (minus item height) + //qDebug() << " - new row:" << row; + } + } + if(ok){ pt = QPoint(col,row); found = true; } //found an open spot + } + if(!found){ col--; row=rowCount-gridheight; } //go to the previous column + } + }else if(TopToBottom){ + //Arrange Top->Bottom + while(col<(colCount-gridwidth) && !found){ + while(row<(rowCount-gridheight) && !found){ + bool ok = true; + geom.moveTo(col*GRIDSIZE, row*GRIDSIZE); + //qDebug() << " - Check Geom:" << geom << col << row; + //Check all the existing items to ensure no overlap + for(int i=0; iwhatsThis()==plugID){ continue; } //same plugin - this is not a conflict + if(geom.intersects(ITEMS[i]->geometry())){ + //Collision - move the next searchable row/column index + ok = false; + row = posToGrid(ITEMS[i]->geometry().bottomLeft()).y(); //use bottom edge for next search + } + } + if(ok){ pt = QPoint(col,row); found = true; } //found an open spot + //else{ row++; } + } + if(!found){ col++; row=0; } //go to the next column + } + }else if(reversed && (startRow>0 || startCol>0) ){ + //Arrange Left->Right (work backwards) + while(row>=0 && !found){ + while(col>=0 && !found){ + bool ok = true; + geom.moveTo(col*GRIDSIZE, row*GRIDSIZE); + //Check all the existing items to ensure no overlap + for(int i=0; iwhatsThis()==plugID){ continue; } //same plugin - this is not a conflict + if(geom.intersects(ITEMS[i]->geometry())){ + //Collision - move the next searchable row/column index + ok = false; + col = (ITEMS[i]->geometry().x()-GRIDSIZE/2)/GRIDSIZE - gridwidth; // Fill according to row/column + } + } + if(ok){ pt = QPoint(col,row); found = true; } //found an open spot + //else{ col++; } + } + if(!found){ row--; col=colCount-gridwidth;} //go to the previous row + } + }else{ + //Arrange Left->Right + while(row<(rowCount-gridheight) && !found){ + while(col<(colCount-gridwidth) && !found){ + bool ok = true; + geom.moveTo(col*GRIDSIZE, row*GRIDSIZE); + //Check all the existing items to ensure no overlap + for(int i=0; iwhatsThis()==plugID){ continue; } //same plugin - this is not a conflict + if(geom.intersects(ITEMS[i]->geometry())){ + //Collision - move the next searchable row/column index + ok = false; + col = posToGrid(ITEMS[i]->geometry().topRight()).x(); // Fill according to row/column + } + } + if(ok){ pt = QPoint(col,row); found = true; } //found an open spot + //else{ col++; } + } + if(!found){ row++; col=0;} //go to the next row + } + } + if(!found){ + //qDebug() << "Could not find a spot:" << startRow << startCol << gridheight << gridwidth; + if( (startRow!=0 || startCol!=0) && !reversed){ + //Did not check the entire screen yet - gradually work it's way back to the top/left corner + //qDebug() << " - Start backwards search"; + return findOpenSpot(gridwidth, gridheight,startRow,startCol, true); //reverse the scan + }else if(gridwidth>1 && gridheight>1){ + //Decrease the size of the item by 1x1 grid points and try again + //qDebug() << " - Out of space: Decrease item size and try again..."; + return findOpenSpot(gridwidth-1, gridheight-1, 0, 0); + }else{ + //qDebug() << " - Could not find an open spot for a desktop plugin:" << gridwidth << gridheight << startRow << startCol; + return QRect(-1,-1,-1,-1); + } + }else{ + return QRect(pt,QSize(gridwidth,gridheight)); + } +} + +QRect LDesktopPluginSpace::findOpenSpot(QRect grid, QString plugID, bool recursive){ //Reverse lookup spotc{ + //This is just an overloaded simplification for checking currently existing plugins + return findOpenSpot(grid.width(), grid.height(), grid.y(), grid.x(), recursive, plugID); +} + +// =================== +// PRIVATE SLOTS +// =================== +void LDesktopPluginSpace::reloadPlugins(bool ForceIconUpdate ){ + //Remove any plugins as necessary + QStringList plugs = plugins; + QStringList items = deskitems; + for(int i=0; iwhatsThis().startsWith("applauncher") && ForceIconUpdate){ + //Change the size of the existing plugin - preserving the location if possible + /*QRect geom = ITEMS[i]->loadPluginGeometry(); //pixel coords + if(!geom.isNull()){ + geom = geomToGrid(geom); //convert to grid coords + geom.setSize(itemSize); //Reset back to default size (does not change location) + ITEMS[i]->savePluginGeometry( gridToGeom(geom)); //save it back in pixel coords + }*/ + //Now remove the plugin for the moment - run it through the re-creation routine below + ITEMS.takeAt(i)->deleteLater(); + i--; + } + else if(plugs.contains(ITEMS[i]->whatsThis())){ plugs.removeAll(ITEMS[i]->whatsThis()); } + else if(items.contains(ITEMS[i]->whatsThis().section("---",0,0).section("::",1,50))){ items.removeAll(ITEMS[i]->whatsThis().section("---",0,0).section("::",1,50)); } + else{ ITEMS[i]->removeSettings(true); ITEMS.takeAt(i)->deleteLater(); i--; } //this is considered a permanent removal (cleans settings) + } + + //Now create any new items + //First load the plugins (almost always have fixed locations) + for(int i=0; irect().adjusted(-1,-1,2,2)); + }else{ + QWidget::paintEvent(ev); + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.h b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.h new file mode 100644 index 00000000..abc34878 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LDesktopPluginSpace.h @@ -0,0 +1,303 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_LDESKTOP_PLUGIN_SPACE_H +#define _LUMINA_DESKTOP_LDESKTOP_PLUGIN_SPACE_H + +#include +#include +#include //includes all the QDrag*Event classes +#include +#include +#include +#include +#include +#include +#include +#include + +#include "desktop-plugins/LDPlugin.h" + +#define MIMETYPE QString("x-special/lumina-desktop-plugin") + +class LDesktopPluginSpace : public QWidget{ + Q_OBJECT + +signals: + void PluginRemovedByUser(QString ID); + void IncreaseIcons(); //increase default icon sizes + void DecreaseIcons(); //decrease default icon sizes + void HideDesktopMenu(); + +public: + LDesktopPluginSpace(); + ~LDesktopPluginSpace(); + + void LoadItems(QStringList plugs, QStringList files); + //void setShowGrid(bool show); This is already implemented in QTableView (inherited) + void SetIconSize(int size); + void ArrangeTopToBottom(bool ttb); //if false, will arrange left->right + void cleanup(); + + void setBackground(QPixmap pix); //should already be sized appropriately for this widget + void setDesktopArea(QRect area); + +public slots: + void UpdateGeom(int oldgrid = -1); + +private: + QSettings *plugsettings; + QStringList plugins, deskitems; + QList ITEMS; + QPixmap wallpaper; + QRect desktopRect; + bool TopToBottom; + float GRIDSIZE; + + int RoundUp(double num){ + int out = num; //This will truncate the number + if(out < num){ out++; } //need to increase by 1 + return out; + } + + void addDesktopItem(QString filepath); //This will convert it into a valid Plugin ID automatically + void addDesktopPlugin(QString plugID); + + + QRect findOpenSpot(int gridwidth = 1, int gridheight = 1, int startRow = 0, int startCol = 0, bool reversed = false, QString plugID = ""); + QRect findOpenSpot(QRect grid, QString plugID, bool recursive = false); + + QPoint posToGrid(QPoint pos){ + //This assumes a point in widget-relative coordinates + pos.setX( RoundUp(pos.x()/GRIDSIZE)); + pos.setY( RoundUp(pos.y()/GRIDSIZE)); + return pos; + } + + QRect geomToGrid(QRect geom, int grid = -1){ + if(grid<0){ + //use the current grid size + return QRect( RoundUp(geom.x()/GRIDSIZE), RoundUp(geom.y()/GRIDSIZE), \ + RoundUp(geom.width()/GRIDSIZE), RoundUp(geom.height()/GRIDSIZE) ); + }else{ + //use the input grid size + return QRect( RoundUp(geom.x()/((double) grid)), RoundUp(geom.y()/((double) grid)), \ + RoundUp(geom.width()/((double) grid)), RoundUp(geom.height()/((double) grid)) ); + } + } + + QRect gridToGeom(QRect grid){ + //This function incorporates the bottom/right edge matchins procedures (for incomplete last grid) + QRect geom(grid.x()*GRIDSIZE, grid.y()*GRIDSIZE, grid.width()*GRIDSIZE, grid.height()*GRIDSIZE); + //Now check the edge conditions (last right/bottom grid points might be smaller than GRIDSIZE) + QSize areaSize = desktopRect.size(); //use the size of the area instead of the geometry - because we need this in child coordinates like "geom" above + //qDebug() << "GridToGeom:" << grid << geom << "Area size:" << areaSize; + if(geom.right() > areaSize.width() && (geom.right()-areaSize.width()) areaSize.height() && (geom.bottom() -areaSize.height())setData(MIMETYPE, QString(type+"::::"+id).toLocal8Bit() ); + //If this is a desktop file - also add it to the generic URI list mimetype + if(id.startsWith("applauncher::")){ + QList urilist; + urilist << QUrl::fromLocalFile( id.section("---",0,0).section("::",1,50) ); + mime->setUrls(urilist); + } + //Create the drag structure + QDrag *drag = new QDrag(this); + drag->setMimeData(mime); + drag->exec(Qt::CopyAction); + } + + bool ValidGrid(QRect grid){ + //qDebug() << "Check Valid Grid:" << grid << RoundUp(this->width()/GRIDSIZE) << RoundUp(this->height()/GRIDSIZE); + //This just checks that the grid coordinates are not out of bounds - should still run ValidGeometry() below with the actual pixel geom + if(grid.x()<0 || grid.y()<0 || grid.width()<0 || grid.height()<0){ return false; } + else if( (grid.x()+grid.width()) > RoundUp(desktopRect.width()/GRIDSIZE) ){ return false; } + else if( (grid.y()+grid.height()) > RoundUp(desktopRect.height()/GRIDSIZE) ){ return false; } + return true; + } + + bool ValidGeometry(QString id, QRect geom){ + //First check that it is within the desktop area completely + // Note that "this->geometry()" is not in the same coordinate space as the geometry inputs + if(!QRect(0,0,desktopRect.width(), desktopRect.height()).contains(geom)){ return false; } + //Now check that it does not collide with any other items + for(int i=0; iwhatsThis()==id){ continue; } + else if(geom.intersects(ITEMS[i]->geometry())){ return false; } + } + return true; + } + + LDPlugin* ItemFromID(QString ID){ + for(int i=0; iwhatsThis()==ID){ return ITEMS[i]; } + } + return 0; + } + + void MovePlugin(LDPlugin* plug, QRect geom){ + plug->setGeometry( geom ); + plug->setFixedSize(geom.size()); //needed for some plugins + plug->savePluginGeometry(geom); + } + +private slots: + void reloadPlugins(bool ForceIconUpdate = false); + + void StartItemMove(QString ID){ + setupDrag(ID, "move"); + } + void StartItemResize(QString ID){ + setupDrag(ID, "resize"); + } + void RemoveItem(QString ID){ + //Special case - desktop file/dir link using the "applauncher" plugin + if(ID.startsWith("applauncher::")){ + QFileInfo info(ID.section("---",0,0).section("::",1,50) ); + if(info.exists() && info.absolutePath()==QDir::homePath()+"/Desktop"){ + qDebug() << "Deleting Desktop Item:" << info.absoluteFilePath(); + if(!info.isSymLink() && info.isDir()){ QProcess::startDetached("rm -r \""+info.absoluteFilePath()+"\""); } + else{ QFile::remove(info.absoluteFilePath()); } //just remove the file/symlink directly + emit PluginRemovedByUser(ID); + return; + } + } + //Any other type of plugin + for(int i=0; iwhatsThis()==ID){ + ITEMS[i]->Cleanup(); + ITEMS.takeAt(i)->deleteLater(); + break; + } + } + emit PluginRemovedByUser(ID); + } + +protected: + void focusInEvent(QFocusEvent *ev){ + this->lower(); //make sure we stay on the bottom of the window stack + QWidget::focusInEvent(ev); //do normal handling + } + void paintEvent(QPaintEvent*ev); + + //Need Drag and Drop functionality (internal movement) + void dragEnterEvent(QDragEnterEvent *ev){ + if(ev->mimeData()->hasFormat(MIMETYPE) ){ + ev->acceptProposedAction(); //allow this to be dropped here + }else if(ev->mimeData()->hasUrls()){ + ev->acceptProposedAction(); //allow this to be dropped here + }else{ + ev->ignore(); + } + } + + void dragMoveEvent(QDragMoveEvent *ev){ + if(ev->mimeData()->hasFormat(MIMETYPE) ){ + //Internal move/resize - Check for validity + QString act = QString( ev->mimeData()->data(MIMETYPE) ); + LDPlugin *item = ItemFromID(act.section("::::",1,50)); + //qDebug() << "Internal Move Event:" << act << ev->pos(); + if(item!=0){ + QRect geom = item->geometry(); + QPoint grid = posToGrid(ev->pos()); + if(act.section("::::",0,0)=="move"){ + QPoint diff = grid - posToGrid(geom.center()); //difference in grid coords + //qDebug() << "Move Event:" << "Diff:" << diff << "Geom:" << geom << grid << ev->pos(); + geom = geomToGrid(geom); //convert to grid coords + //qDebug() << "Move Event:" << "Old geom (grid):" << geom; + geom.moveTo( (geom.topLeft()+diff) ); + //qDebug() << " - After Move:" << geom; + bool valid = ValidGrid(geom); + if(valid){ + //Convert to pixel coordinates and check validity again + geom = gridToGeom(geom); //convert back to px coords with edge matching + valid = ValidGeometry(act.section("::::",1,50), geom); + } + if(valid){ + MovePlugin(item, geom); + //item->setGeometry(geom); + //item->setFixedSize(geom.size()); //needed due to resizing limitations and such for some plugins + ev->acceptProposedAction(); + //item->savePluginGeometry(geom); //save in pixel coords + }else{ ev->ignore(); } //invalid location + + }else{ + //Resize operation + QPoint diff = ev->pos() - (geom.center()-QPoint(1,1)); //need difference from center (pixels) + //Note: Use the 1x1 pixel offset to ensure that the center point is not exactly on a grid point intersection (2x2, 4x4, etc) + //qDebug() << "Resize Plugin:" << geom << grid << posToGrid(geom.center()) << diff; + geom = geomToGrid(geom); //convert to grid coordinates now + //qDebug() << " - Grid Geom:" << geom; + if(diff.x()<0){ geom.setLeft(ev->pos().x()/GRIDSIZE); } //expanding to the left (round down) + else if(diff.x()>0){ geom.setRight( ev->pos().x()/GRIDSIZE); } //expanding to the right (round down) + if(diff.y()<0){ geom.setTop( ev->pos().y()/GRIDSIZE); } //expanding above (round down) + else if(diff.y()>0){ geom.setBottom( ev->pos().y()/GRIDSIZE); } //expanding below (round down) + //qDebug() << " - Adjusted:" << geom; + bool valid = ValidGrid(geom); + if(valid){ + //Convert to pixel coordinates and check validity again + geom = gridToGeom(geom); //convert back to px coords with edge matching + valid = ValidGeometry(act.section("::::",1,50), geom); + } + if(valid){ + MovePlugin(item, geom); + //item->setGeometry(geom); + //item->setFixedSize(geom.size()); //needed due to resizing limitations and such for some plugins + ev->acceptProposedAction(); + //item->savePluginGeometry(geom); //save in pixel coords + }else{ ev->ignore(); } //invalid location + } + } + }else if(ev->mimeData()->hasUrls()){ + ev->acceptProposedAction(); //allow this to be dropped here + }else{ + ev->ignore(); + } + } + + void dropEvent(QDropEvent *ev){ + //QPoint grid = posToGrid(ev->pos()); + if(ev->mimeData()->hasFormat(MIMETYPE)){ + //Desktop Items getting moved around - already performed in the dragMoveEvent + ev->accept(); + }else if(ev->mimeData()->hasUrls()){ + ev->accept(); + //Files getting dropped here + QList urls = ev->mimeData()->urls(); + qDebug() << "Desktop Drop Event:" << urls; + for(int i=0; iignore(); + } + } + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LPanel.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LPanel.cpp new file mode 100644 index 00000000..5df1fcb6 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LPanel.cpp @@ -0,0 +1,399 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LPanel.h" +#include "LSession.h" +#include + +#include "panel-plugins/systemtray/LSysTray.h" + +#define DEBUG 0 + +LPanel::LPanel(QSettings *file, QString scr, int num, QWidget *parent) : QWidget(){ + //Take care of inputs + this->setMouseTracking(true); + hascompositer = false; //LUtils::isValidBinary("xcompmgr"); //NOT WORKING YET - xcompmgr issue with special window flags? + if(DEBUG){ qDebug() << " - Creating Panel:" << scr << num; } + bgWindow = parent; //save for later + //Setup the widget overlay for the entire panel to provide transparency effects + panelArea = new QWidget(this); + //panelArea->setAttribute(Qt::WA_TranslucentBackground); + QBoxLayout *tmp = new QBoxLayout(QBoxLayout::LeftToRight); + tmp->setContentsMargins(0,0,0,0); + this->setLayout(tmp); + tmp->addWidget(panelArea); + settings = file; + screenID = scr; + panelnum = num; //save for later + screen = LSession::desktop(); + QString screenID = QApplication::screens().at(Screen())->name(); + PPREFIX = "panel_"+screenID+"."+QString::number(num)+"/"; + defaultpanel = (LSession::handle()->screenGeom(Screen()).x()==0 && num==0); + horizontal=true; //use this by default initially + hidden = false; //use this by default + //Setup the panel + if(DEBUG){ qDebug() << " -- Setup Panel"; } + this->setContentsMargins(0,0,0,0); + this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + //panels cannot get keyboard focus otherwise it upsets the task manager window detection + //this->setAttribute(Qt::WA_X11DoNotAcceptFocus); + this->setAttribute(Qt::WA_X11NetWmWindowTypeDock); + this->setAttribute(Qt::WA_AlwaysShowToolTips); + this->setAttribute(Qt::WA_TranslucentBackground); + //this->setAttribute(Qt::WA_NoSystemBackground); + this->setAutoFillBackground(false); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint); + //this->setWindowFlags(Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint); + + this->setWindowTitle("LuminaPanel"); + this->setObjectName("LuminaPanelBackgroundWidget"); + this->setStyleSheet("QToolButton::menu-indicator{ image: none; } QWidget#LuminaPanelBackgroundWidget{ background: transparent; }"); + panelArea->setObjectName("LuminaPanelColor"); + layout = new QBoxLayout(QBoxLayout::LeftToRight); + layout->setContentsMargins(0,0,0,0); + layout->setSpacing(1); + //layout->setSizeConstraint(QLayout::SetFixedSize); + panelArea->setLayout(layout); + //Set special window flags on the panel for proper usage + this->show(); + LSession::handle()->XCB->SetAsPanel(this->winId()); + LSession::handle()->XCB->SetAsSticky(this->winId()); + if(hascompositer){ + //qDebug() << "Enable Panel compositing"; + //this->setStyleSheet("QWidget#LuminaPanelBackgroundWidget{ background: transparent; }"); + //this->setWindowOpacity(0.5); //fully transparent background for the main widget + //panelArea->setWindowOpacity(1.0); //fully opaque for the widget on top (apply stylesheet transparencies) + } + QTimer::singleShot(1,this, SLOT(UpdatePanel()) ); + //connect(screen, SIGNAL(resized(int)), this, SLOT(UpdatePanel()) ); //in case the screen resolution changes +} + +LPanel::~LPanel(){ + +} + +int LPanel::Screen(){ + // Find the screen number associated with this ID + QList scrns = QApplication::screens(); + for(int i=0; iname() == screenID){ return i; } + } + return -1; +} + +void LPanel::prepareToClose(){ + //Go through and remove all the plugins + for(int i=0; iAboutToClose(); //any last cleanup for this plugin + layout->takeAt(i); //remove from the layout + PLUGINS.takeAt(i)->deleteLater(); //delete the actual widget + LSession::processEvents(); + i--; //need to back up one space to not miss another plugin + } + this->hide(); +} + +void LPanel::scalePanel(double xscale, double yscale){ + int ht = settings->value(PPREFIX+"height", 30).toInt(); //this is technically the distance into the screen from the edge + QString loc = settings->value(PPREFIX+"location","").toString().toLower(); + if(loc=="top" || loc=="bottom"){ + ht = qRound(ht*yscale); + }else{ + ht = qRound(ht*xscale); + } + settings->setValue(PPREFIX+"height", ht); + settings->sync(); + QTimer::singleShot(0, this, SLOT(UpdatePanel()) ); +} + +//=========== +// PUBLIC SLOTS +//=========== +void LPanel::UpdatePanel(bool geomonly){ + //Create/Update the panel as designated in the Settings file + settings->sync(); //make sure to catch external settings changes + //First set the geometry of the panel and send the EWMH message to reserve that space + if(DEBUG){ qDebug() << "Update Panel: Geometry only=" << geomonly << "Screen Size:" << LSession::handle()->screenGeom(Screen()); } + hidden = settings->value(PPREFIX+"hidepanel",false).toBool(); + QString loc = settings->value(PPREFIX+"location","").toString().toLower(); + if(loc.isEmpty() && defaultpanel){ loc="top"; } + if(loc=="top" || loc=="bottom"){ + horizontal=true; + layout->setAlignment(Qt::AlignLeft); + layout->setDirection(QBoxLayout::LeftToRight); + }else{ + horizontal=false; + layout->setAlignment(Qt::AlignTop); + layout->setDirection(QBoxLayout::TopToBottom); + } + int ht = qRound(settings->value(PPREFIX+"height", 30).toDouble()); //this is technically the distance into the screen from the edge + fullwidth = ht; //save this for later + if(ht<=1){ ht = 30; } //some kind of error in the saved height - use the default value + int hidesize = qRound(ht*0.01); //use 1% of the panel size + if(hidesize<2){ hidesize=2; } //minimum of 2 pixels (need space for the mouse to go over it) + if(hidden){ viswidth = hidesize; } + else{ viswidth = ht; } + if(DEBUG){ qDebug() << "Hidden Panel size:" << hidesize << "pixels"; } + //qDebug() << " - set Geometry"; + int xwid = LSession::handle()->screenGeom(Screen()).width(); + int xhi = LSession::handle()->screenGeom(Screen()).height(); + int xloc = LSession::handle()->screenGeom(Screen()).x(); + int yloc = LSession::handle()->screenGeom(Screen()).y(); + double panelPercent = settings->value(PPREFIX+"lengthPercent",100).toInt(); + if(panelPercent<1 || panelPercent>100){ panelPercent = 100; } + panelPercent = panelPercent/100.0; + QString panelPinLoc = settings->value(PPREFIX+"pinLocation","center").toString().toLower(); //[left/right/center] possible values (assume center otherwise) + if(DEBUG){ qDebug() << " - Panel settings:" << QString::number(panelPercent)+QString("%") << panelPinLoc << loc; } + //xloc=xoffset; + if(loc=="top"){ //top of screen + QSize sz = QSize(xwid*panelPercent, ht); + if(panelPinLoc=="left"){} // no change to xloc + else if(panelPinLoc=="right"){ xloc = xloc+xwid-sz.width(); } + else{ xloc = xloc+((xwid-sz.width())/2) ; } //centered + //qDebug() << " - Panel Sizing:" << xloc << sz; + this->setMinimumSize(sz); + this->setMaximumSize(sz); + this->setGeometry(xloc,yloc,sz.width(), sz.height()); + //qDebug() << " - Reserve Panel Localation"; + if(!hidden){ LSession::handle()->XCB->ReserveLocation(this->winId(), this->geometry(), "top"); } + else{ + LSession::handle()->XCB->ReserveLocation(this->winId(), QRect(xloc, yloc, this->width(), hidesize), "top"); + hidepoint = QPoint(xloc, yloc); + showpoint = QPoint(xloc, yloc); + this->move(hidepoint); + this->resize( this->width(), viswidth); + } + }else if(loc=="bottom"){ //bottom of screen + QSize sz = QSize(xwid*panelPercent, ht); + if(panelPinLoc=="left"){} // no change to xloc + else if(panelPinLoc=="right"){ xloc = xloc+xwid-sz.width(); } + else{ xloc = xloc+((xwid-sz.width())/2) ; } //centered + this->setMinimumSize(sz); + this->setMaximumSize(sz); + this->setGeometry(xloc,yloc+xhi-ht,sz.width(), ht ); + if(!hidden){ LSession::handle()->XCB->ReserveLocation(this->winId(), this->geometry(), "bottom"); } + else{ + LSession::handle()->XCB->ReserveLocation(this->winId(), QRect(xloc,yloc+ xhi-hidesize, this->width(), hidesize), "bottom"); + hidepoint = QPoint(xloc, yloc+xhi-hidesize); + showpoint = QPoint(xloc, yloc+xhi-ht); + this->move(hidepoint); //Could bleed over onto the screen below + this->resize( this->width(), viswidth); + } + }else if(loc=="left"){ //left side of screen + QSize sz = QSize(ht, xhi*panelPercent); + if(panelPinLoc=="left"){} //this is actually the top (left of center in length dimension) + else if(panelPinLoc=="right"){ yloc = yloc+xhi-sz.height(); } + else{ yloc = yloc+((xhi-sz.height())/2) ; } //centered + this->setMinimumSize(sz); + this->setMaximumSize(sz); + this->setGeometry(xloc,yloc, ht, sz.height()); + if(!hidden){ LSession::handle()->XCB->ReserveLocation(this->winId(), this->geometry(), "left"); } + else{ + LSession::handle()->XCB->ReserveLocation(this->winId(), QRect(xloc, yloc, hidesize, sz.height()), "left"); + hidepoint = QPoint(xloc, yloc); + showpoint = QPoint(xloc, yloc); + this->move(hidepoint); //Could bleed over onto the screen left + this->resize( viswidth, this->height()); + } + }else{ //right side of screen + QSize sz = QSize(ht, xhi*panelPercent); + if(panelPinLoc=="left"){} //this is actually the top (left of center in length dimension) + else if(panelPinLoc=="right"){ yloc = yloc+xhi-sz.height(); } + else{ yloc = yloc+((xhi-sz.height())/2) ; } //centered + this->setMinimumSize(sz); + this->setMaximumSize(sz); + this->setGeometry(xloc+xwid-ht,yloc,ht, sz.height()); + if(!hidden){ LSession::handle()->XCB->ReserveLocation(this->winId(), this->geometry(), "right"); } + else{ + LSession::handle()->XCB->ReserveLocation(this->winId(), QRect(xloc+xwid-hidesize, yloc, hidesize, sz.height()), "right"); + hidepoint = QPoint(xloc+xwid-hidesize, yloc); + showpoint = QPoint(xloc+xwid-ht, yloc); + this->move(hidepoint); //Could bleed over onto the screen right + this->resize( viswidth, this->height()); + } + } + if(DEBUG){ qDebug() << " - Done with panel geometry"; } + //Double check that the "sticky" bit is set on the window state + bool needsticky = !LSession::handle()->XCB->WM_Get_Window_States(this->winId()).contains(LXCB::S_STICKY); + if(needsticky){ LSession::handle()->XCB->SetAsSticky(this->winId()); } + if(geomonly){ return; } + //Now update the appearance of the toolbar + if(settings->value(PPREFIX+"customColor", false).toBool()){ + QString color = settings->value(PPREFIX+"color", "rgba(255,255,255,160)").toString(); + QString style = "QWidget#LuminaPanelColor{ background: %1; border-radius: 3px; border: 1px solid %1; }"; + style = style.arg(color); + panelArea->setStyleSheet(style); + }else{ + panelArea->setStyleSheet(""); //clear it and use the one from the theme + } + + //Then go through the plugins and create them as necessary + QStringList plugins = settings->value(PPREFIX+"pluginlist", QStringList()).toStringList(); + /*if(defaultpanel && plugins.isEmpty()){ + plugins << "userbutton" << "taskmanager" << "spacer" << "systemtray" << "clock" << "systemdashboard"; + }*/ + if(DEBUG){ qDebug() << " - Initialize Plugins: " << plugins; } + for(int i=0; inumber())+"."+QString::number(num)) ){ + num++; + } + + plugins[i] = plugins[i]+"---"+QString::number(Screen())+"."+QString::number(this->number())+"."+QString::number(num); + //qDebug() << "Adjust Plugin ID:" << plugins[i]; + } + //See if this plugin is already there or in a different spot + bool found = false; + for(int p=0; ptype()==plugins[i]){ + found = true; //already exists + //Make sure the plugin layout has the correct orientation + if(horizontal){PLUGINS[p]->layout()->setDirection(QBoxLayout::LeftToRight); } + else{ PLUGINS[p]->layout()->setDirection(QBoxLayout::TopToBottom); } + PLUGINS[p]->OrientationChange(); + //Now check the location of the plugin in the panel + if(p!=i){ //wrong place in the panel + layout->takeAt(p); //remove the item from the current location + layout->insertWidget(i, PLUGINS[p]); //add the item into the correct location + PLUGINS.move(p,i); //move the identifier in the list to match + } + break; + } + } + if(!found){ + //New Plugin + if(DEBUG){ qDebug() << " -- New Plugin:" << plugins[i] << i; } + LPPlugin *plug = NewPP::createPlugin(plugins[i], panelArea, horizontal); + if(plug != 0){ + PLUGINS.insert(i, plug); + layout->insertWidget(i, PLUGINS[i]); + connect(plug, SIGNAL(MenuClosed()), this, SLOT(checkPanelFocus())); + }else{ + //invalid plugin type + plugins.removeAt(i); //remove this invalid plugin from the list + i--; //make sure we don't miss the next item with the re-order + } + } + //LSession::processEvents(); + } + //Now remove any extra plugins from the end + //qDebug() << "plugins:" << plugins; + //qDebug() << "PLUGINS length:" << PLUGINS.length(); + for(int i=0; itype())){ continue; } //good plugin - skip it + if(DEBUG){ qDebug() << " -- Remove Plugin: " << PLUGINS[i]->type(); } + //If this is the system tray - stop it first + if( PLUGINS[i]->type().startsWith("systemtray---") ){ + static_cast(PLUGINS[i])->stop(); + } + layout->takeAt(i); //remove from the layout + PLUGINS.takeAt(i)->deleteLater(); //delete the actual widget + //LSession::processEvents(); + i--; //need to back up one space to not miss another plugin + } + this->update(); + this->show(); //make sure the panel is visible now + if(hidden){ this->move(hidepoint); } + //Now go through and send the orientation update signal to each plugin + for(int i=0; isize(); + if( !this->geometry().contains(QCursor::pos()) ){ + //Move the panel back to it's "hiding" spot + if(hidden){ + QSize sz(horizontal ? this->width() : viswidth, horizontal ? viswidth : this->height() ); + this->setMinimumSize(sz); + this->setMaximumSize(sz); + this->setGeometry( QRect(hidepoint, sz) ); + } + //Re-active the old window + if(LSession::handle()->activeWindow()!=0){ + LSession::handle()->XCB->ActivateWindow(LSession::handle()->activeWindow()); + } + }else if(hidden){ + QSize sz(horizontal ? this->width() : fullwidth, horizontal ? fullwidth : this->height() ); + this->setMinimumSize(sz); + this->setMaximumSize(sz); + this->setGeometry( QRect(showpoint, sz) ); + } +} + +//=========== +// PROTECTED +//=========== +void LPanel::resizeEvent(QResizeEvent *event){ + QWidget::resizeEvent(event); + for(int i=0; iOrientationChange(); } +} + +void LPanel::paintEvent(QPaintEvent *event){ + if(!hascompositer){ + QPainter *painter = new QPainter(this); + //qDebug() << "Paint Panel:" << PPREFIX; + //Make sure the base background of the event rectangle is the associated rectangle from the BGWindow + QRect rec = event->rect();//this->geometry(); //start with the global geometry of the panel + rec.adjust(-1,-1,2,2); //add 1 more pixel on each side + //Need to translate that rectangle to the background image coordinates + //qDebug() << " - Rec:" << rec << hidden << this->geometry() << bgWindow->geometry(); + rec.moveTo( bgWindow->mapFromGlobal( this->mapToGlobal(rec.topLeft()) ) ); //(rec.x()-LSession::handle()->screenGeom(Screen()).x(), rec.y()-LSession::handle()->screenGeom(Screen()).y() ); + //qDebug() << " - Adjusted Window Rec:" << rec; + painter->drawPixmap(event->rect().adjusted(-1,-1,2,2), bgWindow->grab(rec)); + //painter->drawPixmap(event->rect().adjusted(-1,-1,2,2), QApplication::screens().at(Screen())->grabWindow(QX11Info::appRootWindow(), rec.x(), rec.y(), rec.width(), rec.height()) ); + delete(painter); + } + QWidget::paintEvent(event); //now pass the event along to the normal painting event +} + +void LPanel::enterEvent(QEvent *event){ + //qDebug() << "Panel Enter Event:"; + checkPanelFocus(); + /*if(hidden){ + //Move the panel out so it is fully available + this->move(showpoint); + this->resize( horizontal ? this->width() : fullwidth, horizontal ? fullwidth : this->height() ); + this->update(); + }*/ + //this->activateWindow(); + event->accept(); //just to quiet the compile warning +} + +void LPanel::leaveEvent(QEvent *event){ + /*qDebug() << "Panel Leave Event:"; + qDebug() << "Panel Geom:" << this->geometry().x() << this->geometry().y() << this->geometry().width() << this->geometry().height() ; + QPoint pt = QCursor::pos(); + qDebug() << "Mouse Point (global):" << pt.x() << pt.y(); + //pt = this->mapFromGlobal(pt); + //qDebug() << "Mouse Point (local):" << pt.x() << pt.y(); + qDebug() << "Contained:" << this->geometry().contains(pt);*/ + checkPanelFocus(); + QWidget::leaveEvent(event); + //event->accept(); //just to quiet the compile warning +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LPanel.h b/src-qt5/core/lumina-desktop-unified/src-DE/LPanel.h new file mode 100644 index 00000000..bcea8eaf --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LPanel.h @@ -0,0 +1,81 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This is the generic class for creating a full-width panel that stays +// on top of all other windows (top or bottom of the screen only) +//=========================================== +#ifndef _LUMINA_DESKTOP_PANEL_H +#define _LUMINA_DESKTOP_PANEL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "panel-plugins/NewPP.h" +#include "panel-plugins/LPPlugin.h" + +#include +#include + +class LPanel : public QWidget{ + Q_OBJECT +private: + QBoxLayout *layout; + QSettings *settings; + QString PPREFIX; //internal prefix for all settings + QDesktopWidget *screen; + QWidget *bgWindow, *panelArea; + //QRect hidegeom, showgeom; //for hidden panels + QPoint hidepoint, showpoint; //for hidden panels: locations when hidden/visible + bool defaultpanel, horizontal, hidden, hascompositer; + QString screenID; + int panelnum; + int viswidth, fullwidth; + QList PLUGINS; + + int Screen(); //Turn the screenID into the appropriate number + +public: + LPanel(QSettings *file, QString scr = 0, int num =0, QWidget *parent=0); //settings file, screen number, panel number + ~LPanel(); + + int number(){ + return panelnum; + } + + QString prefix(){ + return PPREFIX; + } + + int visibleWidth(){ + return viswidth; + } + void prepareToClose(); + void scalePanel(double xscale, double yscale); + +public slots: + void UpdatePanel(bool geomonly = false); //Load the settings file and update the panel appropriately + void UpdateLocale(); //Locale Changed externally + void UpdateTheme(); //Theme Changed externally + +private slots: + void checkPanelFocus(); + +protected: + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LSession.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LSession.cpp new file mode 100644 index 00000000..0387555a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LSession.cpp @@ -0,0 +1,798 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LSession.h" +#include + +#include +#include +#include +#include "LXcbEventFilter.h" +#include "BootSplash.h" + +//LibLumina X11 class +#include +#include + +#include //for usleep() usage + +#ifndef DEBUG +#define DEBUG 0 +#endif + +XCBEventFilter *evFilter = 0; + +LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lumina-desktop"){ + if(this->isPrimaryProcess()){ + connect(this, SIGNAL(InputsAvailable(QStringList)), this, SLOT(NewCommunication(QStringList)) ); + this->setApplicationName("Lumina Desktop Environment"); + this->setApplicationVersion( LDesktopUtils::LuminaDesktopVersion() ); + this->setOrganizationName("LuminaDesktopEnvironment"); + this->setQuitOnLastWindowClosed(false); //since the LDesktop's are not necessarily "window"s + //Enabled a few of the simple effects by default + this->setEffectEnabled( Qt::UI_AnimateMenu, true); + this->setEffectEnabled( Qt::UI_AnimateCombo, true); + this->setEffectEnabled( Qt::UI_AnimateTooltip, true); + //this->setAttribute(Qt::AA_UseDesktopOpenGL); + //this->setAttribute(Qt::AA_UseHighDpiPixmaps); //allow pixmaps to be scaled up as well as down + //this->setStyle( new MenuProxyStyle); //QMenu icon size override + SystemTrayID = 0; VisualTrayID = 0; + sysWindow = 0; + TrayDmgEvent = 0; + TrayDmgError = 0; + lastActiveWin = 0; + cleansession = true; + TrayStopping = false; + screenTimer = new QTimer(this); + screenTimer->setSingleShot(true); + screenTimer->setInterval(50); + connect(screenTimer, SIGNAL(timeout()), this, SLOT(updateDesktops()) ); + for(int i=1; iinstallNativeEventFilter( evFilter ); + connect(this, SIGNAL(screenAdded(QScreen*)), this, SLOT(screensChanged()) ); + connect(this, SIGNAL(screenRemoved(QScreen*)), this, SLOT(screensChanged()) ); + connect(this, SIGNAL(primaryScreenChanged(QScreen*)), this, SLOT(screensChanged()) ); + } //end check for primary process +} + +LSession::~LSession(){ + if(this->isPrimaryProcess()){ + //WM->stopWM(); + for(int i=0; ideleteLater(); + } + //delete WM; + settingsmenu->deleteLater(); + appmenu->deleteLater(); + delete currTranslator; + if(mediaObj!=0){delete mediaObj;} + } +} + +void LSession::setupSession(){ + //Seed random number generator (if needed) + qsrand( QTime::currentTime().msec() ); + + BootSplash splash; + splash.showScreen("init"); + qDebug() << "Initializing Session"; + if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); } + QTime* timer = 0; + //if(DEBUG){ timer = new QTime(); timer->start(); qDebug() << " - Init srand:" << timer->elapsed();} + + //Setup the QSettings default paths + splash.showScreen("settings"); + if(DEBUG){ qDebug() << " - Init QSettings:" << timer->elapsed();} + sessionsettings = new QSettings("lumina-desktop", "sessionsettings"); + DPlugSettings = new QSettings("lumina-desktop","pluginsettings/desktopsettings"); + //Load the proper translation files + if(sessionsettings->value("ForceInitialLocale",false).toBool()){ + //Some system locale override it in place - change the env first + LUtils::setLocaleEnv( sessionsettings->value("InitLocale/LANG","").toString(), \ + sessionsettings->value("InitLocale/LC_MESSAGES","").toString(), \ + sessionsettings->value("InitLocale/LC_TIME","").toString(), \ + sessionsettings->value("InitLocale/LC_NUMERIC","").toString(), \ + sessionsettings->value("InitLocale/LC_MONETARY","").toString(), \ + sessionsettings->value("InitLocale/LC_COLLATE","").toString(), \ + sessionsettings->value("InitLocale/LC_CTYPE","").toString() ); + } + currTranslator = LUtils::LoadTranslation(this, "lumina-desktop"); +//use the system settings + //Setup the user's lumina settings directory as necessary + splash.showScreen("user"); + if(DEBUG){ qDebug() << " - Init User Files:" << timer->elapsed();} + checkUserFiles(); //adds these files to the watcher as well + + //Initialize the internal variables + DESKTOPS.clear(); + + //Start the background system tray + splash.showScreen("systray"); + if(DEBUG){ qDebug() << " - Init System Tray:" << timer->elapsed();} + startSystemTray(); + + //Initialize the global menus + qDebug() << " - Initialize system menus"; + splash.showScreen("apps"); + if(DEBUG){ qDebug() << " - Init AppMenu:" << timer->elapsed();} + appmenu = new AppMenu(); + + splash.showScreen("menus"); + if(DEBUG){ qDebug() << " - Init SettingsMenu:" << timer->elapsed();} + settingsmenu = new SettingsMenu(); + if(DEBUG){ qDebug() << " - Init SystemWindow:" << timer->elapsed();} + sysWindow = new SystemWindow(); + + //Initialize the desktops + splash.showScreen("desktop"); + if(DEBUG){ qDebug() << " - Init Desktops:" << timer->elapsed();} + desktopFiles = QDir(QDir::homePath()+"/Desktop").entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs, QDir::Name | QDir::IgnoreCase | QDir::DirsFirst); + updateDesktops(); + for(int i=0; i<6; i++){ LSession::processEvents(); } //Run through this a few times so the interface systems get up and running + + //Now setup the system watcher for changes + splash.showScreen("final"); + qDebug() << " - Initialize file system watcher"; + if(DEBUG){ qDebug() << " - Init QFileSystemWatcher:" << timer->elapsed();} + watcher = new QFileSystemWatcher(this); + QString confdir = sessionsettings->fileName().section("/",0,-2); + watcherChange(sessionsettings->fileName() ); + watcherChange( confdir+"/desktopsettings.conf" ); + watcherChange( confdir+"/fluxbox-init" ); + watcherChange( confdir+"/fluxbox-keys" ); + watcherChange( confdir+"/favorites.list" ); + //Try to watch the localized desktop folder too + if(QFile::exists(QDir::homePath()+"/"+tr("Desktop"))){ watcherChange( QDir::homePath()+"/"+tr("Desktop") ); } + watcherChange( QDir::homePath()+"/Desktop" ); + + //connect internal signals/slots + connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherChange(QString)) ); + connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherChange(QString)) ); + connect(this, SIGNAL(aboutToQuit()), this, SLOT(SessionEnding()) ); + if(DEBUG){ qDebug() << " - Init Finished:" << timer->elapsed(); delete timer;} + for(int i=0; i<4; i++){ LSession::processEvents(); } //Again, just a few event loops here so thing can settle before we close the splash screen + //launchStartupApps(); + QTimer::singleShot(500, this, SLOT(launchStartupApps()) ); + splash.hide(); + LSession::processEvents(); + splash.close(); + LSession::processEvents(); +} + +void LSession::CleanupSession(){ + //Close any running applications and tray utilities (Make sure to keep the UI interactive) + LSession::processEvents(); + QDateTime time = QDateTime::currentDateTime(); + qDebug() << "Start closing down the session: " << time.toString( Qt::SystemLocaleShortDate); + //Create a temporary flag to prevent crash dialogs from opening during cleanup + LUtils::writeFile("/tmp/.luminastopping",QStringList() << "yes", true); + //Start the logout chimes (if necessary) + LOS::setAudioVolume( LOS::audioVolume() ); //make sure the audio volume is saved in the backend for the next login + bool playaudio = sessionsettings->value("PlayLogoutAudio",true).toBool(); + if( playaudio ){ playAudioFile(LOS::LuminaShare()+"Logout.ogg"); } + //Stop the background system tray (detaching/closing apps as necessary) + stopSystemTray(!cleansession); + //Now perform any other cleanup + if(cleansession){ + //Close any open windows + //qDebug() << " - Closing any open windows"; + QList WL = XCB->WindowList(true); + for(int i=0; iWindowClass(WL[i]) << WL[i]; + XCB->CloseWindow(WL[i]); + LSession::processEvents(); + } + //Now wait a moment for things to close down before quitting + for(int i=0; i<20; i++){ LSession::processEvents(); usleep(25); } //1/2 second pause + //Kill any remaining windows + WL = XCB->WindowList(true); //all workspaces + for(int i=0; iWindowClass(WL[i]) << WL[i]; + XCB->KillClient(WL[i]); + LSession::processEvents(); + } + } + evFilter->StopEventHandling(); + //Stop the window manager + //qDebug() << " - Stopping the window manager"; + //WM->stopWM(); + //Now close down the desktop + qDebug() << " - Closing down the desktop elements"; + for(int i=0; iprepareToClose(); + //don't actually close them yet - that will happen when the session exits + // this will leave the wallpapers up for a few moments (preventing black screens) + } + //Now wait a moment for things to close down before quitting + if(playaudio){ + //wait a max of 5 seconds for audio to finish + bool waitmore = true; + for(int i=0; i<100 && waitmore; i++){ + usleep(50000); //50ms = 50000 us + waitmore = (mediaObj->state()==QMediaPlayer::PlayingState); + LSession::processEvents(); + } + if(waitmore){ mediaObj->stop(); } //timed out + }else{ + for(int i=0; i<20; i++){ LSession::processEvents(); usleep(25000); } //1/2 second pause + } + //Clean up the temporary flag + if(QFile::exists("/tmp/.luminastopping")){ QFile::remove("/tmp/.luminastopping"); } +} + +int LSession::VersionStringToNumber(QString version){ + version = version.section("-",0,0); //trim any extra labels off the end + int maj, mid, min; //major/middle/minor version numbers (..) + maj = mid = min = 0; + bool ok = true; + maj = version.section(".",0,0).toInt(&ok); + if(ok){ mid = version.section(".",1,1).toInt(&ok); }else{ maj = 0; } + if(ok){ min = version.section(".",2,2).toInt(&ok); }else{ mid = 0; } + if(!ok){ min = 0; } + //Now assemble the number + //NOTE: This format allows numbers to be anywhere from 0->999 without conflict + return (maj*1000000 + mid*1000 + min); +} + +void LSession::NewCommunication(QStringList list){ + if(DEBUG){ qDebug() << "New Communications:" << list; } + for(int i=0; ivalue("EnableNumlock",false).toBool()){ + QProcess::startDetached("numlockx on"); + }else{ + QProcess::startDetached("numlockx off"); + } + } + int tmp = LOS::ScreenBrightness(); + if(tmp>0){ + LOS::setScreenBrightness( tmp ); + qDebug() << " - - Screen Brightness:" << QString::number(tmp)+"%"; + } + QProcess::startDetached("nice lumina-open -autostart-apps"); + + //Re-load the screen brightness and volume settings from the previous session + // Wait until after the XDG-autostart functions, since the audio system might be started that way + qDebug() << " - Loading previous settings"; + tmp = LOS::audioVolume(); + LOS::setAudioVolume(tmp); + qDebug() << " - - Audio Volume:" << QString::number(tmp)+"%"; + + //Now play the login music since we are finished + if(sessionsettings->value("PlayStartupAudio",true).toBool()){ + //Make sure to re-set the system volume to the last-used value at outset + int vol = LOS::audioVolume(); + if(vol>=0){ LOS::setAudioVolume(vol); } + LSession::playAudioFile(LOS::LuminaShare()+"Login.ogg"); + } + qDebug() << " - Finished with startup routines"; +} + +void LSession::StartLogout(){ + CleanupSession(); + QCoreApplication::exit(0); +} + +void LSession::StartShutdown(bool skipupdates){ + CleanupSession(); + LOS::systemShutdown(skipupdates); + QCoreApplication::exit(0); +} + +void LSession::StartReboot(bool skipupdates){ + CleanupSession(); + LOS::systemRestart(skipupdates); + QCoreApplication::exit(0); +} + +void LSession::reloadIconTheme(){ + //Wait a moment for things to settle before sending out the signal to the interfaces + QApplication::processEvents(); + QApplication::processEvents(); + emit IconThemeChanged(); +} + +void LSession::watcherChange(QString changed){ + if(DEBUG){ qDebug() << "Session Watcher Change:" << changed; } + //if(changed.endsWith("fluxbox-init") || changed.endsWith("fluxbox-keys")){ refreshWindowManager(); } + if(changed.endsWith("sessionsettings.conf") ){ + sessionsettings->sync(); + //qDebug() << "Session Settings Changed"; + if(sessionsettings->contains("Qt5_theme_engine")){ + QString engine = sessionsettings->value("Qt5_theme_engine","").toString(); + //qDebug() << "Set Qt5 theme engine: " << engine; + if(engine.isEmpty()){ unsetenv("QT_QPA_PLATFORMTHEME"); } + else{ setenv("QT_QPA_PLATFORMTHEME", engine.toUtf8().data(),1); } + } + emit SessionConfigChanged(); + }else if(changed.endsWith("desktopsettings.conf") ){ emit DesktopConfigChanged(); } + else if(changed == QDir::homePath()+"/Desktop" || changed == QDir::homePath()+"/"+tr("Desktop") ){ + desktopFiles = QDir(changed).entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs ,QDir::Name | QDir::IgnoreCase | QDir::DirsFirst); + if(DEBUG){ qDebug() << "New Desktop Files:" << desktopFiles.length(); } + emit DesktopFilesChanged(); + }else if(changed.endsWith("favorites.list")){ emit FavoritesChanged(); } + //Now ensure this file was not removed from the watcher + if(!watcher->files().contains(changed) && !watcher->directories().contains(changed)){ + if(!QFile::exists(changed)){ + //Create the file really quick to ensure it can be watched + //TODO + } + watcher->addPath(changed); + } +} + +void LSession::screensChanged(){ + qDebug() << "Screen Number Changed"; + if(screenTimer->isActive()){ screenTimer->stop(); } + screenTimer->start(); +} + +void LSession::screenResized(int scrn){ + qDebug() << "Screen Resized:" << scrn; + if(screenTimer->isActive()){ screenTimer->stop(); } + screenTimer->start(); +} + +void LSession::checkWindowGeoms(){ + //Only do one window per run (this will be called once per new window - with time delays between) + if(checkWin.isEmpty()){ return; } + WId win = checkWin.takeFirst(); + if(RunningApps.contains(win) ){ //just to make sure it did not close during the delay + adjustWindowGeom( win ); + } +} + +void LSession::checkUserFiles(){ + //internal version conversion examples: + // [1.0.0 -> 1000000], [1.2.3 -> 1002003], [0.6.1 -> 6001] + QString OVS = sessionsettings->value("DesktopVersion","0").toString(); //Old Version String + bool changed = LDesktopUtils::checkUserFiles(OVS); + if(changed){ + //Save the current version of the session to the settings file (for next time) + sessionsettings->setValue("DesktopVersion", this->applicationVersion()); + } +} + +void LSession::refreshWindowManager(){ + LUtils::runCmd("touch \""+QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/fluxbox-init\"" ); +} + +void LSession::updateDesktops(){ + qDebug() << " - Update Desktops"; + QDesktopWidget *DW = this->desktop(); + int sC = DW->screenCount(); + qDebug() << " Screen Count:" << sC; + qDebug() << " DESKTOPS Length:" << DESKTOPS.length(); + if(sC<1){ return; } //stop here - no screens available temporarily (displayport/4K issue) + + for(int i=0; iscreenGeometry(i); } + + bool firstrun = (DESKTOPS.length()==0); + bool numchange = DESKTOPS.length()!=sC; + + // If the screen count is changing on us + if ( sC != DW->screenCount() ) { + qDebug() << "Screen Count changed while running"; + return; + } + + //First clean up any current desktops + QList dnums; //keep track of which screens are already managed + QList geoms; + for(int i=0; iScreen() < 0 || DESKTOPS[i]->Screen() >= sC || geoms.contains(DW->screenGeometry(DESKTOPS[i]->Screen())) ) { + //qDebug() << " - Close desktop:" << i; + qDebug() << " - Close desktop on screen:" << DESKTOPS[i]->Screen(); + DESKTOPS[i]->prepareToClose(); + DESKTOPS.takeAt(i)->deleteLater(); + i--; + } else { + //qDebug() << " - Show desktop:" << i; + qDebug() << " - Show desktop on screen:" << DESKTOPS[i]->Screen(); + DESKTOPS[i]->UpdateGeometry(); + DESKTOPS[i]->show(); + dnums << DESKTOPS[i]->Screen(); + geoms << DW->screenGeometry(DESKTOPS[i]->Screen()); + } + } + + //Now add any new desktops + for(int i=0; iscreenGeometry(i)) ){ + //Start the desktop on this screen + qDebug() << " - Start desktop on screen:" << i; + DESKTOPS << new LDesktop(i); + geoms << DW->screenGeometry(i); + } + } + + //Make sure fluxbox also gets prompted to re-load screen config if the number of screens changes in the middle of a session + if(numchange && !firstrun) { + qDebug() << "Update WM"; + refreshWindowManager(); + } + + //Make sure all the background windows are registered on the system as virtual roots + QTimer::singleShot(100,this, SLOT(registerDesktopWindows())); +} + +void LSession::registerDesktopWindows(){ + QList wins; + for(int i=0; ibackgroundID(); + } + XCB->RegisterVirtualRoots(wins); +} + +void LSession::adjustWindowGeom(WId win, bool maximize){ + //return; //temporary disable + if(DEBUG){ qDebug() << "AdjustWindowGeometry():" << win << maximize << XCB->WindowClass(win); } + if(XCB->WindowIsFullscreen(win) >=0 ){ return; } //don't touch it + //Quick hack for making sure that new windows are not located underneath any panels + // Get the window location + QRect geom = XCB->WindowGeometry(win, false); + //Get the frame size + QList frame = XCB->WindowFrameGeometry(win); //[top,bottom,left,right] sizes of the frame + //Calculate the full geometry (window + frame) + QRect fgeom = QRect(geom.x()-frame[2], geom.y()-frame[0], geom.width()+frame[2]+frame[3], geom.height()+frame[0]+frame[1]); + if(DEBUG){ + qDebug() << "Check Window Geometry:" << XCB->WindowClass(win) << !geom.isNull() << geom << fgeom; + } + if(geom.isNull()){ return; } //Could not get geometry for some reason + //Get the available geometry for the screen the window is on + QRect desk; + for(int i=0; idesktop()->screenGeometry(DESKTOPS[i]->Screen()).contains(geom.center()) ){ + //Window is on this screen + if(DEBUG){ qDebug() << " - On Screen:" << DESKTOPS[i]->Screen(); } + desk = DESKTOPS[i]->availableScreenGeom(); + if(DEBUG){ qDebug() << " - Screen Geom:" << desk; } + break; + } + } + if(desk.isNull()){ return; } //Unable to determine screen + //Adjust the window location if necessary + if(maximize){ + if(DEBUG){ qDebug() << " - Maximizing New Window:" << desk.width() << desk.height(); } + geom = desk; //Use the full screen + XCB->MoveResizeWindow(win, geom); + XCB->MaximizeWindow(win, true); //directly set the appropriate "maximized" flags (bypassing WM) + + }else if(!desk.contains(fgeom) ){ + //Adjust origin point for left/top margins + if(fgeom.y() < desk.y()){ geom.moveTop(desk.y()+frame[0]); fgeom.moveTop(desk.y()); } //move down to the edge (top panel) + if(fgeom.x() < desk.x()){ geom.moveLeft(desk.x()+frame[2]); fgeom.moveLeft(desk.x()); } //move right to the edge (left panel) + //Adjust size for bottom margins (within reason, since window titles are on top normally) + // if(geom.right() > desk.right() && (geom.width() > 100)){ geom.setRight(desk.right()); } + if(fgeom.bottom() > desk.bottom() && geom.height() > 10){ + if(DEBUG){ qDebug() << "Adjust Y:" << fgeom << geom << desk; } + int diff = fgeom.bottom()-desk.bottom(); //amount of overlap + if(DEBUG){ qDebug() << "Y-Diff:" << diff; } + if(diff < 0){ diff = -diff; } //need a positive value + if( (fgeom.height()+ diff)< desk.height()){ + //just move the window - there is room for it above + geom.setBottom(desk.bottom()-frame[1]); + fgeom.setBottom(desk.bottom()); + }else if(geom.height() > diff){ //window bigger than the difference + //Need to resize the window - keeping the origin point the same + geom.setHeight( geom.height()-diff-1 ); //shrink it by the difference (need an extra pixel somewhere) + fgeom.setHeight( fgeom.height()-diff ); + } + } + //Now move/resize the window + if(DEBUG){ + qDebug() << " - New Geom:" << geom << fgeom; + } + XCB->WM_Request_MoveResize_Window(win, geom); + } + +} + +void LSession::SessionEnding(){ + stopSystemTray(); //just in case it was not stopped properly earlier +} + +//=============== +// SYSTEM ACCESS +//=============== +void LSession::LaunchApplication(QString cmd){ + LSession::setOverrideCursor(QCursor(Qt::BusyCursor)); + QProcess::startDetached(cmd); +} + +QFileInfoList LSession::DesktopFiles(){ + return desktopFiles; +} + +QRect LSession::screenGeom(int num){ + if(num < 0 || num >= this->desktop()->screenCount() ){ return QRect(); } + QRect geom = this->desktop()->screenGeometry(num); + return geom; +} + +AppMenu* LSession::applicationMenu(){ + return appmenu; +} + +SettingsMenu* LSession::settingsMenu(){ + return settingsmenu; +} + +QSettings* LSession::sessionSettings(){ + return sessionsettings; +} + +QSettings* LSession::DesktopPluginSettings(){ + return DPlugSettings; +} + +WId LSession::activeWindow(){ + //Check the last active window pointer first + WId active = XCB->ActiveWindow(); + //qDebug() << "Check Active Window:" << active << lastActiveWin; + if(RunningApps.contains(active)){ lastActiveWin = active; } + else if(RunningApps.contains(lastActiveWin) && XCB->WindowState(lastActiveWin) >= LXCB::VISIBLE){} //no change needed + else if(RunningApps.contains(lastActiveWin) && RunningApps.length()>1){ + int start = RunningApps.indexOf(lastActiveWin); + if(start<1){ lastActiveWin = RunningApps.length()-1; } //wrap around to the last item + else{ lastActiveWin = RunningApps[start-1]; } + }else{ + //Need to change the last active window - find the first one which is visible + lastActiveWin = 0; //fallback value - nothing active + for(int i=0; iWindowState(RunningApps[i]) >= LXCB::VISIBLE){ + lastActiveWin = RunningApps[i]; + break; + } + } + //qDebug() << " -- New Last Active Window:" << lastActiveWin; + } + return lastActiveWin; +} + +//Temporarily change the session locale (nothing saved between sessions) +void LSession::switchLocale(QString localeCode){ + currTranslator = LUtils::LoadTranslation(this, "lumina-desktop", localeCode, currTranslator); + if(currTranslator!=0 || localeCode=="en_US"){ + LUtils::setLocaleEnv(localeCode); //will set everything to this locale (no custom settings) + } + emit LocaleChanged(); +} + +void LSession::systemWindow(){ + if(sysWindow==0){ sysWindow = new SystemWindow(); } + else{ sysWindow->updateWindow(); } + sysWindow->show(); + //LSession::processEvents(); +} + +//Play System Audio +void LSession::playAudioFile(QString filepath){ + if( !QFile::exists(filepath) ){ return; } + //Setup the audio output systems for the desktop + if(DEBUG){ qDebug() << "Play Audio File"; } + if(mediaObj==0){ qDebug() << " - Initialize media player"; mediaObj = new QMediaPlayer(); } + if(mediaObj !=0 ){ + if(DEBUG){ qDebug() << " - starting playback:" << filepath; } + mediaObj->setVolume(100); + mediaObj->setMedia(QUrl::fromLocalFile(filepath)); + mediaObj->play(); + LSession::processEvents(); + } + if(DEBUG){ qDebug() << " - Done with Audio File"; } +} +// ======================= +// XCB EVENT FILTER FUNCTIONS +// ======================= +void LSession::RootSizeChange(){ + qDebug() << "Got Root Size Change"; + if(DESKTOPS.isEmpty()){ return; } //Initial setup not run yet + screenTimer->start(); +} + +void LSession::WindowPropertyEvent(){ + if(DEBUG){ qDebug() << "Window Property Event"; } + QList newapps = XCB->WindowList(); + if(RunningApps.length() < newapps.length()){ + //New Window found + //qDebug() << "New window found"; + LSession::restoreOverrideCursor(); //restore the mouse cursor back to normal (new window opened?) + //Perform sanity checks on any new window geometries + for(int i=0; iSelectInput(newapps[i]); //make sure we get property/focus events for this window + if(DEBUG){ qDebug() << "New Window - check geom in a moment:" << XCB->WindowClass(newapps[i]); } + QTimer::singleShot(50, this, SLOT(checkWindowGeoms()) ); + } + } + } + + //Now save the list and send out the event + RunningApps = newapps; + emit WindowListEvent(); +} + +void LSession::WindowPropertyEvent(WId win){ + //Emit the single-app signal if the window in question is one used by the task manager + if(RunningApps.contains(win)){ + if(DEBUG){ qDebug() << "Single-window property event"; } + //emit WindowListEvent(); + WindowPropertyEvent(); //Run through the entire routine for window checks + }else if(RunningTrayApps.contains(win)){ + emit TrayIconChanged(win); + } +} + +void LSession::SysTrayDockRequest(WId win){ + if(TrayStopping){ return; } + attachTrayWindow(win); //Check to see if the window is already registered +} + +void LSession::WindowClosedEvent(WId win){ + if(TrayStopping){ return; } + removeTrayWindow(win); //Check to see if the window is a tray app +} + +void LSession::WindowConfigureEvent(WId win){ + if(TrayStopping){ return; } + if(RunningTrayApps.contains(win)){ + if(DEBUG){ qDebug() << "SysTray: Configure Event"; } + emit TrayIconChanged(win); //trigger a repaint event + }else if(RunningApps.contains(win)){ + WindowPropertyEvent(); + } +} + +void LSession::WindowDamageEvent(WId win){ + if(TrayStopping){ return; } + if(RunningTrayApps.contains(win)){ + if(DEBUG){ qDebug() << "SysTray: Damage Event"; } + emit TrayIconChanged(win); //trigger a repaint event + } +} + +void LSession::WindowSelectionClearEvent(WId win){ + if(win==SystemTrayID && !TrayStopping){ + qDebug() << "Stopping system tray"; + stopSystemTray(true); //make sure to detach all windows + } +} + + +//====================== +// SYSTEM TRAY FUNCTIONS +//====================== +bool LSession::registerVisualTray(WId visualTray){ + //Only one visual tray can be registered at a time + // (limitation of how tray apps are embedded) + if(TrayStopping){ return false; } + else if(VisualTrayID==0){ VisualTrayID = visualTray; return true; } + else if(VisualTrayID==visualTray){ return true; } + else{ return false; } +} + +void LSession::unregisterVisualTray(WId visualTray){ + if(VisualTrayID==visualTray){ + qDebug() << "Unregistered Visual Tray"; + VisualTrayID=0; + if(!TrayStopping){ emit VisualTrayAvailable(); } + } +} + +QList LSession::currentTrayApps(WId visualTray){ + if(visualTray==VisualTrayID){ + //Check the validity of all the current tray apps (make sure nothing closed erratically) + for(int i=0; iWindowClass(RunningTrayApps[i]).isEmpty()){ RunningTrayApps.removeAt(i); i--; } + } + return RunningTrayApps; + }else if( registerVisualTray(visualTray) ){ + return RunningTrayApps; + }else{ + return QList(); + } +} + +void LSession::startSystemTray(){ + if(SystemTrayID!=0){ return; } + RunningTrayApps.clear(); //nothing running yet + SystemTrayID = XCB->startSystemTray(0); + TrayStopping = false; + if(SystemTrayID!=0){ + XCB->SelectInput(SystemTrayID); //make sure TrayID events get forwarded here + TrayDmgEvent = XCB->GenerateDamageID(SystemTrayID); + evFilter->setTrayDamageFlag(TrayDmgEvent); + qDebug() << "System Tray Started Successfully"; + if(DEBUG){ qDebug() << " - System Tray Flags:" << TrayDmgEvent << TrayDmgError; } + } +} + +void LSession::stopSystemTray(bool detachall){ + if(TrayStopping){ return; } //already run + qDebug() << "Stopping system tray..."; + TrayStopping = true; //make sure the internal list does not modified during this + //Close all the running Tray Apps + QList tmpApps = RunningTrayApps; + RunningTrayApps.clear(); //clear this ahead of time so tray's do not attempt to re-access the apps + if(!detachall){ + for(int i=0; iWindowClass(tmpApps[i]); + //Tray apps are special and closing the window does not close the app + XCB->KillClient(tmpApps[i]); + LSession::processEvents(); + } + } + //Now close down the tray backend + XCB->closeSystemTray(SystemTrayID); + SystemTrayID = 0; + TrayDmgEvent = 0; + TrayDmgError = 0; + evFilter->setTrayDamageFlag(0); //turn off tray event handling + emit TrayListChanged(); + LSession::processEvents(); +} + +void LSession::attachTrayWindow(WId win){ + //static int appnum = 0; + if(TrayStopping){ return; } + if(RunningTrayApps.contains(win)){ return; } //already managed + qDebug() << "Session Tray: Window Added"; + RunningTrayApps << win; + LSession::restoreOverrideCursor(); + if(DEBUG){ qDebug() << "Tray List Changed"; } + emit TrayListChanged(); +} + +void LSession::removeTrayWindow(WId win){ + if(SystemTrayID==0){ return; } + for(int i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Globals.h" +#include "AppMenu.h" +#include "SettingsMenu.h" +#include "SystemWindow.h" +#include "LDesktop.h" +//#include "WMProcess.h" +//#include "BootSplash.h" + +#include +#include + +//SYSTEM TRAY STANDARD DEFINITIONS +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +/*class MenuProxyStyle : public QProxyStyle{ +public: + int pixelMetric(PixelMetric metric, const QStyleOption *option=0, const QWidget *widget=0) const{ + if(metric==PM_SmallIconSize){ return 22; } //override QMenu icon size (make it larger) + else{ return QProxyStyle::pixelMetric(metric, option, widget); } //use the current style for everything else + } +};*/ + +class LSession : public LSingleApplication{ + Q_OBJECT +public: + LSession(int &argc, char **argv); + ~LSession(); + //Functions to be called during startup + void setupSession(); + + //Public System Tray Functions + QList currentTrayApps(WId visualTray); + bool registerVisualTray(WId); + void unregisterVisualTray(WId); + + //Public start menu buttons + bool registerStartButton(QString ID); + void unregisterStartButton(QString ID); + + //Special functions for XCB event filter parsing only + // (DO NOT USE MANUALLY) + void RootSizeChange(); + void WindowPropertyEvent(); + void WindowPropertyEvent(WId); + void SysTrayDockRequest(WId); + void WindowClosedEvent(WId); + void WindowConfigureEvent(WId); + void WindowDamageEvent(WId); + void WindowSelectionClearEvent(WId); + + //System Access + //Return a pointer to the current session + static LSession* handle(){ + return static_cast(LSession::instance()); + } + + static void LaunchApplication(QString cmd); + QFileInfoList DesktopFiles(); + + QRect screenGeom(int num); + + AppMenu* applicationMenu(); + void systemWindow(); + SettingsMenu* settingsMenu(); + LXCB *XCB; //class for XCB usage + + QSettings* sessionSettings(); + QSettings* DesktopPluginSettings(); + + //Keep track of which non-desktop window should be treated as active + WId activeWindow(); //This will return the last active window if a desktop element is currently active + + //Temporarily change the session locale (nothing saved between sessions) + void switchLocale(QString localeCode); + + //Play System Audio + void playAudioFile(QString filepath); + //Window Adjustment Routine (due to Fluxbox not respecting _NET_WM_STRUT) + void adjustWindowGeom(WId win, bool maximize = false); + +private: + //WMProcess *WM; + QList DESKTOPS; + QFileSystemWatcher *watcher; + QTimer *screenTimer; + + //Internal variable for global usage + AppMenu *appmenu; + SettingsMenu *settingsmenu; + SystemWindow *sysWindow; + QTranslator *currTranslator; + QMediaPlayer *mediaObj; + QSettings *sessionsettings, *DPlugSettings; + bool cleansession; + //QList savedScreens; + + //System Tray Variables + WId SystemTrayID, VisualTrayID; + int TrayDmgEvent, TrayDmgError; + QList RunningTrayApps; + bool TrayStopping; + //Start Button Variables + QString StartButtonID; + + //Task Manager Variables + WId lastActiveWin; + QList RunningApps; + QList checkWin; + QFileInfoList desktopFiles; + + void CleanupSession(); + + int VersionStringToNumber(QString version); + +public slots: + void StartLogout(); + void StartShutdown(bool skipupdates = false); + void StartReboot(bool skipupdates = false); + + void reloadIconTheme(); + +private slots: + void NewCommunication(QStringList); + void launchStartupApps(); //used during initialization + void watcherChange(QString); + void screensChanged(); + void screenResized(int); + void checkWindowGeoms(); + + //System Tray Functions + void startSystemTray(); + void stopSystemTray(bool detachall = false); + void attachTrayWindow(WId); + void removeTrayWindow(WId); + + //Internal simplification functions + void checkUserFiles(); + void refreshWindowManager(); + void updateDesktops(); + void registerDesktopWindows(); + + + void SessionEnding(); + +signals: + //System Tray Signals + void VisualTrayAvailable(); //new Visual Tray Plugin can be registered + void TrayListChanged(); //Item added/removed from the list + void TrayIconChanged(WId); //WinID of Tray App + //Start Button signals + void StartButtonAvailable(); + void StartButtonActivated(); + //Task Manager Signals + void WindowListEvent(WId); + void WindowListEvent(); + //General Signals + void LocaleChanged(); + void IconThemeChanged(); + void DesktopConfigChanged(); + void SessionConfigChanged(); + void FavoritesChanged(); + void DesktopFilesChanged(); + void WorkspaceChanged(); + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.cpp new file mode 100644 index 00000000..6a6cea0b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.cpp @@ -0,0 +1,48 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LWinInfo.h" + +#include + +#include "LSession.h" + +//Information Retrieval + // Don't cache these results because they can change regularly +QString LWinInfo::text(){ + if(window==0){ return ""; } + QString nm = LSession::handle()->XCB->WindowVisibleIconName(window); + if(nm.simplified().isEmpty()){ nm = LSession::handle()->XCB->WindowIconName(window); } + if(nm.simplified().isEmpty()){ nm = LSession::handle()->XCB->WindowVisibleName(window); } + if(nm.simplified().isEmpty()){ nm = LSession::handle()->XCB->WindowName(window); } + if(nm.simplified().isEmpty()){ nm = LSession::handle()->XCB->OldWindowIconName(window); } + if(nm.simplified().isEmpty()){ nm = LSession::handle()->XCB->OldWindowName(window); } + //Make sure that the text is a reasonable size (40 char limit) + //if(nm.length()>40){ nm = nm.left(40)+"..."; } + return nm; +} + +QIcon LWinInfo::icon(bool &noicon){ + if(window==0){ noicon = true; return QIcon();} + noicon = false; + QIcon ico = LSession::handle()->XCB->WindowIcon(window); + //Check for a null icon, and supply one if necessary + if(ico.isNull()){ ico = LXDG::findIcon( this->Class().toLower(),""); } + if(ico.isNull()){ico = LXDG::findIcon("preferences-system-windows",""); noicon=true;} + return ico; +} + +QString LWinInfo::Class(){ + return LSession::handle()->XCB->WindowClass(window); +} + +LXCB::WINDOWVISIBILITY LWinInfo::status(bool update){ + if(window==0){ return LXCB::IGNORE; } + if(update || cstate == LXCB::IGNORE){ + cstate = LSession::handle()->XCB->WindowState(window); + } + return cstate; +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.h b/src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.h new file mode 100644 index 00000000..3d2ea65a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LWinInfo.h @@ -0,0 +1,50 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_WINDOW_INFO_H +#define _LUMINA_DESKTOP_WINDOW_INFO_H + +// Qt includes +#include +#include +#include +#include + +// libLumina includes +#include +#include + +// Local includes +//#include "Globals.h" //For the STATES enumeration definition +//#include "LSession.h" + + +class LWinInfo{ +private: + WId window; + LXCB::WINDOWVISIBILITY cstate; //current window state + +public: + LWinInfo(WId id = 0){ + window = id; + cstate = LXCB::IGNORE; //make sure this gets updates with the first "status" call + } + ~LWinInfo(){}; + + //The current window ID + WId windowID(){ + return window; + } + + //Information Retrieval + // Don't cache these results because they can change regularly + QString text(); + QIcon icon(bool &noicon); + QString Class(); + LXCB::WINDOWVISIBILITY status(bool update = false); +}; + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.cpp new file mode 100644 index 00000000..ca7fb38d --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.cpp @@ -0,0 +1,118 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LXcbEventFilter.h" + +//For all the XCB interactions and atoms +// is accessed via +// session->XCB->EWMH.(atom name) +// session->XCB->(do something) +#include +#include + +XCBEventFilter::XCBEventFilter(LSession *sessionhandle) : QAbstractNativeEventFilter(){ + session = sessionhandle; //save this for interaction with the session later + TrayDmgFlag = 0; + stopping = false; + session->XCB->SelectInput(QX11Info::appRootWindow()); //make sure we get root window events + InitAtoms(); +} + +void XCBEventFilter::setTrayDamageFlag(int flag){ + //Special flag for system tray damage events + TrayDmgFlag = flag + XCB_DAMAGE_NOTIFY; //save the whole flag (no calculations later) +} + +//This function format taken directly from the Qt5.3 documentation +bool XCBEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *){ + if(stopping){ return false; } //don't do any parsing + //qDebug() << "New Event"; + if(eventType=="xcb_generic_event_t"){ + //qDebug() << " - XCB event"; + //Convert to known event type (for X11 systems) + xcb_generic_event_t *ev = static_cast(message); + //Now parse the event and emit signals as necessary + switch( ev->response_type & ~0x80){ +//============================== + case XCB_PROPERTY_NOTIFY: + //qDebug() << "Property Notify Event:"; + //qDebug() << " - Root Window:" << QX11Info::appRootWindow(); + //qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window; + //System-specific proprty change + if( ((xcb_property_notify_event_t*)ev)->window == QX11Info::appRootWindow() \ + && ( ( ((xcb_property_notify_event_t*)ev)->atom == session->XCB->EWMH._NET_DESKTOP_GEOMETRY) \ + || (((xcb_property_notify_event_t*)ev)->atom == session->XCB->EWMH._NET_WORKAREA) )){ + session->RootSizeChange(); + }else if( ((xcb_property_notify_event_t*)ev)->window == QX11Info::appRootWindow() \ + && ( ( ((xcb_property_notify_event_t*)ev)->atom == session->XCB->EWMH._NET_CURRENT_DESKTOP) )){ + //qDebug() << "Got Workspace Change"; + session->emit WorkspaceChanged(); + }else if( SysNotifyAtoms.contains( ((xcb_property_notify_event_t*)ev)->atom ) ){ + //Update the status/list of all running windows + session->WindowPropertyEvent(); + + //window-specific property change + }else if( WinNotifyAtoms.contains( ((xcb_property_notify_event_t*)ev)->atom ) ){ + //Ping only that window + //session->WindowPropertyEvent( ((xcb_property_notify_event_t*)ev)->window ); + session->WindowPropertyEvent(); + } + break; +//============================== + case XCB_CLIENT_MESSAGE: + //qDebug() << "Client Message Event"; + //qDebug() << " - Root Window:" << QX11Info::appRootWindow(); + //qDebug() << " - Given Window:" << ((xcb_client_message_event_t*)ev)->window; + if( TrayDmgFlag!=0 && ((xcb_client_message_event_t*)ev)->type == _NET_SYSTEM_TRAY_OPCODE && ((xcb_client_message_event_t*)ev)->format == 32){ + //data32[0] is timestamp, [1] is opcode, [2] is window handle + if(SYSTEM_TRAY_REQUEST_DOCK == ((xcb_client_message_event_t*)ev)->data.data32[1]){ + session->SysTrayDockRequest( ((xcb_client_message_event_t*)ev)->data.data32[2] ); + } + //Ignore the System Tray messages at the moment (let the WM handle it) + + //window-specific property changes + /*}else if( ((xcb_client_message_event_t*)ev)->type == session->XCB->EWMH._NET_WM_STATE ){ + if( session->XCB->WindowIsMaximized( ((xcb_client_message_event_t*)ev)->window ) ){ + //Quick fix for maximized windows (since Fluxbox is not doing the STRUT detection properly) + session->adjustWindowGeom( ((xcb_client_message_event_t*)ev)->window ); + } + session->WindowPropertyEvent( ((xcb_client_message_event_t*)ev)->window );*/ + }else if( WinNotifyAtoms.contains( ((xcb_client_message_event_t*)ev)->type ) ){ + //Ping only that window + //session->WindowPropertyEvent( ((xcb_client_message_event_t*)ev)->window ); + session->WindowPropertyEvent(); + } + break; +//============================== + case XCB_DESTROY_NOTIFY: + //qDebug() << "Window Closed Event"; + session->WindowClosedEvent( ( (xcb_destroy_notify_event_t*)ev )->window ); + break; +//============================== + case XCB_CONFIGURE_NOTIFY: + //qDebug() << "Configure Notify Event"; + session->WindowConfigureEvent( ((xcb_configure_notify_event_t*)ev)->window ); + break; +//============================== + case XCB_SELECTION_CLEAR: + //qDebug() << "Selection Clear Event"; + session->WindowSelectionClearEvent( ((xcb_selection_clear_event_t*)ev)->owner ); + break; +//============================== + default: + if(TrayDmgFlag!=0){ + //if( (ev->response_type & ~0x80)==TrayDmgFlag){ + session->WindowDamageEvent( ((xcb_damage_notify_event_t*)ev)->drawable ); + //} + }/*else{ + qDebug() << "Default Event:" << (ev->response_type & ~0x80); + }*/ +//============================== + } + } + //qDebug() << " - finished event"; + return false; //make sure the handling keeps going (transparent watching of events) +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.h b/src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.h new file mode 100644 index 00000000..c56471c9 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/LXcbEventFilter.h @@ -0,0 +1,104 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class provides the XCB ->Xlib conversion necessary for Qt5 usage +//=========================================== +#ifndef _LUMINA_DESKTOP_XCB_FILTER_H +#define _LUMINA_DESKTOP_XCB_FILTER_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "LSession.h" + +/* +List of XCB response types (since almost impossible to find good docs on XCB) +switch (xcb_generic_event_t*->response_type & ~0x80) +case values: +XCB_KEY_[PRESS | RELEASE] +XCB_BUTTON_[PRESS | RELEASE] +XCB_MOTION_NOTIFY +XCB_ENTER_NOTIFY +XCB_LEAVE_NOTIFY +XCB_FOCUS_[IN | OUT] +XCB_KEYMAP_NOTIFY +XCB_EXPOSE +XCB_GRAPHICS_EXPOSURE +XCB_VISIBILITY_NOTIFY +XCB_CREATE_NOTIFY +XCB_DESTROY_NOTIFY +XCB_UNMAP_NOTIFY +XCB_MAP_[NOTIFY | REQUEST] +XCB_REPARENT_NOTIFY +XCB_CONFIGURE_[NOTIFY | REQUEST] +XCB_GRAVITY_NOTIFY +XCB_RESIZE_REQUEST +XCB_CIRCULATE_[NOTIFY | REQUEST] +XCB_PROPERTY_NOTIFY +XCB_SELECTION_[CLEAR | REQUEST | NOTIFY] +XCB_COLORMAP_NOTIFY +XCB_CLIENT_MESSAGE + +*/ + +//SYSTEM TRAY STANDARD DEFINITIONS +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +class XCBEventFilter : public QAbstractNativeEventFilter{ +private: + LSession *session; + xcb_atom_t _NET_SYSTEM_TRAY_OPCODE; + QList WinNotifyAtoms, SysNotifyAtoms; + int TrayDmgFlag; //internal damage event offset value for the system tray + bool stopping; + + void InitAtoms(){ + //Initialize any special atoms that we need to save/use regularly + //NOTE: All the EWMH atoms are already saved in session->XCB->EWMH + WinNotifyAtoms.clear(); + WinNotifyAtoms << session->XCB->EWMH._NET_WM_NAME \ + << session->XCB->EWMH._NET_WM_VISIBLE_NAME \ + << session->XCB->EWMH._NET_WM_ICON_NAME \ + << session->XCB->EWMH._NET_WM_VISIBLE_ICON_NAME \ + << session->XCB->EWMH._NET_WM_ICON \ + << session->XCB->EWMH._NET_WM_ICON_GEOMETRY; + + SysNotifyAtoms.clear(); + SysNotifyAtoms << session->XCB->EWMH._NET_CLIENT_LIST \ + << session->XCB->EWMH._NET_CLIENT_LIST_STACKING \ + << session->XCB->EWMH._NET_CURRENT_DESKTOP \ + << session->XCB->EWMH._NET_WM_STATE \ + << session->XCB->EWMH._NET_ACTIVE_WINDOW \ + << session->XCB->EWMH._NET_WM_ICON \ + << session->XCB->EWMH._NET_WM_ICON_GEOMETRY; + //_NET_SYSTEM_TRAY_OPCODE + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(QX11Info::connection(), 0, 23,"_NET_SYSTEM_TRAY_OPCODE"); + xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(QX11Info::connection(), cookie, NULL); + if(r){ + _NET_SYSTEM_TRAY_OPCODE = r->atom; + free(r); + } + } + +public: + XCBEventFilter(LSession *sessionhandle); + void setTrayDamageFlag(int flag); + void StopEventHandling(){ stopping = true; } + + //This function format taken directly from the Qt5.3 documentation + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE; + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.cpp new file mode 100644 index 00000000..58208931 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.cpp @@ -0,0 +1,67 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "SettingsMenu.h" +#include "LSession.h" + +#include + +SettingsMenu::SettingsMenu() : QMenu(){ + this->setObjectName("SettingsMenu"); + connect(this, SIGNAL(triggered(QAction*)), this, SLOT(runApp(QAction*)) ); + connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(UpdateMenu()) ); + connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(UpdateMenu()) ); + QTimer::singleShot(100, this, SLOT(UpdateMenu()) ); +} + +SettingsMenu::~SettingsMenu(){ + +} + +void SettingsMenu::UpdateMenu(){ + //Change the title/icon to account for locale/icon changes + this->setTitle( tr("Preferences") ); + this->setIcon( LXDG::findIcon("configure","") ); + this->clear(); + //Now setup the possible configuration options + QAction *act = new QAction(LXDG::findIcon("preferences-desktop-screensaver",""), tr("Screensaver"), this); + act->setWhatsThis("xscreensaver-demo"); + this->addAction(act); + act = new QAction( LXDG::findIcon("preferences-desktop-wallpaper",""), tr("Wallpaper"), this); + act->setWhatsThis("lumina-config --page wallpaper"); + this->addAction(act); + act = new QAction( LXDG::findIcon("preferences-other",""), tr("Display"), this); + act->setWhatsThis("lumina-xconfig"); + this->addAction(act); + act = new QAction( LXDG::findIcon("preferences-desktop",""), tr("All Desktop Settings"), this); + act->setWhatsThis("lumina-config"); + this->addAction(act); + this->addSeparator(); + /*QString qtconfig = LOS::QtConfigShortcut(); + if(QFile::exists(qtconfig) && !qtconfig.isEmpty()){ + act = new QAction( LXDG::findIcon("preferences-desktop-theme",""), tr("Window Theme"), this); + act->setWhatsThis(qtconfig); + this->addAction(act); + }*/ + QString CONTROLPANEL = LOS::ControlPanelShortcut(); + if(QFile::exists(CONTROLPANEL) && !CONTROLPANEL.isEmpty()){ + //Now load the info + XDGDesktop cpan(CONTROLPANEL); + if(cpan.isValid()){ + act = new QAction( LXDG::findIcon(cpan.icon,""), tr("Control Panel"), this); + act->setWhatsThis("lumina-open \""+CONTROLPANEL+"\""); + this->addAction(act); + } + } + act = new QAction( LXDG::findIcon("lumina",""), tr("About Lumina"), this); + act->setWhatsThis("lumina-info"); + this->addAction(act); +} + + +void SettingsMenu::runApp(QAction* act){ + LSession::LaunchApplication(act->whatsThis()); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.h b/src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.h new file mode 100644 index 00000000..eeabab85 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/SettingsMenu.h @@ -0,0 +1,28 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SETTINGS_MENU_H +#define _LUMINA_DESKTOP_SETTINGS_MENU_H + +#include +#include +#include + +#include + +class SettingsMenu : public QMenu{ + Q_OBJECT +public: + SettingsMenu(); + ~SettingsMenu(); + +private slots: + void UpdateMenu(); + void runApp(QAction* act); + +}; + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.cpp new file mode 100644 index 00000000..1c0b59a5 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.cpp @@ -0,0 +1,104 @@ +#include "SystemWindow.h" +#include "ui_SystemWindow.h" + +#include "LSession.h" +#include +#include +#include +#include +#include +#include +#include + +SystemWindow::SystemWindow() : QDialog(), ui(new Ui::SystemWindow){ + ui->setupUi(this); //load the designer file + //Setup the window flags + this->setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + //Setup the icons based on the current theme + ui->tool_logout->setIcon( LXDG::findIcon("system-log-out","") ); + ui->tool_restart->setIcon( LXDG::findIcon("system-reboot","") ); + ui->tool_shutdown->setIcon( LXDG::findIcon("system-shutdown","") ); + ui->tool_suspend->setIcon( LXDG::findIcon("system-suspend","") ); + ui->push_cancel->setIcon( LXDG::findIcon("dialog-cancel","") ); + ui->push_lock->setIcon( LXDG::findIcon("system-lock-screen","") ); + //Connect the signals/slots + connect(ui->tool_logout, SIGNAL(clicked()), this, SLOT(sysLogout()) ); + connect(ui->tool_restart, SIGNAL(clicked()), this, SLOT(sysRestart()) ); + connect(ui->tool_shutdown, SIGNAL(clicked()), this, SLOT(sysShutdown()) ); + connect(ui->tool_suspend, SIGNAL(clicked()), this, SLOT(sysSuspend()) ); + connect(ui->push_cancel, SIGNAL(clicked()), this, SLOT(sysCancel()) ); + connect(ui->push_lock, SIGNAL(clicked()), this, SLOT(sysLock()) ); + //Disable buttons if necessary + updateWindow(); + ui->tool_suspend->setVisible(LOS::systemCanSuspend()); //does not change with time - just do a single check + connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(updateWindow()) ); + connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(updateWindow()) ); +} + +SystemWindow::~SystemWindow(){ + +} + +void SystemWindow::updateWindow(){ + //Disable the shutdown/restart buttons if necessary + ui->retranslateUi(this); + bool ok = LOS::userHasShutdownAccess(); + ui->tool_restart->setEnabled(ok); + ui->tool_shutdown->setEnabled(ok); + //Center this window on the current screen + QPoint center = QApplication::desktop()->screenGeometry(QCursor::pos()).center(); //get the center of the current screen + this->move(center.x() - this->width()/2, center.y() - this->height()/2); +} + +bool SystemWindow::promptAboutUpdates(bool &skip){ + QString pending = LOS::systemPendingUpdates(); + if(pending.isEmpty()){ skip = false; } //continue without skip + else{ + QMessageBox dlg(QMessageBox::Question, tr("Apply Updates?"), tr("You have system updates waiting to be applied! Do you wish to install them now?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, this); + dlg.setDetailedText(pending); + dlg.setDefaultButton(QMessageBox::Yes); + dlg.show(); + int ret = dlg.exec(); + if(ret == QMessageBox::Cancel){ return false; } //do not continue + else{ skip = (ret==QMessageBox::No); } + } + return true; +} + +void SystemWindow::sysLogout(){ + this->close(); + LSession::processEvents(); + QTimer::singleShot(0, LSession::handle(), SLOT(StartLogout()) ); +} + +void SystemWindow::sysRestart(){ + bool skip = false; + if(!promptAboutUpdates(skip)){ this->close(); return; } //cancelled + this->close(); + LSession::processEvents(); + LSession::handle()->StartReboot(skip); +} + +void SystemWindow::sysShutdown(){ + bool skip = false; + if(!promptAboutUpdates(skip)){ this->close(); return; } //cancelled + this->close(); + LSession::processEvents(); + LSession::handle()->StartShutdown(skip); +} + +void SystemWindow::sysSuspend(){ + this->hide(); + LSession::processEvents(); + //Make sure to lock the system first (otherwise anybody can access it again) + LUtils::runCmd("xscreensaver-command -lock"); + //Now suspend the system + LOS::systemSuspend(); +} + +void SystemWindow::sysLock(){ + this->hide(); + LSession::processEvents(); + qDebug() << "Locking the desktop..."; + QProcess::startDetached("xscreensaver-command -lock"); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.h b/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.h new file mode 100644 index 00000000..bbef36a3 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.h @@ -0,0 +1,46 @@ +#ifndef _LUMINA_DESKTOP_SYSTEM_WINDOW_H +#define _LUMINA_DESKTOP_SYSTEM_WINDOW_H + +#include + +#include "ui_SystemWindow.h" + + + + +namespace Ui{ + class SystemWindow; +}; + +class SystemWindow : public QDialog{ + Q_OBJECT +public: + SystemWindow(); + ~SystemWindow(); + +public slots: + void updateWindow(); + +private: + Ui::SystemWindow *ui; + + //void closeAllWindows(); + bool promptAboutUpdates(bool &skip); //main bool return: continue/cancel, skip: skip updates or not + +private slots: + void sysLogout(); + + void sysRestart(); + + void sysShutdown(); + + void sysSuspend(); + + void sysCancel(){ + this->close(); + } + + void sysLock(); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.ui b/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.ui new file mode 100644 index 00000000..9e25509b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/SystemWindow.ui @@ -0,0 +1,194 @@ + + + SystemWindow + + + + 0 + 0 + 289 + 135 + + + + System Options + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 3 + + + 3 + + + 0 + + + + + + + Log Out + + + + 64 + 64 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + Restart + + + + 64 + 64 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + Shutdown + + + + 64 + 64 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + + + + + + Cancel + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Lock + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + Suspend + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.cpp new file mode 100644 index 00000000..545ba430 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.cpp @@ -0,0 +1,63 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LDPlugin.h" + +#include "../LSession.h" +#include + +LDPlugin::LDPlugin(QWidget *parent, QString id) : QFrame(parent){ + PLUGID=id; + prefix = id.replace("/","_")+"/"; + //qDebug() << "ID:" << PLUGID << prefix; + settings = LSession::handle()->DesktopPluginSettings(); + //Setup the plugin system control menu + menu = new QMenu(this); + setupMenu(); + //Setup the internal timer for when to start/stop drag events + dragTimer = new QTimer(this); + dragTimer->setSingleShot(true); + dragTimer->setInterval(500); //1/2 second to show the plugin menu + connect(dragTimer, SIGNAL(timeout()), this, SLOT(showPluginMenu())); + //Use plugin-specific values for stylesheet control (applauncher, desktopview, etc...) + this->setObjectName(id.section("---",0,0).section("::",0,0)); + this->setContextMenuPolicy(Qt::CustomContextMenu); + this->setMouseTracking(false); //only catch mouse movement events if the mouse is clicked/held on the plugin + connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(LocaleChange()) ); + connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(ThemeChange()) ); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPluginMenu()) ); +} + +void LDPlugin::setupMenu(){ + menu->clear(); + //SPECIAL CONTEXT MENU OPTIONS FOR PARTICULAR PLUGIN TYPES + if(PLUGID.startsWith("applauncher::")){ + menu->addAction( LXDG::findIcon("quickopen",""), tr("Launch Item"), this, SIGNAL(PluginActivated()) ); + menu->addSeparator(); + } + //General Options + menu->addAction( LXDG::findIcon("transform-move",""), tr("Start Moving Item"), this, SLOT(slotStartMove()) ); + menu->addAction( LXDG::findIcon("transform-scale",""), tr("Start Resizing Item"), this, SLOT(slotStartResize()) ); + menu->addSeparator(); + menu->addAction( LXDG::findIcon("zoom-in",""), tr("Increase Item Sizes"), this, SIGNAL(IncreaseIconSize()) ); + menu->addAction( LXDG::findIcon("zoom-out",""), tr("Decrease Item Sizes"), this, SIGNAL(DecreaseIconSize()) ); + menu->addSeparator(); + menu->addAction( LXDG::findIcon("edit-delete",""), tr("Remove Item"), this, SLOT(slotRemovePlugin()) ); +} + +/*void LDPlugin::setInitialSize(int width, int height){ + //Note: Only run this in the plugin initization routine: + // if the plugin is completely new (first time used), it will be this size + if(settings->allKeys().filter(prefix+"location").isEmpty()){ + //Brand new plugin: set initial size + //qDebug() << "Setting Initial Size:" << PLUGID << width << height; + settings->setValue(prefix+"location/width",width); + settings->setValue(prefix+"location/height",height); + settings->sync(); + } + //Now make sure the plugin is the saved size right away + this->resize( settings->value(prefix+"location/width").toInt(), settings->value(prefix+"location/height").toInt()); +}*/ diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.h new file mode 100644 index 00000000..820880ed --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/LDPlugin.h @@ -0,0 +1,156 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the generic container layout for all desktop plugins +// Simply subclass this when creating a new plugin to enable correct +// visibility and usage within the desktop window +//=========================================== +// WARNING: Do *not* setup a custom context menu for the entire plugins area! +// This can prevent access to the general desktop context menu if +// the plugin was maximized to fill the desktop area! +//=========================================== +#ifndef _LUMINA_DESKTOP_DESKTOP_PLUGIN_H +#define _LUMINA_DESKTOP_DESKTOP_PLUGIN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class LDPlugin : public QFrame{ + Q_OBJECT + +private: + QString PLUGID, prefix; + QSettings *settings; + QMenu *menu; + QTimer *dragTimer; + + void setupMenu(); + +public: + LDPlugin(QWidget *parent = 0, QString id="unknown"); + + ~LDPlugin(){} + + QString ID(){ + return PLUGID; + } + + virtual QSize defaultPluginSize(){ + //This needs to be re-implemented in the subclassed plugin + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(1,1); //1x1 grid size + } + + void savePluginGeometry(QRect geom){ + settings->setValue(prefix+"geometry/desktopGridPoints", geom); + settings->sync(); + } + + QRect loadPluginGeometry(){ + return settings->value(prefix+"geometry/desktopGridPoints", QRect()).toRect(); + } + + void saveSetting(QString var, QVariant val){ + //qDebug() << "Saving Setting:" << prefix+var+QString(" = ")+val.toString(); + settings->setValue(prefix+var, val); + settings->sync(); + } + + QVariant readSetting(QString var, QVariant defaultval){ + return settings->value(prefix+var, defaultval); + } + + virtual void Cleanup(){ + //This needs to be re-implemented in the subclassed plugin + //This is where any last-minute changes are performed before a plugin is removed permanently + //Note1: This is *not* called if the plugin is being temporarily closed + //Note2: All the settings for this plugin will be automatically removed after this is finished + } + + void removeSettings(bool permanent = false){ //such as when a plugin is deleted + if(permanent){ Cleanup(); } + QStringList list = settings->allKeys().filter(prefix); + for(int i=0; iremove(list[i]); } + + } + +public slots: + virtual void LocaleChange(){ + //This needs to be re-implemented in the subclassed plugin + //This is where all text is set/translated + setupMenu(); + } + virtual void ThemeChange(){ + //This needs to be re-implemented in the subclassed plugin + //This is where all the visuals are set if using Theme-dependant icons. + setupMenu(); + } + void showPluginMenu(){ + emit CloseDesktopMenu(); + menu->popup( QCursor::pos() ); + } + +signals: + void OpenDesktopMenu(); + void CloseDesktopMenu(); + void PluginResized(); + void PluginActivated(); + + //Signals for communication with the desktop layout system (not generally used by hand) + void StartMoving(QString); //ID of plugin + void StartResizing(QString); //ID of plugin + void RemovePlugin(QString); //ID of plugin + void IncreaseIconSize(); // only used for desktop icons + void DecreaseIconSize(); // only used for desktop icons + +private slots: + void slotStartMove(){ + QCursor::setPos( this->mapToGlobal(QPoint(this->width()/2, this->height()/2)) ); + emit StartMoving(PLUGID); + } + + void slotStartResize(){ + QCursor::setPos( this->mapToGlobal(QPoint(this->width()/2, this->height()/2)) ); + emit StartResizing(PLUGID); + } + + void slotRemovePlugin(){ + removeSettings(true); + emit RemovePlugin(PLUGID); + } + +protected: + void mousePressEvent(QMouseEvent *ev){ + if(!dragTimer->isActive() && ev->buttons().testFlag(Qt::LeftButton) ){ dragTimer->start(); } + QWidget::mousePressEvent(ev); + } + void mouseReleaseEvent(QMouseEvent *ev){ + if(dragTimer->isActive()){ dragTimer->stop(); } + QWidget::mouseReleaseEvent(ev); + } + void mouseMoveEvent(QMouseEvent *ev){ + if(ev->buttons().testFlag(Qt::LeftButton)){ + if(dragTimer->isActive()){ dragTimer->stop(); } + slotStartMove(); + } + QWidget::mouseMoveEvent(ev); + } + void resizeEvent(QResizeEvent *ev){ + emit PluginResized(); + QFrame::resizeEvent(ev); //do normal processing + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/NewDP.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/NewDP.h new file mode 100644 index 00000000..e28b8c61 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/NewDP.h @@ -0,0 +1,63 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the interface to load all the different desktop plugins +//=========================================== +#ifndef _LUMINA_DESKTOP_NEW_DESKTOP_PLUGIN_H +#define _LUMINA_DESKTOP_NEW_DESKTOP_PLUGIN_H + +#include + +//List all the individual plugin includes here +#include "LDPlugin.h" +//#include "SamplePlugin.h" +#include "calendar/CalendarPlugin.h" +#include "applauncher/AppLauncherPlugin.h" +#include "desktopview/DesktopViewPlugin.h" +#include "notepad/NotepadPlugin.h" +#include "audioplayer/PlayerWidget.h" +#include "systemmonitor/MonitorWidget.h" +//#include "quickcontainer/QuickDPlugin.h" +//#include "messagecenter/MessageCenter.h" +#include "rssreader/RSSFeedPlugin.h" + +class NewDP{ +public: + static LDPlugin* createPlugin(QString plugin, QWidget* parent=0){ + //qDebug() << "Create Plugin:" << plugin; + LDPlugin *plug = 0; + /*if(plugin.section("---",0,0)=="sample"){ + plug = new SamplePlugin(parent, plugin); + }else */ + if(plugin.section("---",0,0)=="calendar"){ + plug = new CalendarPlugin(parent, plugin); + }else if(plugin.section("---",0,0).section("::",0,0)=="applauncher"){ + //This plugin can be pre-initialized to a file path after the "::" delimiter + plug = new AppLauncherPlugin(parent, plugin); + }else if(plugin.section("---",0,0)=="desktopview"){ + plug = new DesktopViewPlugin(parent, plugin); + }else if(plugin.section("---",0,0)=="notepad"){ + plug = new NotePadPlugin(parent, plugin); + }else if(plugin.section("---",0,0)=="audioplayer"){ + plug = new AudioPlayerPlugin(parent, plugin); + }else if(plugin.section("---",0,0)=="systemmonitor"){ + plug = new SysMonitorPlugin(parent, plugin); + //}else if(plugin.section("---",0,0)=="messagecenter"){ + //plug = new MessageCenterPlugin(parent, plugin); + //}else if(plugin.section("---",0,0).startsWith("quick-") && LUtils::validQuickPlugin(plugin.section("---",0,0)) ){ + //plug = new QuickDPlugin(parent, plugin); + }else if(plugin.section("---",0,0)=="rssreader"){ + plug = new RSSFeedPlugin(parent, plugin); + }else{ + qWarning() << "Invalid Desktop Plugin:"< +#include +#include +#include "LDPlugin.h" + +class SamplePlugin : public LDPlugin{ + Q_OBJECT +public: + SamplePlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + this->setLayout( new QVBoxLayout()); + this->layout()->setContentsMargins(0,0,0,0); + button = new QPushButton("sample"); + this->layout()->addWidget(button); + connect(button, SIGNAL(clicked()), this, SLOT(showMessage()) ); + } + + ~SamplePlugin(){} + +private: + QPushButton *button; + +private slots: + void showMessage(){ + QMessageBox::information(this,"sample","sample desktop plugin works"); + } +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.cpp new file mode 100644 index 00000000..3be19faa --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.cpp @@ -0,0 +1,145 @@ +#include "AppLauncherPlugin.h" +#include "../../LSession.h" +#include "OutlineToolButton.h" + +#define OUTMARGIN 10 //special margin for fonts due to the outlining effect from the OutlineToolbutton + +AppLauncherPlugin::AppLauncherPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + QVBoxLayout *lay = new QVBoxLayout(); + this->setLayout(lay); + lay->setContentsMargins(0,0,0,0); + button = new OutlineToolButton(this); + button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + button->setAutoRaise(true); + button->setText("...\n..."); //Need to set something here so that initial sizing works properly + button->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + lay->addWidget(button, 0, Qt::AlignCenter); + connect(button, SIGNAL(DoubleClicked()), this, SLOT(buttonClicked()) ); + button->setContextMenuPolicy(Qt::NoContextMenu); + watcher = new QFileSystemWatcher(this); + connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT( loadButton()) ); + + connect(this, SIGNAL(PluginActivated()), this, SLOT(buttonClicked()) ); //in case they use the context menu to launch it. + loadButton(); + //QTimer::singleShot(0,this, SLOT(loadButton()) ); +} + +void AppLauncherPlugin::Cleanup(){ + //This is run only when the plugin was forcibly closed/removed + +} + +void AppLauncherPlugin::loadButton(){ + QString def = this->ID().section("::",1,50).section("---",0,0).simplified(); + QString path = this->readSetting("applicationpath",def).toString(); //use the default if necessary + //qDebug() << "Default Application Launcher:" << def << path; + bool ok = QFile::exists(path); + if(!ok){ emit RemovePlugin(this->ID()); return;} + int icosize = this->height()-4 - 2.2*button->fontMetrics().height(); + button->setFixedSize( this->width()-4, this->height()-4); + button->setIconSize( QSize(icosize,icosize) ); + QString txt; + if(path.endsWith(".desktop") && ok){ + XDGDesktop file(path); + ok = !file.name.isEmpty(); + if(!ok){ + button->setWhatsThis(""); + button->setIcon( QIcon(LXDG::findIcon("quickopen-file","").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) ); + txt = tr("Click to Set"); + if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); } + }else{ + button->setWhatsThis(file.filePath); + button->setIcon( QIcon(LXDG::findIcon(file.icon,"system-run").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) ); + txt = file.name; + if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); } + watcher->addPath(file.filePath); //make sure to update this shortcut if the file changes + } + }else if(ok){ + QFileInfo info(path); + button->setWhatsThis(info.absoluteFilePath()); + if(info.isDir()){ + button->setIcon( LXDG::findIcon("folder","") ); + }else if(LUtils::imageExtensions().contains(info.suffix().toLower()) ){ + QPixmap pix; + if(pix.load(path)){ button->setIcon( QIcon(pix.scaled(256,256)) ); } //max size for thumbnails in memory + else{ button->setIcon( LXDG::findIcon("dialog-cancel","") ); } + }else{ + button->setIcon( QIcon(LXDG::findMimeIcon(path).pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) ); + } + txt = info.fileName(); + if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); } + watcher->addPath(path); //make sure to update this shortcut if the file changes + }else{ + //InValid File + button->setWhatsThis(""); + button->setIcon( QIcon(LXDG::findIcon("quickopen","dialog-cancel").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) ); + button->setText( tr("Click to Set") ); + if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); } + } + //If the file is a symlink, put the overlay on the icon + if(QFileInfo(path).isSymLink()){ + QImage img = button->icon().pixmap(QSize(icosize,icosize)).toImage(); + int oSize = icosize/3; //overlay size + QPixmap overlay = LXDG::findIcon("emblem-symbolic-link").pixmap(oSize,oSize).scaled(oSize,oSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPainter painter(&img); + painter.drawPixmap(icosize-oSize,icosize-oSize,overlay); //put it in the bottom-right corner + button->setIcon( QIcon(QPixmap::fromImage(img)) ); + } + //Now adjust the visible text as necessary based on font/grid sizing + button->setToolTip(txt); + //Double check that the visual icon size matches the requested size - otherwise upscale the icon + if(button->fontMetrics().width(txt) > (button->width()-OUTMARGIN) ){ + //Text too long, try to show it on two lines + //txt = button->fontMetrics().elidedText(txt, Qt::ElideRight, 2*(button->width()-OUTMARGIN), Qt::TextWordWrap); + txt =txt.section(" ",0,2).replace(" ","\n"); //First take care of any natural breaks + //Go through and combine any lines + if(txt.contains("\n")){ + //need to check each line + QStringList txtL = txt.split("\n"); + for(int i=0; ifontMetrics().width(txtL[i]) < button->width()/2) ){ + txtL[i] = txtL[i]+" "+txtL[i+1]; + txtL.removeAt(i+1); + } + } + txt = txtL.join("\n").section("\n",0,2); + } + + if(txt.contains("\n")){ + //need to check each line + QStringList txtL = txt.split("\n"); + for(int i=0; i1){ txtL.removeAt(i); i--; } //Only take the first two lines + else{ txtL[i] = button->fontMetrics().elidedText(txtL[i], Qt::ElideRight, (button->width()-OUTMARGIN) ); } + } + txt = txtL.join("\n"); + }else{ + txt = this->fontMetrics().elidedText(txt,Qt::ElideRight, 2*(button->width()-OUTMARGIN)); + //Now split the line in half for the two lines + txt.insert( ((txt.count())/2), "\n"); + } + } + if(!txt.contains("\n")){ txt.append("\n "); } //always use two lines + //qDebug() << " - Setting Button Text:" << txt; + button->setText(txt); + + QTimer::singleShot(100, this, SLOT(update()) ); //Make sure to re-draw the image in a moment +} + +void AppLauncherPlugin::buttonClicked(){ + QString path = button->whatsThis(); + if(path.isEmpty() || !QFile::exists(path) ){ + //prompt for the user to select an application + QList apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); //LXDG::sortDesktopNames( LXDG::systemDesktopFiles() ); + QStringList names; + for(int i=0; iname; } + bool ok = false; + QString app = QInputDialog::getItem(this, tr("Select Application"), tr("Name:"), names, 0, false, &ok); + if(!ok || names.indexOf(app)<0){ return; } //cancelled + this->saveSetting("applicationpath", apps[ names.indexOf(app) ]->filePath); + QTimer::singleShot(0,this, SLOT(loadButton())); + }else{ + LSession::LaunchApplication("lumina-open \""+path+"\""); + } + +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.h new file mode 100644 index 00000000..a0f6a7cd --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/AppLauncherPlugin.h @@ -0,0 +1,59 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is a quick sample desktop plugin +//=========================================== +#ifndef _LUMINA_DESKTOP_DESKTOP_PLUGIN_APPLICATION_LAUNCHER_H +#define _LUMINA_DESKTOP_DESKTOP_PLUGIN_APPLICATION_LAUNCHER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../LDPlugin.h" + +#include + +class AppLauncherPlugin : public LDPlugin{ + Q_OBJECT +public: + AppLauncherPlugin(QWidget* parent, QString ID); + ~AppLauncherPlugin(){} + + void Cleanup(); //special function for final cleanup + +private: + QToolButton *button; + QFileSystemWatcher *watcher; + //QMenu *menu; + +private slots: + void loadButton(); + void buttonClicked(); + //void openContextMenu(); + + //void increaseIconSize(); + //void decreaseIconSize(); + //void deleteFile(); + +public slots: + void LocaleChange(){ + loadButton(); //force reload + } + +protected: + void resizeEvent(QResizeEvent *ev){ + LDPlugin::resizeEvent(ev); + QTimer::singleShot(100, this, SLOT(loadButton()) ); + } +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/OutlineToolButton.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/OutlineToolButton.h new file mode 100644 index 00000000..24410e75 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/applauncher/OutlineToolButton.h @@ -0,0 +1,99 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This is a simple subclass for a QToolButton with black/white text (for transparent backgrounds) +//=========================================== +#ifndef _LUMINA_DESKTOP_PLUGIN_APPLAUNCHER_OUTLINE_TOOLBUTTON_H +#define _LUMINA_DESKTOP_PLUGIN_APPLAUNCHER_OUTLINE_TOOLBUTTON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class OutlineToolButton : public QToolButton{ + Q_OBJECT +public: + OutlineToolButton(QWidget *parent=0) : QToolButton(parent){ + //This button needs slightly different font settings - do this in the constructor so that other widgets can take it into account. + QFont font = this->font(); + font.setStyleStrategy(QFont::PreferAntialias); //Always set the font strategy (just in case it starts working down the road) + this->setFont(font); + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + } + ~OutlineToolButton(){} + +signals: + void DoubleClicked(); + +protected: + void mouseDoubleClickEvent(QMouseEvent *ev){ + ev->accept(); + emit DoubleClicked(); + } + void mousePressEvent(QMouseEvent *ev){ + ev->ignore(); + } + void mouseReleaseEvent(QMouseEvent *ev){ + ev->ignore(); + } + + void paintEvent(QPaintEvent*){ + /* NOTE: This is what a standard QToolButton performs (peeked at Qt source code for this tidbit) + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + p.drawComplexControl(QStyle::CC_ToolButton, opt); + */ + + //Modify the standard QToolButton routine to paint the text differently + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + opt.font = this->property("font").value(); //This ensures that the stylesheet values are incorporated + opt.font.setStyleStrategy(QFont::PreferAntialias); //Always set the font strategy (just in case it starts working down the road) + opt.font.setKerning(true); + opt.fontMetrics = QFontMetrics(opt.font); + opt.text.clear(); //Don't paint the text yet - just the background/icon + p.drawComplexControl(QStyle::CC_ToolButton, opt); //This does all the normal QToolButton stuff - just not text + //Now get the text rectangle for the widget + QRect box = p.style()->itemTextRect(opt.fontMetrics, opt.rect, Qt::AlignHCenter | Qt::AlignBottom, true, this->text()); + //Get the QColors for the outline/text + QColor textC = opt.palette.text().color().toHsl(); //need the lightness value in a moment + QColor outC = textC; + //qDebug() << "Font Color Values:" << textC << textC.lightness() << textC.lightnessF(); + if(textC.lightnessF() > 0.5){ outC.setHsl(textC.hue(), textC.hslSaturation(), 0, 90); } + else{outC.setHsl(textC.hue(), textC.hslSaturation(), 255, 50); } + //qDebug() << "Outline Color Values:" << outC; + //Now get the size of the outline border (need to scale for high-res monitors) + qreal OWidth = opt.fontMetrics.width("o")/2.0; + //qDebug() << "Outline Width:" << OWidth; + //Now generate a QPainterPath for the text + QPainterPath path; + QStringList txt = this->text().split("\n"); //need each line independently, the newline actually gets painted otherwise + for(int i=0; i +#include +#include +#include +#include +#include +#include + +PlayerWidget::PlayerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::PlayerWidget()){ + ui->setupUi(this); //load the designer form + PLAYER = new QMediaPlayer(this); + PLAYER->setVolume(100); + PLAYER->setNotifyInterval(1000); //1 second interval (just needs to be a rough estimate) + PLAYLIST = new QMediaPlaylist(this); + PLAYLIST->setPlaybackMode(QMediaPlaylist::Sequential); + PLAYER->setPlaylist(PLAYLIST); + + configMenu = new QMenu(this); + ui->tool_config->setMenu(configMenu); + addMenu = new QMenu(this); + ui->tool_add->setMenu(addMenu); + + updatinglists = false; //start off as false + + ui->combo_playlist->setContextMenuPolicy(Qt::NoContextMenu); + + LoadIcons(); + playerStateChanged(); //update button visibility + currentSongChanged(); + //Connect all the signals/slots + //connect(infoTimer, SIGNAL(timeout()), this, SLOT(rotateTrackInfo()) ); + connect(PLAYER, SIGNAL(positionChanged(qint64)),this, SLOT(updateProgress(qint64)) ); + connect(PLAYER, SIGNAL(durationChanged(qint64)), this, SLOT(updateMaxProgress(qint64)) ); + connect(PLAYLIST, SIGNAL(mediaChanged(int, int)), this, SLOT(playlistChanged()) ); + connect(PLAYER, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(playerStateChanged()) ); + connect(PLAYLIST, SIGNAL(currentMediaChanged(const QMediaContent&)), this, SLOT(currentSongChanged()) ); + connect(ui->combo_playlist, SIGNAL(currentIndexChanged(int)), this, SLOT(userlistSelectionChanged()) ); + connect(ui->tool_play, SIGNAL(clicked()), this, SLOT(playClicked()) ); + connect(ui->tool_pause, SIGNAL(clicked()), this, SLOT(pauseClicked()) ); + connect(ui->tool_stop, SIGNAL(clicked()), this, SLOT(stopClicked()) ); + connect(ui->tool_next, SIGNAL(clicked()), this, SLOT(nextClicked()) ); + connect(ui->tool_prev, SIGNAL(clicked()), this, SLOT(prevClicked()) ); + +} + +PlayerWidget::~PlayerWidget(){ + //qDebug() << "Removing PlayerWidget"; +} + +void PlayerWidget::LoadIcons(){ + ui->tool_stop->setIcon( LXDG::findIcon("media-playback-stop","") ); + ui->tool_play->setIcon( LXDG::findIcon("media-playback-start","") ); + ui->tool_pause->setIcon( LXDG::findIcon("media-playback-pause","") ); + ui->tool_next->setIcon( LXDG::findIcon("media-skip-forward","") ); + ui->tool_prev->setIcon( LXDG::findIcon("media-skip-backward","") ); + ui->tool_add->setIcon( LXDG::findIcon("list-add","") ); + ui->tool_config->setIcon( LXDG::findIcon("configure","") ); + //Now re-assemble the menus as well + configMenu->clear(); + configMenu->addAction(LXDG::findIcon("media-eject",""), tr("Clear Playlist"), this, SLOT(ClearPlaylist())); + configMenu->addAction(LXDG::findIcon("roll",""), tr("Shuffle Playlist"), this, SLOT(ShufflePlaylist())); + addMenu->clear(); + addMenu->addAction(LXDG::findIcon("document-new",""), tr("Add Files"), this, SLOT(AddFilesToPlaylist())); + addMenu->addAction(LXDG::findIcon("folder-new",""), tr("Add Directory"), this, SLOT(AddDirToPlaylist())); + addMenu->addAction(LXDG::findIcon("download",""), tr("Add URL"), this, SLOT(AddURLToPlaylist())); +} + +void PlayerWidget::playClicked(){ + PLAYER->play(); +} + +void PlayerWidget::pauseClicked(){ + PLAYER->pause(); +} + +void PlayerWidget::stopClicked(){ + PLAYER->stop(); +} + +void PlayerWidget::nextClicked(){ + PLAYLIST->next(); +} + +void PlayerWidget::prevClicked(){ + PLAYLIST->previous(); +} + +void PlayerWidget::AddFilesToPlaylist(){ + //Prompt the user to select multimedia files + QFileDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setFileMode(QFileDialog::ExistingFiles); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setNameFilter( tr("Multimedia Files")+" ("+LXDG::findAVFileExtensions().join(" ")+")"); + dlg.setWindowTitle(tr("Select Multimedia Files")); + dlg.setWindowIcon( LXDG::findIcon("file-open","") ); + dlg.setDirectory(QDir::homePath()); //start in the home directory + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + QList files = dlg.selectedUrls(); + if(files.isEmpty() || dlg.result()!=QDialog::Accepted){ return; } //cancelled + //Make this use show/processEvents later + //QList files = QFileDialog::getOpenFileUrls(0, tr("Select Multimedia Files"), QDir::homePath(), "Multimedia Files ("+LXDG::findAVFileExtensions().join(" ")+")"); + QList urls; + for(int i=0; iaddMedia(urls); + playlistChanged(); +} + +void PlayerWidget::AddDirToPlaylist(){ + QFileDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setFileMode(QFileDialog::Directory); + dlg.setOption(QFileDialog::ShowDirsOnly, true); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setWindowTitle(tr("Select Multimedia Directory")); + dlg.setWindowIcon( LXDG::findIcon("folder-open","") ); + dlg.setDirectory(QDir::homePath()); //start in the home directory + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + if(dlg.result() != QDialog::Accepted){ return; } //cancelled + QStringList sel = dlg.selectedFiles(); + if(sel.isEmpty()){ return; } //cancelled + QString dirpath = sel.first(); //QFileDialog::getExistingDirectory(0, tr("Select a Multimedia Directory"), QDir::homePath() ); + if(dirpath.isEmpty()){ return; } //cancelled + QDir dir(dirpath); + QFileInfoList files = dir.entryInfoList(LXDG::findAVFileExtensions(), QDir::Files | QDir::NoDotAndDotDot, QDir::Name); + if(files.isEmpty()){ return; } //nothing in this directory + QList urls; + for(int i=0; iaddMedia(urls); + playlistChanged(); +} + +void PlayerWidget::AddURLToPlaylist(){ + QInputDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setInputMode(QInputDialog::TextInput); + dlg.setLabelText(tr("Enter a valid URL for a multimedia file or stream:")); + dlg.setTextEchoMode(QLineEdit::Normal); + dlg.setWindowTitle(tr("Multimedia URL")); + dlg.setWindowIcon( LXDG::findIcon("download","") ); + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + QString url = dlg.textValue(); + if(url.isEmpty() || dlg.result()!=QDialog::Accepted){ return; } //cancelled + + //QString url = QInputDialog::getText(0, tr("Multimedia URL"), tr("Enter a valid URL for a multimedia file or stream"), QLineEdit::Normal); + //if(url.isEmpty()){ return; } + QUrl newurl(url); + if(!newurl.isValid()){ return; } //invalid URL + PLAYLIST->addMedia(newurl); + playlistChanged(); +} + +void PlayerWidget::ClearPlaylist(){ + PLAYER->stop(); + PLAYLIST->clear(); + playlistChanged(); +} + +void PlayerWidget::ShufflePlaylist(){ + PLAYLIST->shuffle(); +} + + +void PlayerWidget::userlistSelectionChanged(){ //front-end combobox was changed by the user + if(updatinglists){ return; } + PLAYLIST->setCurrentIndex( ui->combo_playlist->currentIndex() ); +} + +void PlayerWidget::playerStateChanged(){ + switch( PLAYER->state() ){ + case QMediaPlayer::StoppedState: + ui->tool_stop->setVisible(false); + ui->tool_play->setVisible(true); + ui->tool_pause->setVisible(false); + ui->progressBar->setVisible(false); + break; + case QMediaPlayer::PausedState: + ui->tool_stop->setVisible(true); + ui->tool_play->setVisible(true); + ui->tool_pause->setVisible(false); + ui->progressBar->setVisible(true); + break; + case QMediaPlayer::PlayingState: + ui->tool_stop->setVisible(true); + ui->tool_play->setVisible(false); + ui->tool_pause->setVisible(true); + ui->progressBar->setVisible(true); + break; + } + +} + +void PlayerWidget::playlistChanged(){ + updatinglists = true; + ui->combo_playlist->clear(); + for(int i=0; imediaCount(); i++){ + QUrl url = PLAYLIST->media(i).canonicalUrl(); + if(url.isLocalFile()){ + ui->combo_playlist->addItem(LXDG::findMimeIcon(url.fileName().section(".",-1)), url.fileName() ); + }else{ + ui->combo_playlist->addItem(LXDG::findIcon("download",""), url.toString() ); + } + } + if(PLAYLIST->currentIndex()<0 && PLAYLIST->mediaCount()>0){ PLAYLIST->setCurrentIndex(0); } + ui->combo_playlist->setCurrentIndex(PLAYLIST->currentIndex()); + + updatinglists = false; +} + +void PlayerWidget::currentSongChanged(){ + if(PLAYLIST->currentIndex() != ui->combo_playlist->currentIndex()){ + updatinglists = true; + ui->combo_playlist->setCurrentIndex(PLAYLIST->currentIndex()); + updatinglists = false; + } + ui->tool_next->setEnabled( PLAYLIST->nextIndex() >= 0 ); + ui->tool_prev->setEnabled( PLAYLIST->previousIndex() >= 0); + ui->label_num->setText( QString::number( PLAYLIST->currentIndex()+1)+"/"+QString::number(PLAYLIST->mediaCount()) ); + ui->progressBar->setRange(0, PLAYER->duration() ); + ui->progressBar->setValue(0); +} + +void PlayerWidget::updateProgress(qint64 val){ + //qDebug() << "Update Progress Bar:" << val; + ui->progressBar->setValue(val); +} + +void PlayerWidget::updateMaxProgress(qint64 val){ + ui->progressBar->setRange(0,val); +} + + +AudioPlayerPlugin::AudioPlayerPlugin(QWidget *parent, QString ID) : LDPlugin(parent, ID){ + player = new PlayerWidget(this); + this->setLayout( new QVBoxLayout() ); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(player); + +} + +AudioPlayerPlugin::~AudioPlayerPlugin(){ + //qDebug() << "Remove AudioPlayerPlugin"; +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.h new file mode 100644 index 00000000..6aaeac4c --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.h @@ -0,0 +1,84 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a simple audio player on the desktop +//=========================================== +#ifndef _LUMINA_DESKTOP_PLUGIN_AUDIO_PLAYER_WIDGET_H +#define _LUMINA_DESKTOP_PLUGIN_AUDIO_PLAYER_WIDGET_H + +#include +#include +#include +#include +#include + +#include "../LDPlugin.h" + +namespace Ui{ + class PlayerWidget; +}; + +class PlayerWidget : public QWidget{ + Q_OBJECT +public: + PlayerWidget(QWidget *parent = 0); + ~PlayerWidget(); + +public slots: + void LoadIcons(); + +private: + Ui::PlayerWidget *ui; + QMediaPlaylist *PLAYLIST; + QMediaPlayer *PLAYER; + QMenu *configMenu, *addMenu; + bool updatinglists; + +private slots: + void playClicked(); + void pauseClicked(); + void stopClicked(); + void nextClicked(); + void prevClicked(); + + void AddFilesToPlaylist(); + void AddDirToPlaylist(); + void AddURLToPlaylist(); + void ClearPlaylist(); + void ShufflePlaylist(); + void userlistSelectionChanged(); //front-end combobox was changed by the user + void playerStateChanged(); + void playlistChanged(); //list of items changed + void currentSongChanged(); + void updateProgress(qint64 val); + void updateMaxProgress(qint64 val); +}; + +// Wrapper class to put this into a desktop plugin container +class AudioPlayerPlugin : public LDPlugin{ + Q_OBJECT +public: + AudioPlayerPlugin(QWidget* parent, QString ID); + ~AudioPlayerPlugin(); + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(3,1); + } + +private: + PlayerWidget *player; + +public slots: + void LocaleChange(){ + QTimer::singleShot(0,player, SLOT(LoadIcons())); + } + void ThemeChange(){ + QTimer::singleShot(0,player, SLOT(LoadIcons())); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.ui b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.ui new file mode 100644 index 00000000..b1e7ee59 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/audioplayer/PlayerWidget.ui @@ -0,0 +1,182 @@ + + + PlayerWidget + + + + 0 + 0 + 346 + 81 + + + + Form + + + QToolButton::menu-indicator{ image: none; } + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Config + + + QToolButton::InstantPopup + + + true + + + + + + + Add + + + QToolButton::InstantPopup + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + prev + + + true + + + + + + + 1/10 + + + + + + + next + + + true + + + + + + + + + + + + + + Play + + + true + + + + + + + Pause + + + true + + + + + + + Stop + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 24 + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/calendar/CalendarPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/calendar/CalendarPlugin.h new file mode 100644 index 00000000..abb138f7 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/calendar/CalendarPlugin.h @@ -0,0 +1,59 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is a quick sample desktop plugin +//=========================================== +#ifndef _LUMINA_DESKTOP_DESKTOP_PLUGIN_CALENDAR_H +#define _LUMINA_DESKTOP_DESKTOP_PLUGIN_CALENDAR_H + +#include +#include +#include +#include +#include "../LDPlugin.h" + +class CalendarPlugin : public LDPlugin{ + Q_OBJECT +private: + QCalendarWidget *cal; + QTimer *timer; + +public: + CalendarPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + this->setLayout( new QVBoxLayout()); + this->layout()->setContentsMargins(0,0,0,0); + cal = new QCalendarWidget(this); + cal->setSelectionMode(QCalendarWidget::NoSelection); + this->layout()->addWidget(cal); + timer = new QTimer(this); + timer->setInterval(1800000); //30 minute refresh timer + timer->start(); + connect(timer, SIGNAL(timeout()), this, SLOT(updateDate()) ); + QTimer::singleShot(0,this, SLOT(updateDate()) ); + connect(this, SIGNAL(PluginResized()), this, SLOT(UpdateCalendarSize())); + } + + ~CalendarPlugin(){ timer->stop(); } + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(3,2); + } + +private slots: + void updateDate(){ + if(cal->selectedDate() != QDate::currentDate()){ + cal->setSelectedDate(QDate::currentDate()); + cal->showSelectedDate(); + } + } + void UpdateCalendarSize(){ + cal->setFixedSize(this->size()); + } + + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktop-plugins.pri b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktop-plugins.pri new file mode 100644 index 00000000..8376316a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktop-plugins.pri @@ -0,0 +1,24 @@ +SOURCES += $$PWD/applauncher/AppLauncherPlugin.cpp \ + $$PWD/desktopview/DesktopViewPlugin.cpp \ + $$PWD/notepad/NotepadPlugin.cpp \ + $$PWD/audioplayer/PlayerWidget.cpp \ + $$PWD/systemmonitor/MonitorWidget.cpp \ + $$PWD/rssreader/RSSFeedPlugin.cpp \ + $$PWD/rssreader/RSSObjects.cpp +# $$PWD/messagecenter/MessageCenter.cpp + +HEADERS += $$PWD/calendar/CalendarPlugin.h \ + $$PWD/applauncher/AppLauncherPlugin.h \ + $$PWD/applauncher/OutlineToolButton.h \ + $$PWD/desktopview/DesktopViewPlugin.h \ + $$PWD/notepad/NotepadPlugin.h \ + $$PWD/audioplayer/PlayerWidget.h \ + $$PWD/systemmonitor/MonitorWidget.h \ + $$PWD/rssreader/RSSFeedPlugin.h \ + $$PWD/rssreader/RSSObjects.h +# $$PWD/quickcontainer/QuickDPlugin.h +# $$PWD/messagecenter/MessageCenter.h + +FORMS += $$PWD/audioplayer/PlayerWidget.ui \ + $$PWD/systemmonitor/MonitorWidget.ui \ + $$PWD/rssreader/RSSFeedPlugin.ui diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.cpp new file mode 100644 index 00000000..90f3374b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.cpp @@ -0,0 +1,214 @@ +#include "DesktopViewPlugin.h" + +#include +#include +#include +#include +#include + +#include +#include "LSession.h" + + +DesktopViewPlugin::DesktopViewPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + this->setLayout( new QVBoxLayout()); + this->layout()->setContentsMargins(0,0,0,0); + + list = new QListWidget(this); + list->setViewMode(QListView::IconMode); + list->setFlow(QListWidget::TopToBottom); //Qt bug workaround - need the opposite flow in the widget constructor + list->setWrapping(true); + list->setSpacing(4); + list->setSelectionBehavior(QAbstractItemView::SelectItems); + list->setSelectionMode(QAbstractItemView::ExtendedSelection); + list->setContextMenuPolicy(Qt::CustomContextMenu); + list->setMovement(QListView::Snap); //make sure items are "stuck" in the grid + + menu = new QMenu(this); + menu->addAction( LXDG::findIcon("run-build-file",""), tr("Open"), this, SLOT(runItems()) ); + menu->addSeparator(); + menu->addAction( LXDG::findIcon("edit-cut",""), tr("Cut"), this, SLOT(cutItems()) ); + menu->addAction( LXDG::findIcon("edit-copy",""), tr("Copy"), this, SLOT(copyItems()) ); + menu->addSeparator(); + menu->addAction( LXDG::findIcon("zoom-in",""), tr("Increase Icons"), this, SLOT(increaseIconSize()) ); + menu->addAction( LXDG::findIcon("zoom-out",""), tr("Decrease Icons"), this, SLOT(decreaseIconSize()) ); + menu->addSeparator(); + menu->addAction( LXDG::findIcon("edit-delete",""), tr("Delete"), this, SLOT(deleteItems()) ); + menu->addSeparator(); + if(LUtils::isValidBinary("lumina-fileinfo")){ + menu->addAction( LXDG::findIcon("system-search",""), tr("Properties"), this, SLOT(displayProperties()) ); + } + this->layout()->addWidget(list); + + connect(QApplication::instance(), SIGNAL(DesktopFilesChanged()), this, SLOT(updateContents()) ); + connect(list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(runItems()) ); + connect(list, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&)) ); + QTimer::singleShot(1000,this, SLOT(updateContents()) ); //wait a second before loading contents +} + +DesktopViewPlugin::~DesktopViewPlugin(){ + +} + +void DesktopViewPlugin::runItems(){ + QList sel = list->selectedItems(); + for(int i=0; iwhatsThis()+"\""); + } +} + +void DesktopViewPlugin::copyItems(){ + QList sel = list->selectedItems(); + if(sel.isEmpty()){ return; } //nothing selected + QStringList items; + //Format the data string + for(int i=0; iwhatsThis(); + } + //Now save that data to the global clipboard + QMimeData *dat = new QMimeData; + dat->clear(); + dat->setData("x-special/lumina-copied-files", items.join("\n").toLocal8Bit()); + QApplication::clipboard()->clear(); + QApplication::clipboard()->setMimeData(dat); +} + +void DesktopViewPlugin::cutItems(){ + QList sel = list->selectedItems(); + if(sel.isEmpty()){ return; } //nothing selected + QStringList items; + //Format the data string + for(int i=0; iwhatsThis(); + } + //Now save that data to the global clipboard + QMimeData *dat = new QMimeData; + dat->clear(); + dat->setData("x-special/lumina-copied-files", items.join("\n").toLocal8Bit()); + QApplication::clipboard()->clear(); + QApplication::clipboard()->setMimeData(dat); +} + +void DesktopViewPlugin::deleteItems(){ + QList sel = list->selectedItems(); + for(int i=0; iwhatsThis()).isDir()){ + QProcess::startDetached("rm -r \""+sel[i]->whatsThis()+"\""); + }else{ + QFile::remove(sel[i]->whatsThis()); + } + } +} + +void DesktopViewPlugin::showMenu(const QPoint &pos){ + //Make sure there is an item underneath the mouse first + if(list->itemAt(pos)!=0){ + menu->popup(this->mapToGlobal(pos)); + }else{ + //Pass the context menu request on to the desktop (emit it from the plugin) + this->showPluginMenu(); + //emit OpenDesktopMenu(); + } +} + +void DesktopViewPlugin::increaseIconSize(){ + int icosize = this->readSetting("IconSize",64).toInt(); + icosize+=16; //go in orders of 16 pixels + //list->setIconSize(QSize(icosize,icosize)); + this->saveSetting("IconSize",icosize); + QTimer::singleShot(10, this, SLOT(updateContents())); +} + +void DesktopViewPlugin::decreaseIconSize(){ + int icosize = this->readSetting("IconSize",64).toInt(); + if(icosize < 20){ return; } //too small to decrease more + icosize-=16; //go in orders of 16 pixels + //list->setIconSize(QSize(icosize,icosize)); + this->saveSetting("IconSize",icosize); + QTimer::singleShot(10,this, SLOT(updateContents())); +} + +void DesktopViewPlugin::updateContents(){ + list->clear(); + + int icosize = this->readSetting("IconSize",64).toInt(); + QSize gridSZ = QSize(qRound(1.8*icosize),icosize+4+(2*this->fontMetrics().height()) ); + //qDebug() << "Icon Size:" << icosize <<"Grid Size:" << gridSZ.width() << gridSZ.height(); + list->setGridSize(gridSZ); + list->setIconSize(QSize(icosize,icosize)); + QDir dir(QDir::homePath()+"/Desktop"); + QFileInfoList files = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Type | QDir::DirsFirst); + for(int i=0; isetSizeHint(gridSZ); //ensure uniform item sizes + //it->setForeground(QBrush(Qt::black, Qt::Dense2Pattern)); //Try to use a font color which will always be visible + it->setTextAlignment(Qt::AlignCenter); + it->setWhatsThis(files[i].absoluteFilePath()); + QString txt; + if(files[i].isDir()){ + it->setIcon( LXDG::findIcon("folder","") ); + txt = files[i].fileName(); + }else if(files[i].suffix() == "desktop" ){ + XDGDesktop desk(files[i].absoluteFilePath()); + if(desk.isValid()){ + it->setIcon( LXDG::findIcon(desk.icon,"unknown") ); + if(desk.name.isEmpty()){ + txt = files[i].fileName(); + }else{ + txt = desk.name; + } + }else{ + //Revert back to a standard file handling + it->setIcon( LXDG::findMimeIcon(files[i].fileName()) ); + txt = files[i].fileName(); + } + }else if(LUtils::imageExtensions().contains(files[i].suffix().toLower()) ){ + it->setIcon( QIcon( QPixmap(files[i].absoluteFilePath()).scaled(icosize,icosize,Qt::IgnoreAspectRatio, Qt::SmoothTransformation) ) ); + txt = files[i].fileName(); + }else{ + it->setIcon( LXDG::findMimeIcon( files[i].fileName() ) ); + txt = files[i].fileName(); + } + //Add the sym-link overlay to the icon as necessary + if(files[i].isSymLink()){ + QImage img = it->icon().pixmap(QSize(icosize,icosize)).toImage(); + int oSize = icosize/2; //overlay size + QPixmap overlay = LXDG::findIcon("emblem-symbolic-link").pixmap(oSize,oSize).scaled(oSize,oSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QPainter painter(&img); + painter.drawPixmap(icosize-oSize,icosize-oSize,overlay); //put it in the bottom-right corner + it->setIcon( QIcon(QPixmap::fromImage(img)) ); + } + //Now adjust the visible text as necessary based on font/grid sizing + it->setToolTip(txt); + if(this->fontMetrics().width(txt) > (gridSZ.width()-4) ){ + //int dash = this->fontMetrics().width("-"); + //Text too long, try to show it on two lines + txt = txt.section(" ",0,2).replace(" ","\n"); //First take care of any natural breaks + if(txt.contains("\n")){ + //need to check each line + QStringList txtL = txt.split("\n"); + for(int i=0; ifontMetrics().elidedText(txtL[i], Qt::ElideRight, gridSZ.width()-4); } + txt = txtL.join("\n"); + if(txtL.length()>2){ txt = txt.section("\n",0,1); } //only keep the first two lines + }else{ + txt = this->fontMetrics().elidedText(txt,Qt::ElideRight, 2*(gridSZ.width()-4)); + //Now split the line in half for the two lines + txt.insert( (txt.count()/2), "\n"); + } + }else{ + txt.append("\n "); //ensure two lines (2nd one invisible) - keeps formatting sane + } + it->setText(txt); + list->addItem(it); + if( (i%10) == 0){ QApplication::processEvents(); }//keep the UI snappy, every 10 items + } + list->setFlow(QListWidget::TopToBottom); //To ensure this is consistent - issues with putting it in the constructor + list->update(); //Re-paint the widget after all items are added +} + +void DesktopViewPlugin::displayProperties(){ + QList sel = list->selectedItems(); + for(int i=0; iwhatsThis()); + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.h new file mode 100644 index 00000000..046b6e5c --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/desktopview/DesktopViewPlugin.h @@ -0,0 +1,55 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a listing/launcher for things in the ~/Desktop folder +//=========================================== +#ifndef _LUMINA_DESKTOP_DESKTOP_VIEW_PLUGIN_H +#define _LUMINA_DESKTOP_DESKTOP_VIEW_PLUGIN_H + +#include +#include +#include +#include +#include + +#include "../LDPlugin.h" + +class DesktopViewPlugin : public LDPlugin{ + Q_OBJECT +public: + DesktopViewPlugin(QWidget* parent, QString ID); + ~DesktopViewPlugin(); + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(3,3); + } +private: + QListWidget *list; + QMenu *menu; + +private slots: + void runItems(); + void copyItems(); + void cutItems(); + void deleteItems(); + void showMenu(const QPoint&); + void increaseIconSize(); + void decreaseIconSize(); + void updateContents(); + void displayProperties(); + + +public slots: + void LocaleChange(){ + QTimer::singleShot(0,this, SLOT(updateContents())); + } + void ThemeChange(){ + QTimer::singleShot(0,this, SLOT(updateContents())); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/LXDG-DBusNotifier.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/LXDG-DBusNotifier.h new file mode 100644 index 00000000..64413e95 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/LXDG-DBusNotifier.h @@ -0,0 +1,17 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// Simple DBUS message handler for the FreeDesktop desktop notifications specification + + +class LXDG-DBusNotifier : public QDBusVirtualObkect{ + Q_OBJECT +public: + +private: + + +}; diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.cpp new file mode 100644 index 00000000..df07a122 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.cpp @@ -0,0 +1,90 @@ +#include "MessageCenter.h" + +#include +#include +#include +#include +#include + +MessageCenterPlugin::MessageCenterPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + //Setup the UI + QVBoxLayout *vlay = new QVBoxLayout(); + this->setLayout( new QVBoxLayout() ); + this->layout()->setContentsMargins(0,0,0,0); + vlay->setContentsMargins(3,3,3,3); + frame = new QFrame(this); + frame->setObjectName("messagecenterbase"); + this->layout()->addWidget(frame); + frame->setLayout(vlay); + + + //Setup the title bar header buttons + QHBoxLayout *hlay = new QHBoxLayout(); + tool_clearall = new QToolButton(this); + tool_clearall->setAutoRaise(true); + tool_clearone = new QToolButton(this); + tool_clearone->setAutoRaise(true); + QWidget *spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + hlay->addWidget(spacer); + hlay->addWidget(tool_clearone); + hlay->addWidget(tool_clearall); + vlay->addLayout(hlay); + + //Setup the main text widget + list_messages = new QListWidget(this); + list_messages->setSelectionMode(QAbstractItemView::SingleSelection); + list_messages->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + vlay->addWidget(list_messages); + + //Now setup the initial values + this->setInitialSize(200,300); + //Setup the button connections + connect(tool_clearall, SIGNAL(clicked()), this, SLOT(clearAllMessages()) ); + connect(tool_clearone, SIGNAL(clicked()), this, SLOT(clearSelectedMessage()) ); + + //Setup the DBUS signals/slots + if(QDBusConnection::sessionBus().isConnected()){ + if( QDBusConnection::sessionBus().registerService("org.freedesktop.Notifications") ){ + //Was able to register this service, also register everything it can do... + //SUPPORTED: "body", "body-hyperlinks", "body-markup", "icon-static" + + + } + QDBusConnection::sessionBus().connect("", "", "org.freedesktop.Notifications", "Notify", this, SLOT(newMessage(QString, uint, QString, QString, QString, QStringList, dict, int)) ); + QDBusConnection::sessionBus().interface().call("AddMatch", "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + qDebug() << "Available Session DBUS Services:" << QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); + //connect(QString(), QString(), + } + if(QDBusConnection::systemBus().isConnected()){ + qDebug() << "Available System DBUS Services:" << QDBusConnection::systemBus().interface()->registeredServiceNames().value(); + } + + QTimer::singleShot(0,this, SLOT(loadIcons()) ); +} + +MessageCenterPlugin::~MessageCenterPlugin(){ + +} + +void MessageCenterPlugin::newMessage(QString summary, QString body){ + qDebug() << "New Message:" << summary, body; +} + +void MessageCenterPlugin::clearAllMessages(){ + list_messages->clear(); +} + +void MessageCenterPlugin::clearSelectedMessage(){ + if( list_messages->currentItem()==0){ return; } //nothing selected + list_messages->removeItemWidget( list_messages->currentItem() ); +} + + +void MessageCenterPlugin::loadIcons(){ + tool_clearall->setIcon( LXDG::findIcon("edit-clear-list","") ); + tool_clearall->setToolTip( tr("Clear all messages") ); + tool_clearone->setIcon( LXDG::findIcon("edit-delete","") ); + tool_clearone->setToolTip( tr("Clear selected message") ); + +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.h new file mode 100644 index 00000000..8491546f --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/messagecenter/MessageCenter.h @@ -0,0 +1,48 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a simple DBUS monitor which display's messages that come in +//=========================================== +#ifndef _LUMINA_DESKTOP_MESSAGE_CENTER_PLUGIN_H +#define _LUMINA_DESKTOP_MESSAGE_CENTER_PLUGIN_H + +#include +#include +#include + +#include +#include "../LDPlugin.h" + +class MessageCenterPlugin : public LDPlugin{ + Q_OBJECT +public: + MessageCenterPlugin(QWidget* parent, QString ID); + ~MessageCenterPlugin(); + +private: + //QDBusConnection *sess, *sys; + QListWidget *list_messages; + QFrame *frame; + QToolButton *tool_clearall; //clear all messages + QToolButton *tool_clearone; //clear selected message + +private slots: + //void newMessage(QDBusMessage *message); + void clearAllMessages(); + void clearSelectedMessage(); + + void loadIcons(); + +public slots: + void LocaleChange(){ + QTimer::singleShot(0,this, SLOT(loadIcons())); + } + void ThemeChange(){ + QTimer::singleShot(0,this, SLOT(loadIcons())); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.cpp new file mode 100644 index 00000000..6d321305 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.cpp @@ -0,0 +1,330 @@ +#include "NotepadPlugin.h" + +#include +#include "LSession.h" +#include +#include +#include +#include +#include + +NotePadPlugin::NotePadPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + //qDebug() << "Creating Notepad Plugin:"; + QVBoxLayout *vlay = new QVBoxLayout(); + this->setLayout( new QVBoxLayout() ); + this->layout()->setContentsMargins(0,0,0,0); + vlay->setContentsMargins(3,3,3,3); + frame = new QFrame(this); + frame->setObjectName("notepadbase"); + //frame->setStyleSheet("QFrame#notepadbase{border-width: 1px; background: rgba(255,255,255,50); color: black;} QFrame{ border: none; border-radius: 3px; background: rgba(255,255,255,100); color: black;}"); + this->layout()->addWidget(frame); + frame->setLayout(vlay); + + if(!QFile::exists(QDir::homePath()+"/Notes")){ + //Create the notes directory if non-existant + QDir dir; + dir.mkpath(QDir::homePath()+"/Notes"); + } + watcher = new QFileSystemWatcher(this); + //Always watch the notes directory for new files/changes + watcher->addPath(QDir::homePath()+"/Notes"); + + typeTimer = new QTimer(this); + typeTimer->setInterval(1000); // 1 second before it saves + typeTimer->setSingleShot(true); //compress lots of signals into a single save + + updating = false; + //Setup the title bar header buttons + QHBoxLayout *hlay = new QHBoxLayout(); + config = new QToolButton(this); + config->setAutoRaise(true); + config->setMenu(new QMenu(this)); + config->setPopupMode(QToolButton::InstantPopup); + /*open = new QToolButton(this); + open->setAutoRaise(true); + add = new QToolButton(this); + add->setAutoRaise(true); + rem = new QToolButton(this); + rem->setAutoRaise(true);*/ + cnote = new QComboBox(this); + + hlay->addWidget(cnote); + hlay->addWidget(config); + //hlay->addWidget(open); + //hlay->addWidget(add); + //hlay->addWidget(rem); + vlay->addLayout(hlay); + + //Setup the main text widget + edit = new QPlainTextEdit(this); + edit->setReadOnly(false); + edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + vlay->addWidget(edit); + edit->setContextMenuPolicy(Qt::NoContextMenu); + + //Now load the new file-based system for saving notes + //qDebug() << "Saving a new setting"; + this->saveSetting("customFile",""); //always clear this when the plugin is initialized (only maintained per-session) + //qDebug() << "Loading Notes Dir"; + QTimer::singleShot(10, this, SLOT(notesDirChanged())); + //qDebug() << "Set Sizing"; + + //qDebug() << "Connect Signals/slots"; + //Setup the button connections + /*connect(open, SIGNAL(clicked()), this, SLOT(openNoteClicked()) ); + connect(add, SIGNAL(clicked()), this, SLOT(newNoteClicked()) ); + connect(rem, SIGNAL(clicked()), this, SLOT(remNote()) );*/ + //connect(config, SIGNAL(clicked()), this, SLOT(openConfigMenu()) ); + connect(edit, SIGNAL(textChanged()), this, SLOT(newTextAvailable()) ); + connect(cnote, SIGNAL(currentIndexChanged(QString)), this, SLOT(noteChanged()) ); + connect(typeTimer, SIGNAL(timeout()), this, SLOT(updateContents()) ); + connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(notesDirChanged()) ); //re-load the available notes + connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(noteChanged()) ); //re-load the current file + QTimer::singleShot(0,this, SLOT(loadIcons()) ); + //qDebug() << " - Done with init"; +} + +NotePadPlugin::~NotePadPlugin(){ + +} + + +void NotePadPlugin::openNote(){ + //qDebug() << "Open New Note:"; + //Prompt for a name for the new note + QFileDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setFileMode(QFileDialog::ExistingFile); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setNameFilters( QStringList() << tr("Note Files (*.note)") << tr("Text Files (*)")); + dlg.setWindowTitle(tr("Open a note file")); + dlg.setWindowIcon( LXDG::findIcon("document-open","") ); + dlg.setDirectory(QDir::homePath()); //start in the home directory + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + QStringList sel = dlg.selectedFiles(); + if(sel.isEmpty() || dlg.result()!=QDialog::Accepted){ return; } //cancelled + QString fullpath = sel.first(); + QString name = fullpath.section("/",-1); + //qDebug() << " - Found Note:" << name << fullpath; + int index = cnote->findText(name, Qt::MatchExactly | Qt::MatchCaseSensitive); + if(QFile::exists(fullpath) && index <0){ + //Alternate option of searching for the file in the list + index = cnote->findText(fullpath, Qt::MatchExactly | Qt::MatchCaseSensitive); + } + if(index>=0){ + //This note already exists: just load it + cnote->setCurrentIndex(index); + }else{ + //New note - add it to the end of the list and then load it + cnote->addItem(name, fullpath); + this->saveSetting("customFile", fullpath); //save this as a custom file + cnote->setCurrentIndex( cnote->count()-1 ); + QTimer::singleShot(1000, this, SLOT(notesDirChanged())); //Make sure to refresh the list (only one custom file at a time) + } +} + +QString NotePadPlugin::newNoteName(QString oldname, bool tryagain){ + //Prompt for a name for the new note + //qDebug() << "Create new note"; + QInputDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setInputMode(QInputDialog::TextInput); + dlg.setLabelText(tr("Name:")); + dlg.setTextEchoMode(QLineEdit::Normal); + if(tryagain){ dlg.setWindowTitle(tr("Invalid Note Name: Try Again")); } + else{ dlg.setWindowTitle(tr("Select a Note Name")); } + dlg.setWindowIcon( LXDG::findIcon("document-new","") ); + dlg.setTextValue(oldname); + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + //this->thread()->usleep(300000); //300 ms between updates + QApplication::processEvents(); + } + QString name = dlg.textValue(); + //make sure to remove any "bad" characters from the name + name.remove("\""); name.remove(";"); name.remove("\'"); name.replace("/","_"); + if(name.isEmpty() || dlg.result()!=QDialog::Accepted){ return ""; } //cancelled + //Check validity of the new note filename + QString fullpath = QDir::homePath()+"/Notes/"+name; + if(!fullpath.endsWith(".note")){ fullpath.append(".note"); } + if(QFile::exists(fullpath)){ + return newNoteName(name, true); //try again + } + return name; //good name - go ahead and return it +} + +void NotePadPlugin::updateConfigMenu(){ + //Re-create the menu and open it + config->menu()->clear(); + config->menu()->addAction(LXDG::findIcon("document-open",""), tr("Open Text File"), this, SLOT(openNoteClicked()) ); + config->menu()->addAction(LXDG::findIcon("document-new",""), tr("Create a Note"), this, SLOT(newNoteClicked()) ); + if(cnote->currentIndex()>=0){ + config->menu()->addSeparator(); + config->menu()->addAction(LXDG::findIcon("document-edit",""), tr("Rename Note"), this, SLOT(renameNote()) ); + config->menu()->addAction(LXDG::findIcon("document-close",""), tr("Delete Note"), this, SLOT(remNote()) ); + } +} + +void NotePadPlugin::openNoteClicked(){ + openNote(); +} + +void NotePadPlugin::newNoteClicked(){ + //QtConcurrent::run(this, &NotePadPlugin::newNote); + QString name = newNoteName(); + if(name.isEmpty()){ return; } + QString fullpath = QDir::homePath()+"/Notes/"+name; + if(!fullpath.endsWith(".note")){ fullpath.append(".note"); } + //qDebug() << " - New Note:" << name << fullpath; + int index = cnote->findText(name, Qt::MatchExactly | Qt::MatchCaseSensitive); + if(QFile::exists(fullpath) && index <0){ + //Alternate option of searching for the file in the list + index = cnote->findText(fullpath, Qt::MatchExactly | Qt::MatchCaseSensitive); + } + if(index>=0){ + //This note already exists: just load it + cnote->setCurrentIndex(index); + }else{ + //New note - add it to the end of the list and then load it + cnote->addItem(name, fullpath); + cnote->setCurrentIndex( cnote->count()-1 ); + } +} + +void NotePadPlugin::remNote(){ + QString note = cnote->currentData().toString(); + if(note.isEmpty()){ return; } + watcher->removePath(note); //remove this file from the watcher + this->saveSetting("currentFile",""); //reset the internal value + QFile::remove(note); //remove the file + //if(!note.startsWith(QDir::homePath()+"/Notes/") ){ + //If the file was not in the notes directory, need to manually prompt for a re-load + // otherwise, the directory watcher will catch it and trigger a re-load (no need to double-load) + notesDirChanged(); + //} +} + +void NotePadPlugin::renameNote(){ + int item = cnote->currentIndex(); + if(item<0){ return; } //nothing selected + QString oldpath = cnote->currentData().toString(); + if(oldpath.isEmpty() || !oldpath.endsWith(".note")){ return; } + QString name = newNoteName(cnote->currentText()); + if(name.isEmpty()){ return; } + QString fullpath = QDir::homePath()+"/Notes/"+name; + if(!fullpath.endsWith(".note")){ fullpath.append(".note"); } + //qDebug() << " - New Note:" << name << fullpath; + //Update the current item data to point to this file + cnote->setItemText(item, name); + cnote->setItemData(item, fullpath); + //Now move the file over + QFile::rename(oldpath, fullpath); + noteChanged(); +} + +void NotePadPlugin::newTextAvailable(){ + if(updating){ return; } //programmatic change of the widget + if(typeTimer->isActive()){ typeTimer->stop(); } + typeTimer->start(); +} + +void NotePadPlugin::updateContents(){ + if(updating){ return; } //this was a programmatic change to the widget + //The text was changed in the plugin - save it in the file + QString note = cnote->currentData().toString(); + updating = true; + LUtils::writeFile(note, edit->toPlainText().split("\n"), true); + QApplication::processEvents(); //make sure to process/discard the file changed signal before disabling the flag + updating = false; +} + +void NotePadPlugin::notesDirChanged(){ + if(updating){ return; } + QString cfile = this->readSetting("currentFile","").toString(); + QStringList notes; + QDir dir(QDir::homePath()+"/Notes"); + QStringList files = dir.entryList(QStringList() << "*.note", QDir::Files | QDir::NoDotAndDotDot, QDir::Name); + for(int i=0; ireadSetting("customFile","").toString(); + if(!custom.isEmpty() && QFile::exists(custom) ){ notes << custom; } + //qDebug() << "Available Notes:" << notes << cfile; + //Now update the UI list + updating = true; //don't refresh the UI until done changing lists + cnote->clear(); + bool found = false; + for(int i=0; iaddItem(name, notes[i]); + if(notes[i]==cfile){ cnote->setCurrentIndex(i); found = true;} + } + if(!found && !cfile.isEmpty() && QFile::exists(cfile)){ + //Current note is a manually-loaded text file + cnote->addItem(cfile.section("/",-1), cfile); + cnote->setCurrentIndex( cnote->count()-1 ); //last item + found = true; + } + if(!found && cnote->count()>0){ cnote->setCurrentIndex(0); } + updating =false; + noteChanged(); +} + +void NotePadPlugin::noteChanged(){ + if(updating){ return; } + updating =true; + QString note; + if(cnote->currentIndex()>=0){ + note = cnote->currentData().toString(); + } + QTimer::singleShot(0, this, SLOT(updateConfigMenu()) ); + if(note.isEmpty() && cnote->count()>0){ + updating=false; + cnote->setCurrentIndex(0); + return; + } + QString oldnote = this->readSetting("currentFile","").toString(); + //qDebug() << "Note Changed:" << note << oldnote; + if( oldnote!=note ){ + //Clear the old note file/setting + if(!oldnote.isEmpty()){ + watcher->removePath(oldnote); + this->saveSetting("currentFile",""); + } + if(!note.isEmpty()){ + this->saveSetting("currentFile",note); + watcher->addPath(note); + } + } + + if(!note.isEmpty()){ + QString text = LUtils::readFile(note).join("\n"); + if(text!=edit->toPlainText()){ + edit->setPlainText( text ); + } + }else{ + edit->clear(); + } + //If no notes available - disable the editor until a new one is created + edit->setEnabled(!note.isEmpty()); + //rem->setEnabled(!note.isEmpty()); + cnote->setEnabled(!note.isEmpty()); + //leave the new/open buttons enabled all the time + updating = false; +} + + +void NotePadPlugin::loadIcons(){ + /*open->setIcon( LXDG::findIcon("document-open","") ); + add->setIcon( LXDG::findIcon("document-new","") ); + rem->setIcon( LXDG::findIcon("document-close","") );*/ + config->setIcon( LXDG::findIcon("configure","") ); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.h new file mode 100644 index 00000000..5084dadf --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/notepad/NotepadPlugin.h @@ -0,0 +1,66 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a simple text editor for notes on the desktop +//=========================================== +#ifndef _LUMINA_DESKTOP_NOTEPAD_PLUGIN_H +#define _LUMINA_DESKTOP_NOTEPAD_PLUGIN_H + +#include +#include +#include +#include +#include +#include +#include "../LDPlugin.h" + +class NotePadPlugin : public LDPlugin{ + Q_OBJECT +public: + NotePadPlugin(QWidget* parent, QString ID); + ~NotePadPlugin(); + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(3,3); + } +private: + QPlainTextEdit *edit; + QToolButton *config; //*open, *add, *rem; + QComboBox *cnote; + QFrame *frame; + QFileSystemWatcher *watcher; + bool updating; + QTimer *typeTimer; + + void openNote(); + QString newNoteName(QString oldname = "", bool tryagain = false); + +private slots: + void updateConfigMenu(); + + void openNoteClicked(); + void newNoteClicked(); + void remNote(); + void renameNote(); + void newTextAvailable(); + void updateContents(); + + void notesDirChanged(); + void noteChanged(); + + void loadIcons(); + +public slots: + void LocaleChange(){ + QTimer::singleShot(0,this, SLOT(noteChanged())); + } + void ThemeChange(){ + QTimer::singleShot(0,this, SLOT(loadIcons())); + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/quickcontainer/QuickDPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/quickcontainer/QuickDPlugin.h new file mode 100644 index 00000000..d6039ac0 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/quickcontainer/QuickDPlugin.h @@ -0,0 +1,51 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is a simple container for a QtQuick plugin +//=========================================== +#ifndef _LUMINA_DESKTOP_DESKTOP_PLUGIN_QUICK_H +#define _LUMINA_DESKTOP_DESKTOP_PLUGIN_QUICK_H + +#include +#include +#include "../LDPlugin.h" + +#include + +class QuickDPlugin : public LDPlugin{ + Q_OBJECT +public: + QuickDPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID){ + this->setLayout( new QVBoxLayout()); + this->layout()->setContentsMargins(0,0,0,0); + container = new QQuickWidget(this); + container->setResizeMode(QQuickWidget::SizeRootObjectToView); + connect(container, SIGNAL(statusChanged(QQuickWidget::Status)), this, SLOT(statusChange(QQuickWidget::Status)) ); + this->layout()->addWidget(container); + container->setSource(QUrl::fromLocalFile( LUtils::findQuickPluginFile(ID.section("---",0,0)) )); + QApplication::processEvents(); //to check for errors right away + //this->setInitialSize(container->initialSize().width(), container->initialSize().height()); + } + + ~QuickDPlugin(){} + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(2,2); + } +private: + QQuickWidget *container; + +private slots: + void statusChange(QQuickWidget::Status status){ + if(status == QQuickWidget::Error){ + qDebug() << "Quick Widget Error:" << this->ID(); + container->setSource(QUrl()); //clear out the script - experienced an error + } + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.cpp new file mode 100644 index 00000000..c330d6c0 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.cpp @@ -0,0 +1,363 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "RSSFeedPlugin.h" +#include "ui_RSSFeedPlugin.h" + +#include +#include "LSession.h" +#include +#include +#include +#include +#include + +RSSFeedPlugin::RSSFeedPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID), ui(new Ui::RSSFeedPlugin()){ + ui->setupUi(this); + //Load the global settings + setprefix = "rssreader/"; //this structure/prefix should be used for *all* plugins of this type + RSS = new RSSReader(this, setprefix); + ui->text_feed->setContextMenuPolicy(Qt::NoContextMenu); + //Create the options menu + optionsMenu = new QMenu(this); + ui->tool_options->setMenu(optionsMenu); + presetMenu = new QMenu(this); + ui->tool_add_preset->setMenu(presetMenu); + + //Setup any signal/slot connections + connect(ui->push_back1, SIGNAL(clicked()), this, SLOT(backToFeeds()) ); + connect(ui->push_back2, SIGNAL(clicked()), this, SLOT(backToFeeds()) ); + connect(ui->push_back3, SIGNAL(clicked()), this, SLOT(backToFeeds()) ); + connect(ui->push_save_settings, SIGNAL(clicked()), this, SLOT(saveSettings()) ); + connect(RSS, SIGNAL(rssChanged(QString)), this, SLOT(RSSItemChanged(QString)) ); + connect(RSS, SIGNAL(newChannelsAvailable()), this, SLOT(UpdateFeedList())); + connect(ui->tool_gotosite, SIGNAL(clicked()), this, SLOT(openFeedPage()) ); + connect(ui->push_rm_feed, SIGNAL(clicked()), this, SLOT(removeFeed()) ); + connect(ui->push_add_url, SIGNAL(clicked()), this, SLOT(addNewFeed()) ); + connect(ui->combo_feed, SIGNAL(currentIndexChanged(int)), this, SLOT(currentFeedChanged()) ); + + connect(presetMenu, SIGNAL(triggered(QAction*)), this, SLOT(loadPreset(QAction*)) ); + + updateOptionsMenu(); + QTimer::singleShot(0,this, SLOT(ThemeChange()) ); + //qDebug() << " - Done with init"; + QStringList feeds; + if( !LSession::handle()->DesktopPluginSettings()->contains(setprefix+"currentfeeds") ){ + //First-time run of the plugin - automatically load the default feeds + feeds = LOS::RSSFeeds(); + for(int i=0; iDesktopPluginSettings()->setValue(setprefix+"currentfeeds", feeds); + }else{ + feeds = LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList(); + } + RSS->addUrls(feeds); + backToFeeds(); //always load the first page +} + +RSSFeedPlugin::~RSSFeedPlugin(){ + +} + +//================ +// PRIVATE +//================ +void RSSFeedPlugin::updateOptionsMenu(){ + optionsMenu->clear(); + optionsMenu->addAction(LXDG::findIcon("list-add",""), tr("Add RSS Feed"), this, SLOT(openFeedNew()) ); + optionsMenu->addAction(LXDG::findIcon("help-about",""), tr("View Feed Details"), this, SLOT(openFeedInfo()) ); + optionsMenu->addAction(LXDG::findIcon("configure",""), tr("Settings"), this, SLOT(openSettings()) ); + optionsMenu->addSeparator(); + optionsMenu->addAction(LXDG::findIcon("download",""), tr("Update Feeds Now"), this, SLOT(resyncFeeds()) ); + + presetMenu->clear(); + QStringList feeds = LOS::RSSFeeds(); + feeds << tr("Lumina Desktop RSS")+"::::http://lumina-desktop.org/?feed=rss2"; + feeds.sort(); + for(int i=0; iaddAction(feeds[i].section("::::",0,0) ); + tmp->setWhatsThis( feeds[i].section("::::",1,-1) ); + } +} + +void RSSFeedPlugin::checkFeedNotify(){ + bool notify = false; + for(int i=0; icombo_feed->count() && !notify; i++){ + if( !ui->combo_feed->itemData(i, Qt::WhatsThisRole).toString().isEmpty()){ notify = true; } + } + QString style; + if(notify){ style = "QComboBox{ background-color: rgba(255,0,0,120); }"; } + ui->combo_feed->setStyleSheet(style); +} + +//Simplification functions for loading feed info onto widgets +void RSSFeedPlugin::updateFeed(QString ID){ + //Now clear/update the feed viewer (HTML) + ui->text_feed->clear(); + if(ID.isEmpty()){ return; } //nothing to show + + //Save the datetime this feed was read + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"feedReads/"+ID, QDateTime::currentDateTime() ); + //Get the color to use for hyperlinks (need to specify in html) + QString color = ui->text_feed->palette().text().color().name(); //keep the hyperlinks the same color as the main text (different formatting still applies) + QString html; + RSSchannel data = RSS->dataForID(ID); + ui->label_lastupdate->setText( data.lastsync.toString(Qt::DefaultLocaleShortDate) ); + // - generate the html + // html.append("
    \n"); + for(int i=0; i"); + html.append("

    "+data.items[i].title+"

    "); + if(!data.items[i].pubdate.isNull() || !data.items[i].author.isEmpty()){ + html.append("("); + if(!data.items[i].pubdate.isNull()){ html.append(data.items[i].pubdate.toString(Qt::DefaultLocaleShortDate)); } + if(!data.items[i].author.isEmpty()){ + if(!html.endsWith("(")){ html.append(", "); } //spacing between date/author + if(!data.items[i].author_email.isEmpty()){ html.append(""+data.items[i].author+""); } + else{ html.append(data.items[i].author); } + } + html.append(")
    "); + } + html.append(data.items[i].description); + //html.append("\n"); + if(i+1 < data.items.length()){ html.append("
    "); } + } + //html.append("
\n"); + // - load that html into the viewer + ui->text_feed->setHtml(html); +} + +void RSSFeedPlugin::updateFeedInfo(QString ID){ + ui->page_feed_info->setWhatsThis(ID); + ui->text_feed_info->clear(); + if(ID.isEmpty()){ return; } + //Get the color to use for hyperlinks (need to specify in html) + QString color = ui->text_feed->palette().text().color().name(); //keep the hyperlinks the same color as the main text (different formatting still applies) + QString html; + RSSchannel data = RSS->dataForID(ID); + // - generate the html + // "+TEXT+" + html.append( QString(tr("Feed URL: %1")).arg(""+data.originalURL+"") +"

"); + html.append( QString(tr("Title: %1")).arg(data.title) +"
"); + html.append( QString(tr("Description: %1")).arg(data.description) +"
"); + html.append( QString(tr("Website: %1")).arg(""+data.link+"") +"

"); + if(!data.lastBuildDate.isNull()){ html.append( QString(tr("Last Build Date: %1")).arg(data.lastBuildDate.toString(Qt::DefaultLocaleShortDate)) +"
"); } + html.append( QString(tr("Last Sync: %1")).arg(data.lastsync.toString(Qt::DefaultLocaleShortDate)) +"
"); + html.append( QString(tr("Next Sync: %1")).arg(data.nextsync.toString(Qt::DefaultLocaleShortDate)) +"
"); + // - load that html into the viewer + ui->text_feed_info->setHtml(html); +} + +//================ +// PRIVATE SLOTS +//================ +void RSSFeedPlugin::loadIcons(){ + ui->tool_options->setIcon( LXDG::findIcon("configure","") ); + ui->tool_gotosite->setIcon( LXDG::findIcon("applications-internet","") ); + ui->push_back1->setIcon( LXDG::findIcon("go-previous","") ); + ui->push_back2->setIcon( LXDG::findIcon("go-previous","") ); + ui->push_back3->setIcon( LXDG::findIcon("go-previous","") ); + ui->push_rm_feed->setIcon( LXDG::findIcon("list-remove","") ); + ui->push_add_url->setIcon( LXDG::findIcon("list-add","") ); + ui->push_save_settings->setIcon( LXDG::findIcon("document-save","") ); + ui->tool_add_preset->setIcon( LXDG::findIcon("bookmark-new-list","") ); +} + +//GUI slots +// - Page management +void RSSFeedPlugin::backToFeeds(){ + ui->stackedWidget->setCurrentWidget(ui->page_feed); +} + +void RSSFeedPlugin::openFeedInfo(){ + QString ID = ui->combo_feed->currentData().toString(); + if(ID.isEmpty()){ return; } + updateFeedInfo(ID); + ui->stackedWidget->setCurrentWidget(ui->page_feed_info); + +} + +void RSSFeedPlugin::openFeedNew(){ + ui->line_new_url->setText(""); + ui->stackedWidget->setCurrentWidget(ui->page_new_feed); +} + +void RSSFeedPlugin::openSettings(){ + //Sync the widget with the current settings + QSettings *set = LSession::handle()->DesktopPluginSettings(); + + ui->check_manual_sync->setChecked( set->value(setprefix+"manual_sync_only", false).toBool() ); + int DI = set->value(setprefix+"default_interval_minutes", 60).toInt(); + if(DI<1){ DI = 60; } + if( (DI%60) == 0 ){DI = DI/60; ui->combo_sync_units->setCurrentIndex(1); } //hourly setting + else{ ui->combo_sync_units->setCurrentIndex(1); } //minutes setting + ui->spin_synctime->setValue(DI); + + //Now show the page + ui->stackedWidget->setCurrentWidget(ui->page_settings); +} + +// - Feed Management +void RSSFeedPlugin::addNewFeed(){ + if(ui->line_new_url->text().isEmpty()){ return; } //nothing to add + //Validate the URL + QUrl url(ui->line_new_url->text()); + if(!url.isValid()){ + ui->line_new_url->setFocus(); + return; + } + //Add the URL to the settings file for next login + QStringList feeds = LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList(); + feeds << url.toString(); + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"currentfeeds", feeds); + + //Set this URL as the current selection + ui->combo_feed->setWhatsThis(url.toString()); //hidden field - will trigger an update in a moment + //Add the URL to the backend + RSS->addUrls(QStringList() << url.toString()); + //UpdateFeedList(); //now re-load the feeds which are available + + //Now go back to the main page + backToFeeds(); +} + +void RSSFeedPlugin::loadPreset(QAction *act){ + ui->line_new_url->setText( act->whatsThis() ); +} + +void RSSFeedPlugin::removeFeed(){ + QString ID = ui->page_feed_info->whatsThis(); + if(ID.isEmpty()){ return; } + //Remove from the RSS feed object + RSSchannel info = RSS->dataForID(ID); + RSS->removeUrl(ID); + //Remove the URL from the settings file for next login + QStringList feeds = LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList(); + feeds.removeAll(info.originalURL); + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"currentfeeds", feeds); + LSession::handle()->DesktopPluginSettings()->remove(setprefix+"feedReads/"+ID); + //Now go back to the main page + backToFeeds(); +} + +void RSSFeedPlugin::resyncFeeds(){ + RSS->addUrls( LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList() ); + RSS->syncNow(); +} + +// - Feed Interactions +void RSSFeedPlugin::currentFeedChanged(){ + QString ID = ui->combo_feed->currentData().toString(); + //Remove the "unread" color/flag from the feed + ui->combo_feed->setItemData( ui->combo_feed->currentIndex(), QBrush(Qt::transparent) , Qt::BackgroundRole); + ui->combo_feed->setItemData( ui->combo_feed->currentIndex(), "", Qt::WhatsThisRole); + checkFeedNotify(); + updateFeed(ID); +} + +void RSSFeedPlugin::openFeedPage(){ //Open main website for feed + QString ID = ui->combo_feed->currentData().toString(); + //Find the data associated with this feed + RSSchannel data = RSS->dataForID(ID); + QString url = data.link; + //qDebug() << "Open Feed Page:" << url; + //Now launch the browser + if(!url.isEmpty()){ + LSession::LaunchApplication("lumina-open \""+url+"\""); + } +} + +void RSSFeedPlugin::saveSettings(){ + QSettings *set = LSession::handle()->DesktopPluginSettings(); + set->setValue(setprefix+"manual_sync_only", ui->check_manual_sync->isChecked() ); + int DI = ui->spin_synctime->value(); + if(ui->combo_sync_units->currentIndex()==1){ DI = DI*60; } //convert from hours to minutes + set->setValue(setprefix+"default_interval_minutes", DI); + set->sync(); + + //Now go back to the feeds + backToFeeds(); +} + +//Feed Object interactions +void RSSFeedPlugin::UpdateFeedList(){ + + QString activate = ui->combo_feed->whatsThis(); + if(!activate.isEmpty()){ ui->combo_feed->setWhatsThis(""); } + if(activate.isEmpty()){ activate = ui->combo_feed->currentData().toString(); } // keep current item selected + //Now get/list all the available feeds + QStringList IDS = RSS->channels(); //this is pre-sorted by title of the feed + //qDebug() << "Update RSS Feed List:" << IDS << activate; + for(int i=0; icombo_feed->count()<=i){ newitem = true; } + else{ + QString cid = ui->combo_feed->itemData(i).toString(); + if(IDS[i]!=cid){ + if(IDS.contains(cid)){ newitem = true; } //this item is just out of order + else{ ui->combo_feed->removeItem(i); } //item no longer is valid + } + } + if(newitem){ + //Need to add a new item at this point in the menu + RSSchannel info = RSS->dataForID(IDS[i]); + if(info.title.isEmpty()){ + //invalid/empty channel + ui->combo_feed->insertItem(i, IDS[i], IDS[i]); //just show the URL + }else{ + ui->combo_feed->insertItem(i, info.icon, info.title, IDS[i]); + } + } + } + //Remove any extra items on the end of the list + for(int i=IDS.length(); icombo_feed->count(); i++){ + ui->combo_feed->removeItem(i); i--; + } + //Now activate the proper item as needed + if(IDS.contains(activate)){ + ui->combo_feed->setCurrentIndex( IDS.indexOf(activate) ); + } + checkFeedNotify(); +} + +void RSSFeedPlugin::RSSItemChanged(QString ID){ + for(int i=0; icombo_feed->count(); i++){ + if(ui->combo_feed->itemData(i).toString()!=ID){ continue; } + RSSchannel info = RSS->dataForID(ID); + if(info.title.isEmpty()){ + ui->combo_feed->setItemText(i, ID); + ui->combo_feed->setItemIcon(i, LXDG::findIcon("dialog-cancel","") ); + }else{ + ui->combo_feed->setItemText(i, info.title); + ui->combo_feed->setItemIcon(i, info.icon ); + QColor color(Qt::transparent); + if( info.lastBuildDate > LSession::handle()->DesktopPluginSettings()->value(setprefix+"feedReads/"+ID,QDateTime()).toDateTime() ){ + color = QColor(255,10,10,100); //semi-transparent red + ui->combo_feed->setItemData(i, "notify", Qt::WhatsThisRole); + }else{ + ui->combo_feed->setItemData(i, "", Qt::WhatsThisRole); + } + ui->combo_feed->setItemData(i, QBrush(color) , Qt::BackgroundRole); + } + } + if(ID == ui->combo_feed->currentData().toString()){ + currentFeedChanged(); //re-load the current feed + }else{ + checkFeedNotify(); + } +} + +//================== +// PUBLIC SLOTS +//================== +void RSSFeedPlugin::LocaleChange(){ + ui->retranslateUi(this); + updateOptionsMenu(); +} +void RSSFeedPlugin::ThemeChange(){ + QTimer::singleShot(0,this, SLOT(loadIcons())); + updateOptionsMenu(); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.h new file mode 100644 index 00000000..68b36760 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.h @@ -0,0 +1,72 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a simple RSS feed reader for the desktop +//=========================================== +#ifndef _LUMINA_DESKTOP_RSS_FEEDER_PLUGIN_H +#define _LUMINA_DESKTOP_RSS_FEEDER_PLUGIN_H + +#include +#include "../LDPlugin.h" + +#include "RSSObjects.h" + +namespace Ui{ + class RSSFeedPlugin; +}; + +class RSSFeedPlugin : public LDPlugin{ + Q_OBJECT +public: + RSSFeedPlugin(QWidget* parent, QString ID); + ~RSSFeedPlugin(); + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(3,3); + } +private: + Ui::RSSFeedPlugin *ui; + QMenu *optionsMenu, *presetMenu; + QString setprefix; //settings prefix + RSSReader *RSS; + + void updateOptionsMenu(); + void checkFeedNotify(); //check if unread feeds are available and change the styling a bit as needed + + //Simplification functions for loading feed info onto widgets + void updateFeed(QString ID); + void updateFeedInfo(QString ID); + +private slots: + void loadIcons(); + + //GUI slots + // - Page management + void backToFeeds(); + void openFeedInfo(); + void openFeedNew(); + void openSettings(); + // - Feed Management + void addNewFeed(); // the "add" button (current url in widget on page) + void loadPreset(QAction*); //the add-preset menu + void removeFeed(); // the "remove" button (current feed for page) + void resyncFeeds(); + // - Feed Interactions + void currentFeedChanged(); + void openFeedPage(); //Open the website in a browser + void saveSettings(); + + //Feed Object interactions + void UpdateFeedList(); + void RSSItemChanged(QString ID); + +public slots: + void LocaleChange(); + void ThemeChange(); + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.ui b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.ui new file mode 100644 index 00000000..dc7acd0b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSFeedPlugin.ui @@ -0,0 +1,552 @@ + + + RSSFeedPlugin + + + + 0 + 0 + 238 + 278 + + + + Form + + + + 0 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 1 + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + View Options + + + + + + QToolButton::InstantPopup + + + true + + + + + + + + + + + + + + + + + + Open Website + + + More + + + true + + + Qt::NoArrow + + + + + + + + + false + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + + + + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 5 + + + + + + + Back to Feeds + + + true + + + + + + + + + + 0 + 0 + + + + Feed Information + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + true + + + + + + + + + + + + Remove Feed + + + true + + + + + + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Back to Feeds + + + true + + + + + + + + + + 0 + 0 + + + + New Feed Subscription + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + RSS URL + + + Qt::AlignCenter + + + + + + + + + + + + Load a preset RSS Feed + + + + + + QToolButton::InstantPopup + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add to Feeds + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Back to Feeds + + + true + + + + + + + + + + 0 + 0 + + + + Feed Reader Settings + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Manual Sync Only + + + + + + + Some RSS feeds may request custom update intervals instead of using this setting + + + Default Sync Interval + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 1 + + + 60 + + + + + + + Hour(s) + + + 1 + + + + Minutes + + + + + Hour(s) + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save Settings + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSObjects.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSObjects.cpp new file mode 100644 index 00000000..0a805252 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/rssreader/RSSObjects.cpp @@ -0,0 +1,287 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "RSSObjects.h" +#include +#include + +#include "LSession.h" + +//============ +// PUBLIC +//============ +RSSReader::RSSReader(QObject *parent, QString settingsPrefix) : QObject(parent){ + NMAN = new QNetworkAccessManager(this); + connect(NMAN, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)) ); + connect(NMAN, SIGNAL(sslErrors(QNetworkReply*, const QList&)), this, SLOT(sslErrors(QNetworkReply*, const QList&)) ); + + setprefix = settingsPrefix; + syncTimer = new QTimer(this); + syncTimer->setInterval(300000); //5 minutes + connect(syncTimer, SIGNAL(timeout()), this, SLOT(checkTimes())); + syncTimer->start(); +} + +RSSReader::~RSSReader(){ + +} + +//Information retrieval +QStringList RSSReader::channels(){ + QStringList urls = hash.keys(); + QStringList ids; + //sort all the channels by title before output + for(int i=0; iget( QNetworkRequest( QUrl(url) ) ); + outstandingURLS << url; + } +} + +//RSS parsing functions +RSSchannel RSSReader::readRSS(QByteArray bytes){ + //Note: We could expand this later to support multiple "channel"s per Feed + // but it seems like there is normally only one channel anyway + //qDebug() << "Read RSS:" << bytes.left(100); + QXmlStreamReader xml(bytes); + RSSchannel rssinfo; + //qDebug() << "Can Read XML Stream:" << !xml.hasError(); + if(xml.readNextStartElement()){ + //qDebug() << " - RSS Element:" << xml.name(); + if(xml.name() == "rss" && (xml.attributes().value("version") =="2.0" || xml.attributes().value("version") =="0.91") ){ + while(xml.readNextStartElement()){ + //qDebug() << " - RSS Element:" << xml.name(); + if(xml.name()=="channel"){ rssinfo = readRSSChannel(&xml); } + else{ xml.skipCurrentElement(); } + } + } + } + if(xml.hasError()){ qDebug() << " - XML Read Error:" << xml.errorString() << "\n" << bytes; } + return rssinfo; +} +RSSchannel RSSReader::readRSSChannel(QXmlStreamReader *rss){ + RSSchannel info; + info.timetolive = -1; + while(rss->readNextStartElement()){ + //qDebug() << " - RSS Element (channel):" <name(); + if(rss->name()=="item"){ info.items << readRSSItem(rss); } + else if(rss->name()=="title"){ info.title = rss->readElementText(); } + else if(rss->name()=="link"){ + QString txt = rss->readElementText(); + if(!txt.isEmpty()){ info.link = txt; } + } + else if(rss->name()=="description"){ info.description = rss->readElementText(); } + else if(rss->name()=="lastBuildDate"){ info.lastBuildDate = RSSDateTime(rss->readElementText()); } + else if(rss->name()=="pubDate"){ info.lastPubDate = RSSDateTime(rss->readElementText()); } + else if(rss->name()=="image"){ readRSSImage(&info, rss); } + //else if(rss->name()=="skipHours"){ info.link = rss->readElementText(); } + //else if(rss->name()=="skipDays"){ info.link = rss->readElementText(); } + else if(rss->name()=="ttl"){ info.timetolive = rss->readElementText().toInt(); } + else{ rss->skipCurrentElement(); } + } + return info; +} + +RSSitem RSSReader::readRSSItem(QXmlStreamReader *rss){ + RSSitem item; + while(rss->readNextStartElement()){ + //qDebug() << " - RSS Element (Item):" << rss->name(); + if(rss->name()=="title"){ item.title = rss->readElementText(); } + else if(rss->name()=="link"){ item.link = rss->readElementText(); } + else if(rss->name()=="description"){ item.description = rss->readElementText(); } + else if(rss->name()=="comments"){ item.comments_url = rss->readElementText(); } + else if(rss->name()=="author"){ + //Special handling - this field can contain both email and name + QString raw = rss->readElementText(); + if(raw.contains("@")){ + item.author_email = raw.split(" ").filter("@").first(); + item.author = raw.remove(item.author_email).remove("(").remove(")").simplified(); //the name is often put within parentheses after the email + }else{ item.author = raw; } + } + else if(rss->name()=="guid"){ item.guid = rss->readElementText(); } + else if(rss->name()=="pubDate"){ item.pubdate = RSSDateTime(rss->readElementText()); } + else{ rss->skipCurrentElement(); } + } + return item; +} + +void RSSReader::readRSSImage(RSSchannel *item, QXmlStreamReader *rss){ + while(rss->readNextStartElement()){ + //qDebug() << " - RSS Element (Image):" << rss->name(); + if(rss->name()=="url"){ item->icon_url = rss->readElementText(); } + else if(rss->name()=="title"){ item->icon_title = rss->readElementText(); } + else if(rss->name()=="link"){ item->icon_link = rss->readElementText(); } + else if(rss->name()=="width"){ item->icon_size.setWidth(rss->readElementText().toInt()); } + else if(rss->name()=="height"){ item->icon_size.setHeight(rss->readElementText().toInt()); } + else if(rss->name()=="description"){ item->icon_description = rss->readElementText(); } + } + //Go ahead and kick off the request for the icon + if(!item->icon_url.isEmpty()){ requestRSS(item->icon_url); } +} + +QDateTime RSSReader::RSSDateTime(QString datetime){ + return QDateTime::fromString(datetime, Qt::RFC2822Date); +} + +//================= +// PRIVATE SLOTS +//================= +void RSSReader::replyFinished(QNetworkReply *reply){ + QString url = reply->request().url().toString(); + //qDebug() << "Got Reply:" << url; + QString key = keyForUrl(url); //current hash key for this URL + QByteArray data = reply->readAll(); + outstandingURLS.removeAll(url); + if(data.isEmpty()){ + //qDebug() << "No data returned:" << url; + //see if the URL can be adjusted for known issues + bool handled = false; + QUrl redirecturl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + if(redirecturl.isValid() && (redirecturl.toString() != url )){ + //New URL redirect - make the change and send a new request + QString newurl = redirecturl.toString(); + //qDebug() << " - Redirect to:" << newurl; + if(hash.contains(key) && !hash.contains(newurl)){ + hash.insert(newurl, hash.take(key) ); //just move the data over to the new url + requestRSS(newurl); + emit newChannelsAvailable(); + handled = true; + } + } + if(!handled && hash.contains(key) ){ + emit rssChanged(hash[key].originalURL); + } + return; + } + + if(!hash.contains(key)){ + //qDebug() << " - hash does not contain URL:" << url; + //URL removed from list while a request was outstanding? + //Could also be an icon fetch response + QStringList keys = hash.keys(); + for(int i=0; ideleteLater(); + }else{ + //RSS reply + RSSchannel info = readRSS(data); //QNetworkReply can be used as QIODevice + reply->deleteLater(); //clean up + //Validate the info and announce any changes + if(info.title.isEmpty() || info.link.isEmpty() || info.description.isEmpty()){ + qDebug() << "Missing XML Information:" << url << info.title << info.link << info.description; + return; + } //bad info/read + //Update the bookkeeping elements of the info + if(info.timetolive<=0){ info.timetolive = LSession::handle()->DesktopPluginSettings()->value(setprefix+"default_interval_minutes", 60).toInt(); } + if(info.timetolive <=0){ info.timetolive = 60; } //error in integer conversion from settings? + info.lastsync = QDateTime::currentDateTime(); info.nextsync = info.lastsync.addSecs(info.timetolive * 60); + //Now see if anything changed and save the info into the hash + bool changed = (hash[key].lastBuildDate.isNull() || (hash[key].lastBuildDate < info.lastBuildDate) ); + bool newinfo = false; + if(changed){ newinfo = hash[key].title.isEmpty(); } //no previous info from this URL + info.originalURL = hash[key].originalURL; //make sure this info gets preserved across updates + if(!hash[key].icon.isNull()){ info.icon = hash[key].icon; } //copy over the icon from the previous reply + hash.insert(key, info); + if(newinfo){ emit newChannelsAvailable(); } //new channel + else if(changed){ emit rssChanged(info.originalURL); } //update to existing channel + } +} + +void RSSReader::sslErrors(QNetworkReply *reply, const QList &errors){ + int ok = 0; + qDebug() << "SSL Errors Detected (RSS Reader):" << reply->url(); + for(int i=0; iurl(); reply->ignoreSslErrors(); } + else{ qDebug() << " - Denied:" << reply->url(); } +} + +void RSSReader::checkTimes(){ + if(LSession::handle()->DesktopPluginSettings()->value(setprefix+"manual_sync_only", false).toBool()){ return; } + QStringList urls = hash.keys(); + QDateTime cdt = QDateTime::currentDateTime(); + for(int i=0; i +#include +#include +#include +#include +#include +#include +#include //Contained in the Qt "core" module - don't need the full "xml" module for this +#include + +struct RSSitem{ + //Required Fields + QString title, link, description; + + //Optional Fields + QString comments_url, author_email, author, guid; + QDateTime pubdate; //when the item was published + //IGNORED INFO from RSS2 spec: "category", "source", "enclosure" +}; + +struct RSSchannel{ + //Required fields + QString title, link, description; + + //Optional Fields + QDateTime lastBuildDate, lastPubDate; //last build/publish dates + // - channel refresh information + int timetolive; //in minutes - time until next sync should be performed + //QList skiphours; + //QStringList skipdays; + // - icon info + QIcon icon; + QString icon_url, icon_title, icon_link, icon_description; + QSize icon_size; + //All items within this channel + QList items; + + //Optional RSS2 elements ignored: + // "cloud", "textInput", "rating", "language", "copyright", "managingEditor", "webMaster", + // "category", "generator", "docs" + + //Internal data for bookkeeping + QDateTime lastsync, nextsync; + QString originalURL; //in case it was redirected to some "fixed" url later +}; + +class RSSReader : public QObject{ + Q_OBJECT +public: + RSSReader(QObject *parent, QString settingsPrefix); + ~RSSReader(); + + //Information retrieval + QStringList channels(); //returns all ID's + RSSchannel dataForID(QString ID); + + //Initial setup + void addUrls(QStringList urls); + void removeUrl(QString ID); + +public slots: + void syncNow(); //not generally needed + +private: + //Internal data objects + QHash hash; // ID/data + QString setprefix; + QTimer *syncTimer; + QNetworkAccessManager *NMAN; + QStringList outstandingURLS; + + + //Simple hash data search functions + QString keyForUrl(QString url); + + //Network request function + void requestRSS(QString url); + + //RSS parsing functions + RSSchannel readRSS(QByteArray bytes); + RSSchannel readRSSChannel(QXmlStreamReader *rss); + RSSitem readRSSItem(QXmlStreamReader *rss); + void readRSSImage(RSSchannel *item, QXmlStreamReader *rss); + QDateTime RSSDateTime(QString datetime); + +private slots: + void replyFinished(QNetworkReply *reply); + void sslErrors(QNetworkReply *reply, const QList &errors); + void checkTimes(); + +signals: + void rssChanged(QString); //ID + void newChannelsAvailable(); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.cpp new file mode 100644 index 00000000..951bcc98 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.cpp @@ -0,0 +1,63 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "MonitorWidget.h" +#include "ui_MonitorWidget.h" + + +#include +#include + +MonitorWidget::MonitorWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MonitorWidget()){ + ui->setupUi(this); //load the designer form + upTimer = new QTimer(this); + upTimer->setInterval(2000); //update every 2 seconds + connect(upTimer, SIGNAL(timeout()), this, SLOT(UpdateStats()) ); + LoadIcons(); + upTimer->start(); +} + +MonitorWidget::~MonitorWidget(){ + //qDebug() << "Removing MonitorWidget"; +} + +void MonitorWidget::LoadIcons(){ + ui->tabWidget->setTabIcon(0,LXDG::findIcon("appointment-recurring","") ); //Summary + ui->tabWidget->setTabIcon(1,LXDG::findIcon("drive-harddisk","") ); //Disk Usage + //ui->tabWidget->setTabIcon(1,LXDG::findIcon("cpu","") ); //CPU Log + //ui->tabWidget->setTabIcon(2,LXDG::findIcon("media-flash-memory-stick","") ); //Mem Log +} + +void MonitorWidget::UpdateStats(){ + //qDebug() << "Updating System statistics..."; + ui->label_temps->setText( LOS::CPUTemperatures().join(", ") ); + if(ui->progress_cpu->isEnabled()){ + int perc = LOS::CPUUsagePercent(); + ui->progress_cpu->setValue(perc); + if(perc<0){ ui->progress_cpu->setEnabled(false); } //disable this for future checks + } + if(ui->progress_mem->isEnabled()){ + int perc = LOS::MemoryUsagePercent(); + ui->progress_mem->setValue(perc); + if(perc<0){ ui->progress_mem->setEnabled(false); } //disable this for future checks + } + ui->label_diskinfo->setText( LOS::DiskUsage().join("\n") ); + //Also perform/update the logs as necessary + // -- TO DO -- +} + +SysMonitorPlugin::SysMonitorPlugin(QWidget *parent, QString ID) : LDPlugin(parent, ID){ + monitor = new MonitorWidget(this); + this->setLayout( new QVBoxLayout() ); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(monitor); + + //this->setInitialSize(monitor->sizeHint().width(),monitor->sizeHint().height()); +} + +SysMonitorPlugin::~SysMonitorPlugin(){ + //qDebug() << "Remove SysMonitorPlugin"; +} \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.h new file mode 100644 index 00000000..618ac8f4 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.h @@ -0,0 +1,62 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a simple hardware status monitor on the desktop +//=========================================== +#ifndef _LUMINA_DESKTOP_PLUGIN_HW_MONITOR_WIDGET_H +#define _LUMINA_DESKTOP_PLUGIN_HW_MONITOR_WIDGET_H + +#include +#include + +#include "../LDPlugin.h" + +namespace Ui{ + class MonitorWidget; +}; + +class MonitorWidget : public QWidget{ + Q_OBJECT +public: + MonitorWidget(QWidget *parent = 0); + ~MonitorWidget(); + +public slots: + void LoadIcons(); + +private: + Ui::MonitorWidget *ui; + QTimer *upTimer; + +private slots: + void UpdateStats(); +}; + +// Wrapper class to put this into a desktop plugin container +class SysMonitorPlugin : public LDPlugin{ + Q_OBJECT +public: + SysMonitorPlugin(QWidget* parent, QString ID); + ~SysMonitorPlugin(); + + virtual QSize defaultPluginSize(){ + // The returned QSize is in grid points (typically 100 or 200 pixels square) + return QSize(3,2); + } + +private: + MonitorWidget *monitor; + +public slots: + void LocaleChange(){ + QTimer::singleShot(0,monitor, SLOT(LoadIcons())); + } + void ThemeChange(){ + QTimer::singleShot(0,monitor, SLOT(LoadIcons())); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.ui b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.ui new file mode 100644 index 00000000..8798bc25 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/desktop-plugins/systemmonitor/MonitorWidget.ui @@ -0,0 +1,143 @@ + + + MonitorWidget + + + + 0 + 0 + 300 + 122 + + + + Form + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 0 + + + + Summary + + + + + + CPU Temp: + + + + + + + + + + + + + + CPU Usage: + + + + + + + 0 + + + + + + + Mem Usage: + + + + + + + 0 + + + + + + + + Disk I/O + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 292 + 89 + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LPPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LPPlugin.h new file mode 100644 index 00000000..c4c76297 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LPPlugin.h @@ -0,0 +1,77 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the generic container layout for all panel plugins +// Simply subclass this when creating a new plugin to enable correct +// visibility and usage within a panel +//=========================================== +#ifndef _LUMINA_DESKTOP_PANEL_PLUGIN_H +#define _LUMINA_DESKTOP_PANEL_PLUGIN_H + +#include +#include +#include +#include +#include + +class LPPlugin : public QWidget{ + Q_OBJECT + +private: + QBoxLayout *LY; + QString plugintype; + +public: + LPPlugin(QWidget *parent = 0, QString ptype="unknown", bool horizontal = true) : QWidget(parent){ + plugintype=ptype; + this->setContentsMargins(1,1,1,1); + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + this->setFocusPolicy(Qt::NoFocus); //no keyboard focus on the panel/plugins + if(horizontal){LY = new QBoxLayout(QBoxLayout::LeftToRight, this); } + else{ LY = new QBoxLayout(QBoxLayout::TopToBottom, this); } + this->setObjectName(ptype.section("---",0,0)); + LY->setContentsMargins(0,0,0,0); + LY->setSpacing(1); + this->setLayout(LY); + connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(LocaleChange()) ); + connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(ThemeChange()) ); + } + + ~LPPlugin(){ + } + + QBoxLayout* layout(){ + return LY; + } + + QString type(){ + return plugintype; + } + + virtual void AboutToClose(){ + //This needs to be re-implemented in the subclasses plugin + //This is for any last-minute cleanup before the plugin gets deleted + } + +public slots: + virtual void LocaleChange(){ + //This needs to be re-implemented in the subclassed plugin + //This is where all text is set/translated + } + virtual void ThemeChange(){ + //This needs to be re-implemented in the subclasses plugin + //This is where all the visuals are set if using Theme-dependant icons. + } + virtual void OrientationChange(){ + //This needs to be re-implemented in the subclasses plugin + //This is where any horizontal/vertical orientations can be changed appropriately + } + +signals: + void MenuClosed(); //This needs to be emitted when any plugin's menu is closed for some reason (check/set focus properly) +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LTBWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LTBWidget.h new file mode 100644 index 00000000..560e5811 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/LTBWidget.h @@ -0,0 +1,71 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2013, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_TOOLBAR_WIDGET_H +#define _LUMINA_TOOLBAR_WIDGET_H + +#include +#include +#include + +#include "Globals.h" +#include + +class LTBWidget : public QToolButton{ + Q_OBJECT + +private: + LXCB::WINDOWVISIBILITY cstate; + //QString rawstyle; + void updateBackground(){ + //QString background = "background: transparent; "; //default value + //QString border = "border: 1px solid transparent;"; + if(cstate == LXCB::IGNORE){ this->setObjectName(""); } //just use the defaults + else if(cstate == LXCB::VISIBLE){ this->setObjectName("WindowVisible"); }//background = "background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(255, 255, 255, 240), stop:0.505682 rgba(240, 240, 240, 150), stop:1 rgba(210, 210, 210, 55));"; border="border: 1px solid transparent;"; } + else if(cstate == LXCB::INVISIBLE){this->setObjectName("WindowInvisible"); } //background = "background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(215, 215, 215, 240), stop:0.505682 rgba(184, 185, 186, 150), stop:1 rgba(221, 246, 255, 55));"; border="border: 1px solid transparent;"; } + else if(cstate == LXCB::ACTIVE){ this->setObjectName("WindowActive"); }//background= "background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(241, 233, 156, 240), stop:0.355682 rgba(255, 243, 127, 150), stop:1 rgba(221, 246, 255, 55));"; border ="border: 1px solid transparent;"; } + else if(cstate == LXCB::ATTENTION){ this->setObjectName("WindowAttention"); }//background= "background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(252, 187, 127, 240), stop:0.505682 rgba(255, 222, 197, 150), stop:1 rgba(221, 246, 255, 55));"; border="border: 1px solid transparent;"; } + this->setStyleSheet(""); //force the object to re-evaluate the current theme stylesheet and update visuals + //QString raw = rawstyle; + //this->setStyleSheet( raw.replace("%1",background).replace("%2", border) ); + } + +signals: + + void wheelScroll(int change); + +public: + LTBWidget(QWidget* parent) : QToolButton(parent){ + //this->setStyleSheet( this->styleSheet()+" LTBWidget::menu-indicator{image: none;}"); + cstate = LXCB::IGNORE; + + this->setPopupMode(QToolButton::InstantPopup); + this->setAutoRaise(true); + + //rawstyle = "LTBWidget{ %1 %2 border-radius: 5px;} LTBWidget::menu-indicator{image: none;} LTBWidget::hover{ %1 border: 1px solid black; border-radius: 5px; } LTBWidget::menu-button{ background: transparent; width: 15px; } LTBWidget[popupMode=\"1\"]{%1 %2 border-radius: 5px; padding-right: 15px;} LTBWidget[popupMode=\"1\"]::hover{%1 border: 1px solid black; border-radius: 5px; padding-right: 15px}"; + updateBackground(); + } + + ~LTBWidget(){ + } + + void setState(LXCB::WINDOWVISIBILITY newstate){ + cstate = newstate; + updateBackground(); + } + +public slots: + + +protected: + void wheelEvent(QWheelEvent *event){ + int change = event->delta()/120; // 1/15th of a rotation (delta/120) is usually one "click" of the wheel + emit wheelScroll(change); + } + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/NewPP.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/NewPP.h new file mode 100644 index 00000000..3a5f6a5b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/NewPP.h @@ -0,0 +1,82 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is the interface to load all the different panel plugins +//=========================================== +#ifndef _LUMINA_DESKTOP_NEW_PANEL_PLUGIN_H +#define _LUMINA_DESKTOP_NEW_PANEL_PLUGIN_H + +#include + +//List all the individual plugin includes here +#include "LPPlugin.h" +#include "userbutton/LUserButton.h" +#include "desktopbar/LDeskBar.h" +#include "spacer/LSpacer.h" +#include "line/LLine.h" +#include "clock/LClock.h" +#include "battery/LBattery.h" +#include "desktopswitcher/LDesktopSwitcher.h" +#include "taskmanager/LTaskManagerPlugin.h" +#include "systemdashboard/LSysDashboard.h" +#include "showdesktop/LHomeButton.h" +#include "appmenu/LAppMenuPlugin.h" +#include "applauncher/AppLaunchButton.h" +#include "systemstart/LStartButton.h" +#include "audioplayer/LPAudioPlayer.h" +#include "jsonmenu/PPJsonMenu.h" +//#include "quickcontainer/QuickPPlugin.h" +#include "systemtray/LSysTray.h" //must be last due to X11 compile issues + + +class NewPP{ +public: + static LPPlugin* createPlugin(QString plugin, QWidget* parent = 0, bool horizontal = true){ + LPPlugin *plug = 0; + if(plugin.startsWith("userbutton---")){ + plug = new LUserButtonPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("homebutton---")){ + plug = new LHomeButtonPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("desktopbar---")){ + plug = new LDeskBarPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("spacer---")){ + plug = new LSpacerPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("line---")){ + plug = new LLinePlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("taskmanager")){ + //This one can be "taskmanager[-nogroups]---" + plug = new LTaskManagerPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("systemtray---")){ + plug = new LSysTray(parent, plugin, horizontal); + }else if(plugin.startsWith("desktopswitcher---")){ + plug = new LDesktopSwitcher(parent, plugin, horizontal); + }else if(plugin.startsWith("battery---") && LOS::hasBattery()){ + plug = new LBattery(parent, plugin, horizontal); + }else if(plugin.startsWith("clock---")){ + plug = new LClock(parent, plugin, horizontal); + }else if(plugin.startsWith("systemdashboard---")){ + plug = new LSysDashboard(parent, plugin, horizontal); + }else if(plugin.startsWith("appmenu---")){ + plug = new LAppMenuPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("systemstart---")){ + plug = new LStartButtonPlugin(parent, plugin, horizontal); + }else if(plugin.startsWith("audioplayer---")){ + plug = new LPAudioPlayer(parent, plugin, horizontal); + }else if(plugin.section("::::",0,0)=="jsonmenu" && plugin.split("::::").length()>=3 ){ + plug = new LPJsonMenu(parent, plugin, horizontal); + }else if(plugin.section("---",0,0).section("::",0,0)=="applauncher"){ + plug = new AppLaunchButtonPlugin(parent, plugin, horizontal); + //}else if( plugin.section("---",0,0).startsWith("quick-") && LUtils::validQuickPlugin(plugin.section("---",0,0)) ){ + //plug = new QuickPPlugin(parent, plugin, horizontal); + }else{ + qWarning() << "Invalid Panel Plugin:"< +#include +#include +#include + +class RotateToolButton : public QToolButton{ + Q_OBJECT + +private: + int rotate_degrees; + void paintEvent(QPaintEvent*){ + /* NOTE: This is what a standard QToolButton performs (peeked at Qt source code for this tidbit) + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + p.drawComplexControl(QStyle::CC_ToolButton, opt); + */ + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + //Apply the rotation matrix to the painter before starting the paint + QTransform trans = QTransform( p.transform() ).rotate(rotate_degrees); + p.setTransform(trans, false); //merging already taken care of + //Now do the normal painting procedure + p.drawComplexControl(QStyle::CC_ToolButton, opt); + } + +public: + RotateToolButton(QWidget *parent = Q_NULLPTR) : QToolButton(parent){ + rotate_degrees = 0; //no rotation initially + } + + void setRotation(int degrees){ + rotate_degrees = degrees; + this->update(); //trigger a paint event + } + + /*virtual void setText(QString text){ + this->setText(text); + if(rotate_degrees !=0){ + this->setSizeHint( this->sizeHint() + } + }*/ +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.cpp new file mode 100644 index 00000000..1fd819b5 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.cpp @@ -0,0 +1,77 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "AppLaunchButton.h" +#include "../../LSession.h" + +#include +#include +#include + +AppLaunchButtonPlugin::AppLaunchButtonPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + appfile = id.section("---",0,0).section("::",1,1); + if(!QFile::exists(appfile) && appfile.endsWith(".desktop")){ + //might be a relative path - try to find the file + appfile = LUtils::AppToAbsolute(appfile.section("/",-1) ); + } + if(!QFile::exists(appfile)){ appfile.clear(); } + connect(button, SIGNAL(clicked()), this, SLOT(AppClicked())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes +} + +AppLaunchButtonPlugin::~AppLaunchButtonPlugin(){ + +} + +void AppLaunchButtonPlugin::updateButtonVisuals(){ + QIcon icon; + QString tooltip = tr("Click to assign an application"); + if(appfile.endsWith(".desktop")){ + XDGDesktop desk(appfile); + if(desk.isValid()){ + icon = LXDG::findIcon(desk.icon, "unknown"); + tooltip = QString(tr("Launch %1")).arg(desk.name); + }else{ + icon = LXDG::findIcon("task-attention",""); + appfile.clear(); + } + }else if(QFile::exists(appfile)){ + icon = LXDG::findMimeIcon(appfile.section("/",-1)); + tooltip = QString(tr("Open %1")).arg(appfile.section("/",-1)); + }else{ + icon = LXDG::findIcon("task-attention", ""); + } + button->setIcon( icon ); + button->setToolTip(tooltip); +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void AppLaunchButtonPlugin::AppClicked(){ + if(appfile.isEmpty()){ + //No App File selected + QList apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); + QStringList names; + for(int i=0; iname; } + bool ok = false; + QString app = QInputDialog::getItem(this, tr("Select Application"), tr("Name:"), names, 0, false, &ok); + if(!ok || names.indexOf(app)<0){ return; } //cancelled + appfile = apps[ names.indexOf(app) ]->filePath; + //Still need to find a way to set this value persistently + // --- perhaps replace the plugin in the desktop settings file with the new path? + // --- "applauncher::broken---" -> "applauncher::fixed---" ? + QTimer::singleShot(0,this, SLOT(updateButtonVisuals())); + }else{ + LSession::LaunchApplication("lumina-open \""+appfile+"\""); + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.h new file mode 100644 index 00000000..3aa3c7ad --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/applauncher/AppLaunchButton.h @@ -0,0 +1,63 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin is a simple button to launch a single application +//=========================================== +#ifndef _LUMINA_DESKTOP_LAUNCH_APP_PANEL_PLUGIN_H +#define _LUMINA_DESKTOP_LAUNCH_APP_PANEL_PLUGIN_H + +// Qt includes +#include +#include +#include + + +// Lumina-desktop includes +#include "../LPPlugin.h" //main plugin widget + +// libLumina includes +#include "LuminaXDG.h" + +// PANEL PLUGIN BUTTON +class AppLaunchButtonPlugin : public LPPlugin{ + Q_OBJECT + +public: + AppLaunchButtonPlugin(QWidget *parent = 0, QString id = "applauncher", bool horizontal=true); + ~AppLaunchButtonPlugin(); + +private: + QToolButton *button; + QString appfile; + + void updateButtonVisuals(); + +private slots: + void AppClicked(); + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + this->layout()->update(); + updateButtonVisuals(); + } + + void LocaleChange(){ + updateButtonVisuals(); + } + + void ThemeChange(){ + updateButtonVisuals(); + } +}; + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.cpp new file mode 100644 index 00000000..e3be55c2 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.cpp @@ -0,0 +1,142 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LAppMenuPlugin.h" +#include "../../LSession.h" + +#include + +LAppMenuPlugin::LAppMenuPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + mainmenu = new QMenu(this); + button->setMenu( mainmenu ); + button->setPopupMode(QToolButton::InstantPopup); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + + connect(mainmenu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + connect(mainmenu, SIGNAL(triggered(QAction*)), this, SLOT(LaunchItem(QAction*)) ); + connect(LSession::handle()->applicationMenu(), SIGNAL(AppMenuUpdated()), this, SLOT(UpdateMenu())); + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes + QTimer::singleShot(0,this, SLOT(UpdateMenu()) ); + //Setup the global shortcut handling for opening the start menu + connect(QApplication::instance(), SIGNAL(StartButtonActivated()), this, SLOT(shortcutActivated()) ); + LSession::handle()->registerStartButton(this->type()); +} + +LAppMenuPlugin::~LAppMenuPlugin(){ + +} + +void LAppMenuPlugin::updateButtonVisuals(){ + button->setToolTip( tr("Quickly launch applications or open files")); + button->setText( tr("Applications") ); + //Use the TrueOS icon by default (or the Lumina icon for non-TrueOS systems) + button->setIcon( LXDG::findIcon("start-here-lumina","Lumina-DE") ); +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void LAppMenuPlugin::shortcutActivated(){ + if(LSession::handle()->registerStartButton(this->type())){ + if(button->menu()->isVisible()){ button->menu()->hide(); } + else{ button->showMenu(); } + } +} + +void LAppMenuPlugin::LaunchItem(QAction* item){ + QString appFile = item->whatsThis(); + if(appFile.startsWith("internal::")){ + appFile = appFile.section("::",1,50); //cut off the "internal" flag + if(appFile=="logout"){ LSession::handle()->systemWindow(); } + }else if(!appFile.isEmpty()){ + LSession::LaunchApplication("lumina-open "+appFile); + } +} + +void LAppMenuPlugin::UpdateMenu(){ + mainmenu->clear(); + QHash > *HASH = LSession::handle()->applicationMenu()->currentAppHash(); + //Now Re-create the menu (orignally copied from the AppMenu class) + //Add link to the file manager + QAction *tmpact = mainmenu->addAction( LXDG::findIcon("user-home", ""), tr("Browse Files") ); + tmpact->setWhatsThis("\""+QDir::homePath()+"\""); + //--Look for the app store + XDGDesktop store(LOS::AppStoreShortcut()); + if(store.isValid()){ + tmpact = mainmenu->addAction( LXDG::findIcon(store.icon, ""), tr("Install Applications") ); + tmpact->setWhatsThis("\""+store.filePath+"\""); + } + //--Look for the control panel + XDGDesktop controlp(LOS::ControlPanelShortcut()); + if(controlp.isValid()){ + tmpact = mainmenu->addAction( LXDG::findIcon(controlp.icon, ""), tr("Control Panel") ); + tmpact->setWhatsThis("\""+controlp.filePath+"\""); + } + mainmenu->addSeparator(); + //--Now create the sub-menus + QStringList cats = HASH->keys(); + cats.sort(); //make sure they are alphabetical + for(int i=0; isetIcon(LXDG::findIcon(icon,"")); + QList appL = HASH->value(cats[i]); + for( int a=0; aactions.isEmpty()){ + //Just a single entry point - no extra actions + QAction *act = new QAction(LXDG::findIcon(appL[a]->icon, ""), appL[a]->name, menu); + act->setToolTip(appL[a]->comment); + act->setWhatsThis("\""+appL[a]->filePath+"\""); + menu->addAction(act); + }else{ + //This app has additional actions - make this a sub menu + // - first the main menu/action + QMenu *submenu = new QMenu(appL[a]->name, menu); + submenu->setIcon( LXDG::findIcon(appL[a]->icon,"") ); + //This is the normal behavior - not a special sub-action (although it needs to be at the top of the new menu) + QAction *act = new QAction(LXDG::findIcon(appL[a]->icon, ""), appL[a]->name, submenu); + act->setToolTip(appL[a]->comment); + act->setWhatsThis(appL[a]->filePath); + submenu->addAction(act); + //Now add entries for every sub-action listed + for(int sa=0; saactions.length(); sa++){ + QAction *sact = new QAction(LXDG::findIcon(appL[a]->actions[sa].icon, appL[a]->icon), appL[a]->actions[sa].name, this); + sact->setToolTip(appL[a]->comment); + sact->setWhatsThis("-action \""+appL[a]->actions[sa].ID+"\" \""+appL[a]->filePath+"\""); + submenu->addAction(sact); + } + menu->addMenu(submenu); + } + }//end loop over apps within this category + mainmenu->addMenu(menu); + } //end loop over categories + //Now add any logout options + mainmenu->addSeparator(); + //QMenu *tmpmenu = mainmenu->addMenu(LXDG::findIcon("system-log-out",""), tr("Leave")); + tmpact =mainmenu->addAction(LXDG::findIcon("system-log-out"),tr("Leave")); + tmpact->setWhatsThis("internal::logout"); + +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.h new file mode 100644 index 00000000..659d781f --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/appmenu/LAppMenuPlugin.h @@ -0,0 +1,64 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin is a re-creation of the classic "start" menu +//=========================================== +#ifndef _LUMINA_DESKTOP_APP_MENU_PANEL_PLUGIN_H +#define _LUMINA_DESKTOP_APP_MENU_PANEL_PLUGIN_H + +// Qt includes +#include +#include +#include +#include + + +// Lumina-desktop includes +#include "../LPPlugin.h" //main plugin widget + + +// PANEL PLUGIN BUTTON +class LAppMenuPlugin : public LPPlugin{ + Q_OBJECT + +public: + LAppMenuPlugin(QWidget *parent = 0, QString id = "appmenu", bool horizontal=true); + ~LAppMenuPlugin(); + +private: + QToolButton *button; + QMenu *mainmenu; + + void updateButtonVisuals(); + +private slots: + void shortcutActivated(); + void LaunchItem(QAction* item); + void UpdateMenu(); + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + this->layout()->update(); + updateButtonVisuals(); + } + + void LocaleChange(){ + updateButtonVisuals(); + } + + void ThemeChange(){ + updateButtonVisuals(); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.cpp new file mode 100644 index 00000000..5669aaf5 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.cpp @@ -0,0 +1,30 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Susanne Jaeckel +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LPAudioPlayer.h" +#include "LSession.h" + +LPAudioPlayer::LPAudioPlayer(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + //Setup the button + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setPopupMode(QToolButton::InstantPopup); //make sure it runs the update routine first + //connect(button, SIGNAL(clicked()), this, SLOT(openMenu())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + wact = new QWidgetAction(this); + aplayer = new PPlayerWidget(this); + button ->setMenu(new QMenu(this) ); + wact->setDefaultWidget(aplayer); + button->menu()->addAction(wact); + //Now start up the widgets + button->setIcon( LXDG::findIcon("audio-volume-high","") ); + QTimer::singleShot(0,this,SLOT(OrientationChange()) ); //update the sizing/icon +} + +LPAudioPlayer::~LPAudioPlayer(){ +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.h new file mode 100644 index 00000000..e5132b1f --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/LPAudioPlayer.h @@ -0,0 +1,49 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_PANEL_AUDIO_PLAYER_PLUGIN_H +#define _LUMINA_PANEL_AUDIO_PLAYER_PLUGIN_H + +#include "../../Globals.h" +#include "../LTBWidget.h" +#include "../LPPlugin.h" +#include "PPlayerWidget.h" + +class LPAudioPlayer : public LPPlugin{ + Q_OBJECT +public: + LPAudioPlayer(QWidget *parent = 0, QString id = "audioplayer", bool horizontal=true); + ~LPAudioPlayer(); + +private: + QToolButton *button; + QWidgetAction *wact; + PPlayerWidget *aplayer; + + //int iconOld; + +private slots: + //void updateBattery(bool force = false); + //QString getRemainingTime(); + +public slots: + void LocaleChange(){ + //updateBattery(true); + } + + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + this->layout()->update(); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.cpp new file mode 100644 index 00000000..023e20c7 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.cpp @@ -0,0 +1,258 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "PPlayerWidget.h" +#include "ui_PPlayerWidget.h" + +#include +#include +#include +#include +#include +#include +#include + +PPlayerWidget::PPlayerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::PPlayerWidget()){ + ui->setupUi(this); //load the designer form + PLAYER = new QMediaPlayer(this); + PLAYER->setVolume(100); + PLAYER->setNotifyInterval(1000); //1 second interval (just needs to be a rough estimate) + PLAYLIST = new QMediaPlaylist(this); + PLAYLIST->setPlaybackMode(QMediaPlaylist::Sequential); + PLAYER->setPlaylist(PLAYLIST); + + configMenu = new QMenu(this); + ui->tool_config->setMenu(configMenu); + addMenu = new QMenu(this); + ui->tool_add->setMenu(addMenu); + + updatinglists = false; //start off as false + + ui->combo_playlist->setContextMenuPolicy(Qt::NoContextMenu); + + LoadIcons(); + playerStateChanged(); //update button visibility + currentSongChanged(); + //Connect all the signals/slots + //connect(infoTimer, SIGNAL(timeout()), this, SLOT(rotateTrackInfo()) ); + connect(PLAYER, SIGNAL(positionChanged(qint64)),this, SLOT(updateProgress(qint64)) ); + connect(PLAYER, SIGNAL(durationChanged(qint64)), this, SLOT(updateMaxProgress(qint64)) ); + connect(PLAYLIST, SIGNAL(mediaChanged(int, int)), this, SLOT(playlistChanged()) ); + connect(PLAYER, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(playerStateChanged()) ); + connect(PLAYLIST, SIGNAL(currentMediaChanged(const QMediaContent&)), this, SLOT(currentSongChanged()) ); + connect(ui->combo_playlist, SIGNAL(currentIndexChanged(int)), this, SLOT(userlistSelectionChanged()) ); + connect(ui->tool_play, SIGNAL(clicked()), this, SLOT(playClicked()) ); + connect(ui->tool_pause, SIGNAL(clicked()), this, SLOT(pauseClicked()) ); + connect(ui->tool_stop, SIGNAL(clicked()), this, SLOT(stopClicked()) ); + connect(ui->tool_next, SIGNAL(clicked()), this, SLOT(nextClicked()) ); + connect(ui->tool_prev, SIGNAL(clicked()), this, SLOT(prevClicked()) ); + +} + +PPlayerWidget::~PPlayerWidget(){ + //qDebug() << "Removing PPlayerWidget"; +} + +void PPlayerWidget::LoadIcons(){ + ui->tool_stop->setIcon( LXDG::findIcon("media-playback-stop","") ); + ui->tool_play->setIcon( LXDG::findIcon("media-playback-start","") ); + ui->tool_pause->setIcon( LXDG::findIcon("media-playback-pause","") ); + ui->tool_next->setIcon( LXDG::findIcon("media-skip-forward","") ); + ui->tool_prev->setIcon( LXDG::findIcon("media-skip-backward","") ); + ui->tool_add->setIcon( LXDG::findIcon("list-add","") ); + ui->tool_config->setIcon( LXDG::findIcon("configure","") ); + //Now re-assemble the menus as well + configMenu->clear(); + configMenu->addAction(LXDG::findIcon("media-eject",""), tr("Clear Playlist"), this, SLOT(ClearPlaylist())); + configMenu->addAction(LXDG::findIcon("roll",""), tr("Shuffle Playlist"), this, SLOT(ShufflePlaylist())); + addMenu->clear(); + addMenu->addAction(LXDG::findIcon("document-new",""), tr("Add Files"), this, SLOT(AddFilesToPlaylist())); + addMenu->addAction(LXDG::findIcon("folder-new",""), tr("Add Directory"), this, SLOT(AddDirToPlaylist())); + addMenu->addAction(LXDG::findIcon("download",""), tr("Add URL"), this, SLOT(AddURLToPlaylist())); +} + +void PPlayerWidget::playClicked(){ + PLAYER->play(); +} + +void PPlayerWidget::pauseClicked(){ + PLAYER->pause(); +} + +void PPlayerWidget::stopClicked(){ + PLAYER->stop(); +} + +void PPlayerWidget::nextClicked(){ + PLAYLIST->next(); +} + +void PPlayerWidget::prevClicked(){ + PLAYLIST->previous(); +} + +void PPlayerWidget::AddFilesToPlaylist(){ + //Prompt the user to select multimedia files + QFileDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setFileMode(QFileDialog::ExistingFiles); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setNameFilter( tr("Multimedia Files")+" ("+LXDG::findAVFileExtensions().join(" ")+")"); + dlg.setWindowTitle(tr("Select Multimedia Files")); + dlg.setWindowIcon( LXDG::findIcon("file-open","") ); + dlg.setDirectory(QDir::homePath()); //start in the home directory + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + QList files = dlg.selectedUrls(); + if(files.isEmpty() || dlg.result()!=QDialog::Accepted){ return; } //cancelled + //Make this use show/processEvents later + //QList files = QFileDialog::getOpenFileUrls(0, tr("Select Multimedia Files"), QDir::homePath(), "Multimedia Files ("+LXDG::findAVFileExtensions().join(" ")+")"); + QList urls; + for(int i=0; iaddMedia(urls); + playlistChanged(); +} + +void PPlayerWidget::AddDirToPlaylist(){ + QFileDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setFileMode(QFileDialog::Directory); + dlg.setOption(QFileDialog::ShowDirsOnly, true); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setWindowTitle(tr("Select Multimedia Directory")); + dlg.setWindowIcon( LXDG::findIcon("folder-open","") ); + dlg.setDirectory(QDir::homePath()); //start in the home directory + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + if(dlg.result() != QDialog::Accepted){ return; } //cancelled + QStringList sel = dlg.selectedFiles(); + if(sel.isEmpty()){ return; } //cancelled + QString dirpath = sel.first(); //QFileDialog::getExistingDirectory(0, tr("Select a Multimedia Directory"), QDir::homePath() ); + if(dirpath.isEmpty()){ return; } //cancelled + QDir dir(dirpath); + QFileInfoList files = dir.entryInfoList(LXDG::findAVFileExtensions(), QDir::Files | QDir::NoDotAndDotDot, QDir::Name); + if(files.isEmpty()){ return; } //nothing in this directory + QList urls; + for(int i=0; iaddMedia(urls); + playlistChanged(); +} + +void PPlayerWidget::AddURLToPlaylist(){ + QInputDialog dlg(0, Qt::Dialog | Qt::WindowStaysOnTopHint ); + dlg.setInputMode(QInputDialog::TextInput); + dlg.setLabelText(tr("Enter a valid URL for a multimedia file or stream:")); + dlg.setTextEchoMode(QLineEdit::Normal); + dlg.setWindowTitle(tr("Multimedia URL")); + dlg.setWindowIcon( LXDG::findIcon("download","") ); + //ensure it is centered on the current screen + QPoint center = QApplication::desktop()->screenGeometry(this).center(); + dlg.move( center.x()-(dlg.width()/2), center.y()-(dlg.height()/2) ); + dlg.show(); + while( dlg.isVisible() ){ + QApplication::processEvents(); + } + QString url = dlg.textValue(); + if(url.isEmpty() || dlg.result()!=QDialog::Accepted){ return; } //cancelled + + //QString url = QInputDialog::getText(0, tr("Multimedia URL"), tr("Enter a valid URL for a multimedia file or stream"), QLineEdit::Normal); + //if(url.isEmpty()){ return; } + QUrl newurl(url); + if(!newurl.isValid()){ return; } //invalid URL + PLAYLIST->addMedia(newurl); + playlistChanged(); +} + +void PPlayerWidget::ClearPlaylist(){ + PLAYER->stop(); + PLAYLIST->clear(); + playlistChanged(); +} + +void PPlayerWidget::ShufflePlaylist(){ + PLAYLIST->shuffle(); +} + + +void PPlayerWidget::userlistSelectionChanged(){ //front-end combobox was changed by the user + if(updatinglists){ return; } + PLAYLIST->setCurrentIndex( ui->combo_playlist->currentIndex() ); +} + +void PPlayerWidget::playerStateChanged(){ + switch( PLAYER->state() ){ + case QMediaPlayer::StoppedState: + ui->tool_stop->setVisible(false); + ui->tool_play->setVisible(true); + ui->tool_pause->setVisible(false); + ui->progressBar->setVisible(false); + break; + case QMediaPlayer::PausedState: + ui->tool_stop->setVisible(true); + ui->tool_play->setVisible(true); + ui->tool_pause->setVisible(false); + ui->progressBar->setVisible(true); + break; + case QMediaPlayer::PlayingState: + ui->tool_stop->setVisible(true); + ui->tool_play->setVisible(false); + ui->tool_pause->setVisible(true); + ui->progressBar->setVisible(true); + break; + } + +} + +void PPlayerWidget::playlistChanged(){ + updatinglists = true; + ui->combo_playlist->clear(); + for(int i=0; imediaCount(); i++){ + QUrl url = PLAYLIST->media(i).canonicalUrl(); + if(url.isLocalFile()){ + ui->combo_playlist->addItem(LXDG::findMimeIcon(url.fileName().section(".",-1)), url.fileName() ); + }else{ + ui->combo_playlist->addItem(LXDG::findIcon("download",""), url.toString() ); + } + } + if(PLAYLIST->currentIndex()<0 && PLAYLIST->mediaCount()>0){ PLAYLIST->setCurrentIndex(0); } + ui->combo_playlist->setCurrentIndex(PLAYLIST->currentIndex()); + + updatinglists = false; +} + +void PPlayerWidget::currentSongChanged(){ + if(PLAYLIST->currentIndex() != ui->combo_playlist->currentIndex()){ + updatinglists = true; + ui->combo_playlist->setCurrentIndex(PLAYLIST->currentIndex()); + updatinglists = false; + } + ui->tool_next->setEnabled( PLAYLIST->nextIndex() >= 0 ); + ui->tool_prev->setEnabled( PLAYLIST->previousIndex() >= 0); + ui->label_num->setText( QString::number( PLAYLIST->currentIndex()+1)+"/"+QString::number(PLAYLIST->mediaCount()) ); + ui->progressBar->setRange(0, PLAYER->duration() ); + ui->progressBar->setValue(0); +} + +void PPlayerWidget::updateProgress(qint64 val){ + //qDebug() << "Update Progress Bar:" << val; + ui->progressBar->setValue(val); +} + +void PPlayerWidget::updateMaxProgress(qint64 val){ + ui->progressBar->setRange(0,val); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.h new file mode 100644 index 00000000..a551d74f --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.h @@ -0,0 +1,59 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin is a simple audio player on the desktop +//=========================================== +#ifndef _LUMINA_PANEL_PLUGIN_AUDIO_PLAYER_WIDGET_H +#define _LUMINA_PANEL_PLUGIN_AUDIO_PLAYER_WIDGET_H + +#include +#include +#include +#include +#include + + +namespace Ui{ + class PPlayerWidget; +}; + +class PPlayerWidget : public QWidget{ + Q_OBJECT +public: + PPlayerWidget(QWidget *parent = 0); + ~PPlayerWidget(); + +public slots: + void LoadIcons(); + +private: + Ui::PPlayerWidget *ui; + QMediaPlaylist *PLAYLIST; + QMediaPlayer *PLAYER; + QMenu *configMenu, *addMenu; + bool updatinglists; + +private slots: + void playClicked(); + void pauseClicked(); + void stopClicked(); + void nextClicked(); + void prevClicked(); + + void AddFilesToPlaylist(); + void AddDirToPlaylist(); + void AddURLToPlaylist(); + void ClearPlaylist(); + void ShufflePlaylist(); + void userlistSelectionChanged(); //front-end combobox was changed by the user + void playerStateChanged(); + void playlistChanged(); //list of items changed + void currentSongChanged(); + void updateProgress(qint64 val); + void updateMaxProgress(qint64 val); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.ui b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.ui new file mode 100644 index 00000000..2d2450be --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/audioplayer/PPlayerWidget.ui @@ -0,0 +1,182 @@ + + + PPlayerWidget + + + + 0 + 0 + 346 + 90 + + + + Form + + + QToolButton::menu-indicator{ image: none; } + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Config + + + QToolButton::InstantPopup + + + true + + + + + + + Add + + + QToolButton::InstantPopup + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + prev + + + true + + + + + + + 1/10 + + + + + + + next + + + true + + + + + + + + + + + + + + Play + + + true + + + + + + + Pause + + + true + + + + + + + Stop + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 24 + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.cpp new file mode 100644 index 00000000..ee379613 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.cpp @@ -0,0 +1,115 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Susanne Jaeckel, 2015-2016 Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LBattery.h" +#include "LSession.h" + +LBattery::LBattery(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + iconOld = -1; + //Setup the widget + label = new QLabel(this); + label->setScaledContents(true); + //label->setAlignment(Qt::AlignCenter); + this->layout()->addWidget(label); + //Setup the timer + timer = new QTimer(); + timer->setInterval(5000); //update every 5 seconds + connect(timer,SIGNAL(timeout()), this, SLOT(updateBattery()) ); + timer->start(); + QTimer::singleShot(0,this,SLOT(OrientationChange()) ); //update the sizing/icon +} + +LBattery::~LBattery(){ + timer->stop(); + delete timer; +} + +void LBattery::updateBattery(bool force){ + // Get current state of charge + //QStringList result = LUtils::getCmdOutput("/usr/sbin/apm", QStringList() << "-al"); + int charge = LOS::batteryCharge(); //result.at(1).toInt(); +//qDebug() << "1: " << result.at(0).toInt() << " 2: " << result.at(1).toInt(); + int icon = -1; + if (charge > 90) { icon = 4; } + else if (charge > 70) { icon = 3; } + else if (charge > 20) { icon = 2; } + else if (charge > 5) { icon = 1; } + else if (charge > 0 ) { icon = 0; } + if(LOS::batteryIsCharging()){ icon = icon+10; } + //icon = icon + result.at(0).toInt() * 10; + if (icon != iconOld || force) { + switch (icon) { + case 0: + label->setPixmap( LXDG::findIcon("battery-caution", "").pixmap(label->size()) ); + break; + case 1: + label->setPixmap( LXDG::findIcon("battery-040", "").pixmap(label->size()) ); + break; + case 2: + label->setPixmap( LXDG::findIcon("battery-060", "").pixmap(label->size()) ); + break; + case 3: + label->setPixmap( LXDG::findIcon("battery-080", "").pixmap(label->size()) ); + break; + case 4: + label->setPixmap( LXDG::findIcon("battery-100", "").pixmap(label->size()) ); + break; + case 10: + label->setPixmap( LXDG::findIcon("battery-charging-caution", "").pixmap(label->size()) ); + break; + case 11: + label->setPixmap( LXDG::findIcon("battery-charging-040", "").pixmap(label->size()) ); + break; + case 12: + label->setPixmap( LXDG::findIcon("battery-charging-060", "").pixmap(label->size()) ); + break; + case 13: + label->setPixmap( LXDG::findIcon("battery-charging-080", "").pixmap(label->size()) ); + break; + case 14: + label->setPixmap( LXDG::findIcon("battery-charging", "").pixmap(label->size()) ); + break; + default: + label->setPixmap( LXDG::findIcon("battery-missing", "").pixmap(label->size()) ); + break; + } + if(iconplayAudioFile(LOS::LuminaShare()+"low-battery.ogg"); + } + if(icon==0){ label->setStyleSheet("QLabel{ background: red;}"); } + else if(icon==14 && charge>98){ label->setStyleSheet("QLabel{ background: green;}"); } + else{ label->setStyleSheet("QLabel{ background: transparent;}"); } + iconOld = icon; + + } + //Now update the display + QString tt; + //Make sure the tooltip can be properly translated as necessary (Ken Moore 5/9/14) + if(icon > 9 && icon < 15){ tt = QString(tr("%1 % (Charging)")).arg(QString::number(charge)); } + else{ tt = QString( tr("%1 % (%2 Remaining)") ).arg(QString::number(charge), getRemainingTime() ); } + label->setToolTip(tt); +} + +QString LBattery::getRemainingTime(){ + int secs = LOS::batterySecondsLeft(); + if(secs < 0){ return "??"; } + QString rem; //remaining + if(secs > 3600){ + int hours = secs/3600; + rem.append( QString::number(hours)+"h "); + secs = secs - (hours*3600); + } + if(secs > 60){ + int min = secs/60; + rem.append( QString::number(min)+"m "); + secs = secs - (min*60); + } + if(secs > 0){ + rem.append(QString::number(secs)+"s"); + } + return rem; +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.h new file mode 100644 index 00000000..29562d5d --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/LBattery.h @@ -0,0 +1,53 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Susanne Jaeckel +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_BATTERY_H +#define _LUMINA_DESKTOP_BATTERY_H + +#include +#include +#include +#include + +#include +#include +#include + +#include "../../Globals.h" +//#include "../LTBWidget.h" +#include "../LPPlugin.h" + +class LBattery : public LPPlugin{ + Q_OBJECT +public: + LBattery(QWidget *parent = 0, QString id = "battery", bool horizontal=true); + ~LBattery(); + +private: + QTimer *timer; + QLabel *label; + int iconOld; + +private slots: + void updateBattery(bool force = false); + QString getRemainingTime(); + +public slots: + void LocaleChange(){ + updateBattery(true); + } + + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + label->setFixedSize( QSize(this->height(), this->height()) ); + }else{ + label->setFixedSize( QSize(this->width(), this->width()) ); + } + updateBattery(true); //force icon refresh + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/NOTES b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/NOTES new file mode 100644 index 00000000..3d93267e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/battery/NOTES @@ -0,0 +1,49 @@ +Eventuell mit einem Menü implementieren, mit Einträgen für: +Anzeige des kompletten Status und Infos +Herunterfahren des Systems etc. + +apm -a + Zeigt den AC line status an + 0 = off-line + 1 = on-line + 2 = backup-power + +apm -b + Zeigt + 0 = high + 1 = low + 2 = critical + 3 = charging + +apm -l + Zeit die prozentuale Kapazitaet + 255 = nicht unterstuetzt + +apm -t + Zeigt die verbleibende Zeit in Sekunden + +Aufruf Systemfunktionen: LUtils.h + +mit der Methode: +QStringList LUtils::getCmdOutput(QString cmd, QStringList args) + +Icons: +/usr/local/share/icons/oxygen/22x22/status +oder unter: +/usr/local/share/icons/oxygen/16x16/status + +battery-040.png // 40 % +battery-060.png +battery-080.png +battery-100.png + +battery-caution.png +battery-charging.png +battery-charging-040.png +battery-charging-060.png +battery-charging-080.png +battery-charging-caution.png + +battery-charging-log.png +battery-log.png +battery-missing.png diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.cpp new file mode 100644 index 00000000..b370c536 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.cpp @@ -0,0 +1,230 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LClock.h" +#include "LSession.h" +#include +#include + +LClock::LClock(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + button = new QToolButton(this); //RotateToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonTextOnly); + button->setStyleSheet("font-weight: bold;"); + button->setPopupMode(QToolButton::DelayedPopup); //make sure it runs the update routine first + button->setMenu(new QMenu()); + //button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + //this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + connect(button, SIGNAL(clicked()), this, SLOT(openMenu())); + connect(button->menu(), SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + calendar = new QCalendarWidget(this); + calAct = new QWidgetAction(this); + calAct->setDefaultWidget(calendar); + TZMenu = new QMenu(this); + connect(TZMenu, SIGNAL(triggered(QAction*)), this, SLOT(ChangeTZ(QAction*)) ); + + //Now assemble the menu + button->menu()->addAction(calAct); + button->menu()->addMenu(TZMenu); + + this->layout()->setContentsMargins(0,0,0,0); //reserve some space on left/right + this->layout()->addWidget(button); + + //Setup the timer + timer = new QTimer(); + //Load all the initial settings + updateFormats(); + LocaleChange(); + ThemeChange(); + OrientationChange(); + //Now connect/start the timer + connect(timer,SIGNAL(timeout()), this, SLOT(updateTime()) ); + connect(QApplication::instance(), SIGNAL(SessionConfigChanged()), this, SLOT(updateFormats()) ); + timer->start(); +} + +LClock::~LClock(){ + timer->stop(); + delete timer; +} + + +void LClock::updateTime(bool adjustformat){ + QDateTime CT = QDateTime::currentDateTime(); + //Now update the display + QString label; + QString timelabel; + QString datelabel; + if(deftime){ timelabel = CT.time().toString(Qt::DefaultLocaleShortDate) ; } + else{ timelabel=CT.toString(timefmt); } + if(defdate){ datelabel = CT.date().toString(Qt::DefaultLocaleShortDate); } + else{ datelabel = CT.toString(datefmt); } + if(datetimeorder == "dateonly"){ + label = datelabel; + button->setToolTip(timelabel); + }else if(datetimeorder == "timedate"){ + label = timelabel + "\n" + datelabel; + button->setToolTip(""); + }else if(datetimeorder == "datetime"){ + label = datelabel + "\n" + timelabel; + button->setToolTip(""); + }else{ + label = timelabel; + button->setToolTip(datelabel); + } + if( this->layout()->direction() == QBoxLayout::TopToBottom ){ + //different routine for vertical text (need newlines instead of spaces) + for(int i=0; isize().width() < (this->fontMetrics().width(label.section("\n",i,i))+10 )&& label.section("\n",i,i).contains(" ")){ + label.replace(label.section("\n",i,i), label.section("\n",i,i).replace(" ", "\n")); + i--; + } + } + //label.replace(" ","\n"); + }else if( this->size().height() < 2*this->fontMetrics().height() ){ + label.replace("\n",", "); + } + if(adjustformat){ + //Check the font/spacing for the display and adjust as necessary + /*double efflines = label.count("\n")+1; //effective lines (with wordwrap) + if( (button->fontMetrics().height()*efflines) > button->height() ){ + //Force a pixel metric font size to fit everything + int szH = qRound( (button->height() - button->fontMetrics().lineSpacing() )/efflines ); + //Need to supply a *width* pixel, not a height metric + int szW = qRound( (szH*button->fontMetrics().maxWidth())/( (double) button->fontMetrics().height()) ); + qDebug() << "Change Clock font:" << button->height() << szH << szW << efflines << button->fontMetrics().height() << button->fontMetrics().lineSpacing(); + button->setStyleSheet("font-weight: bold; font-size: "+QString::number(szW)+"px;"); + }else{ + button->setStyleSheet("font-weight: bold;"); + }*/ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + //horizontal layout + this->setFixedWidth( this->sizeHint().width() +6); + }else{ + //vertical layout + this->setMaximumWidth(100000); + } + } + button->setText(label); +} + +void LClock::updateFormats(){ + qDebug() << "Updating clock format"; + timefmt = LSession::handle()->sessionSettings()->value("TimeFormat","").toString(); + datefmt = LSession::handle()->sessionSettings()->value("DateFormat","").toString(); + deftime = timefmt.simplified().isEmpty(); + defdate = datefmt.simplified().isEmpty(); + //Adjust the timer interval based on the smallest unit displayed + if(deftime){ timer->setInterval(500); } //1/2 second + else if(timefmt.contains("z")){ timer->setInterval(1); } //every millisecond (smallest unit) + else if(timefmt.contains("s")){ timer->setInterval(500); } //1/2 second + else if(timefmt.contains("m")){ timer->setInterval(2000); } //2 seconds + else{ timer->setInterval(1000); } //unknown format - use 1 second interval + datetimeorder = LSession::handle()->sessionSettings()->value("DateTimeOrder", "timeonly").toString().toLower(); + //this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + updateTime(true); + //Now fix the size of the widget with the new size hint + //this->setFixedWidth( this->sizeHint().width() +6); +} + +void LClock::updateMenu(){ + QDateTime cdt = QDateTime::currentDateTime(); + TZMenu->setTitle(QString(tr("Time Zone (%1)")).arg(cdt.timeZoneAbbreviation()) ); + calendar->showToday(); //make sure the current month is visible + calendar->setSelectedDate(QDate::currentDate()); //select the actual date for today +} + +void LClock::openMenu(){ + updateMenu(); + button->showMenu(); +} + +void LClock::closeMenu(){ + button->menu()->hide(); +} + +void LClock::ChangeTZ(QAction *act){ + LTHEME::setCustomEnvSetting("TZ",act->whatsThis()); + QTimer::singleShot(500, this, SLOT(updateTime()) ); +} + +void LClock::LocaleChange(){ + //Refresh all the time zone information + TZMenu->clear(); + TZMenu->addAction(tr("Use System Time")); + TZMenu->addSeparator(); + QList TZList = QTimeZone::availableTimeZoneIds(); + //Orgnize time zones for smaller menus (Continent/Country/City) + // Note: id = Continent/City + QStringList info; + for(int i=0; iisEmpty()){ + tmpC->addMenu(tmpCM); + } + if(!tmpC->isEmpty()){ TZMenu->addMenu(tmpC); } + } + tmpC = new QMenu(this); + tmpC->setTitle(info[i].section("::::",1,1)); + tmpCM = new QMenu(this); + tmpCM->setTitle(info[i].section("::::",2,2)); + //Check if different country + }else if(info[i].section("::::",2,2)!=country){ + if(tmpC!=0 && tmpCM!=0 && !tmpCM->isEmpty()){ + tmpC->addMenu(tmpCM); + } + tmpCM = new QMenu(this); + tmpCM->setTitle(info[i].section("::::",2,2)); + } + //Now create the entry within the country menu + if(tmpCM!=0){ + QAction *act = new QAction(info[i].section("::::",3,3), this); + act->setWhatsThis(info[i].section("::::",4,4) ); + tmpCM->addAction(act); + } + //Save the values for the next run + continent = info[i].section("::::",1,1); + country = info[i].section("::::",2,2); + + if(i== info.length()-1){ + //last go through - save all menus + if(tmpCM!=0 && tmpC!=0 && !tmpCM->isEmpty()){ tmpC->addMenu(tmpCM); } + if(tmpC!=0 && !tmpC->isEmpty()){ TZMenu->addMenu(tmpC); } + } + } + +} + +void LClock::ThemeChange(){ + TZMenu->setIcon(LXDG::findIcon("clock","")); +} + +void LClock::OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ //horizontal panel + //button->setRotation(0); //no rotation of text + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + }else{ //vertical panel + //button->setRotation(90); //90 degree rotation + this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + } + updateTime(true); //re-adjust the font/spacings + this->layout()->update(); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.h new file mode 100644 index 00000000..eddf782c --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/clock/LClock.h @@ -0,0 +1,58 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_CLOCK_H +#define _LUMINA_DESKTOP_CLOCK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../LPPlugin.h" + +//#include "../RotateToolButton.h" + +class LClock : public LPPlugin{ + Q_OBJECT +public: + LClock(QWidget *parent = 0, QString id = "clock", bool horizontal=true); + ~LClock(); + +private: + QTimer *timer; + QToolButton *button; //RotateToolButton + QString timefmt, datefmt, datetimeorder; + bool deftime, defdate; + QMenu *TZMenu; + QCalendarWidget *calendar; + QWidgetAction *calAct; + +private slots: + void updateTime(bool adjustformat = false); + void updateFormats(); + + void updateMenu(); + void openMenu(); + void closeMenu(); + + void ChangeTZ(QAction*); + +public slots: + void LocaleChange(); + void ThemeChange(); + void OrientationChange(); +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.cpp new file mode 100644 index 00000000..90d942de --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.cpp @@ -0,0 +1,207 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LDeskBar.h" +#include "../../LSession.h" + +LDeskBarPlugin::LDeskBarPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + this->layout()->setContentsMargins(0,0,0,0); + this->setStyleSheet( "QToolButton::menu-indicator{ image: none; } QToolButton{ padding: 0px; }"); + + //initialize the desktop bar items + initializeDesktop(); + //setup the directory watcher + QString fav = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/favorites.list"; + if(!QFile::exists(fav)){ QProcess::execute("touch \""+fav+"\""); } + watcher = new QFileSystemWatcher(this); + watcher->addPath( fav ); + connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(filechanged(QString)) ); + QTimer::singleShot(1,this, SLOT(updateFiles()) ); //make sure to load it the first time + QTimer::singleShot(0,this, SLOT(OrientationChange()) ); //adjust sizes/layout + connect(QApplication::instance(), SIGNAL(DesktopFilesChanged()), this, SLOT(updateFiles()) ); +} + +LDeskBarPlugin::~LDeskBarPlugin(){ +} + +// ======================= +// PRIVATE FUNCTIONS +// ======================= +void LDeskBarPlugin::initializeDesktop(){ + //Applications on the desktop + appB = new QToolButton(this); + appB->setToolButtonStyle(Qt::ToolButtonIconOnly); + appB->setAutoRaise(true); + appB->setPopupMode(QToolButton::InstantPopup); + appM = new QMenu(this); + appB->setMenu(appM); + this->layout()->addWidget(appB); + connect(appM,SIGNAL(triggered(QAction*)),this,SLOT(ActionTriggered(QAction*)) ); + connect(appM, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + //Directories on the desktop + dirB = new QToolButton(this); + dirB->setToolButtonStyle(Qt::ToolButtonIconOnly); + dirB->setAutoRaise(true); + dirB->setPopupMode(QToolButton::InstantPopup); + dirM = new QMenu(this); + dirB->setMenu(dirM); + this->layout()->addWidget(dirB); + connect(dirM,SIGNAL(triggered(QAction*)),this,SLOT(ActionTriggered(QAction*)) ); + connect(dirM, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + //Audio Files on the desktop + audioM = new QMenu(this); + connect(audioM,SIGNAL(triggered(QAction*)),this,SLOT(ActionTriggered(QAction*)) ); + //Video Files on the desktop + videoM = new QMenu(this); + connect(videoM,SIGNAL(triggered(QAction*)),this,SLOT(ActionTriggered(QAction*)) ); + //Picture Files on the desktop + pictureM = new QMenu(this); + connect(pictureM,SIGNAL(triggered(QAction*)),this,SLOT(ActionTriggered(QAction*)) ); + //Other Files on the desktop + otherM = new QMenu(this); + connect(otherM,SIGNAL(triggered(QAction*)),this,SLOT(ActionTriggered(QAction*)) ); + docM = new QMenu(this); + connect(docM,SIGNAL(triggered(QAction*)), this,SLOT(ActionTriggered(QAction*)) ); + //All Files Button + fileB = new QToolButton(this); + fileB->setToolButtonStyle(Qt::ToolButtonIconOnly); + fileB->setAutoRaise(true); + fileB->setPopupMode(QToolButton::InstantPopup); + fileM = new QMenu(this); + fileB->setMenu(fileM); + this->layout()->addWidget(fileB); + + updateIcons(); //set all the text/icons +} + +QAction* LDeskBarPlugin::newAction(QString filepath, QString name, QString iconpath){ + return newAction(filepath, name, QIcon(iconpath)); +} + +QAction* LDeskBarPlugin::newAction(QString filepath, QString name, QIcon icon){ + QAction *act = new QAction(icon, name, this); + act->setWhatsThis(filepath); + return act; +} + +// ======================= +// PRIVATE SLOTS +// ======================= +void LDeskBarPlugin::ActionTriggered(QAction* act){ + //Open up the file with the appropriate application + QString cmd = "lumina-open \""+act->whatsThis()+"\""; + qDebug() << "Open File:" << cmd; + LSession::LaunchApplication(cmd); +} +void LDeskBarPlugin::filechanged(QString file){ + updateFiles(); + if(!watcher->files().contains(file)){ watcher->addPath(file); } //make sure the file does not get removed from the watcher +} +void LDeskBarPlugin::updateFiles(){ + QFileInfoList homefiles = LSession::handle()->DesktopFiles(); + QStringList favitems = LDesktopUtils::listFavorites(); + //Remember for format for favorites: ::::[app/dir/]:::: + for(int i=0; iclear(); + dirM->clear(); + audioM->clear(); + videoM->clear(); + pictureM->clear(); + docM->clear(); + otherM->clear(); + for(int i=0; iaddAction( newAction(df.filePath, df.name, LXDG::findIcon(df.icon, ":/images/default-application.png")) ); + } + }else if(type=="dir"){ + //Add it to dirM + dirM->addAction( newAction(path, name, LXDG::findIcon("folder","")) ); + }else if(type.startsWith("audio/")){ + //Add it to audioM + audioM->addAction( newAction(path, name, LXDG::findMimeIcon(type)) ); + }else if(type.startsWith("video/")){ + //Add it to videoM + videoM->addAction( newAction(path, name, LXDG::findMimeIcon(type)) ); + }else if(type.startsWith("image/")){ + //Add it to pictureM + if(LUtils::imageExtensions().contains(path.section("/",-1).section(".",-1).toLower()) ){ + pictureM->addAction( newAction(path, name, QIcon(path)) ); + }else{ + pictureM->addAction( newAction(path, name, LXDG::findMimeIcon(type)) ); + } + }else if(type.startsWith("text/")){ + //Add it to docM + docM->addAction( newAction(path, name, LXDG::findMimeIcon(type)) ); + }else{ + //Add it to otherM + otherM->addAction( newAction(path, name, LXDG::findMimeIcon(type)) ); + } + + } + + //Now update the file menu as appropriate + fileM->clear(); + if(!audioM->isEmpty()){ fileM->addMenu(audioM); } + if(!docM->isEmpty()){ fileM->addMenu(docM); } + if(!pictureM->isEmpty()){ fileM->addMenu(pictureM); } + if(!videoM->isEmpty()){ fileM->addMenu(videoM); } + if(!otherM->isEmpty()){ fileM->addMenu(otherM); } + //Check for a single submenu, and skip the main if need be + disconnect(fileB->menu(), SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed()) ); + if(fileM->actions().length()==1){ + if(!audioM->isEmpty()){ fileB->setMenu(audioM); } + else if(!pictureM->isEmpty()){ fileB->setMenu(pictureM); } + else if(!videoM->isEmpty()){ fileB->setMenu(videoM); } + else if(!docM->isEmpty()){ fileB->setMenu(docM); } + else if(!otherM->isEmpty()){ fileB->setMenu(otherM); } + }else{ + fileB->setMenu(fileM); + } + connect(fileB->menu(), SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + + //Setup the visibility of the buttons + appB->setVisible( !appM->isEmpty() ); + dirB->setVisible( !dirM->isEmpty() ); + fileB->setVisible( !fileM->isEmpty() ); +} + +void LDeskBarPlugin::updateIcons(){ + //Set all the text/icons + appB->setIcon( LXDG::findIcon("favorites", "") ); + appB->setToolTip(tr("Favorite Applications")); + dirB->setIcon( LXDG::findIcon("folder", "") ); + dirB->setToolTip(tr("Favorite Folders")); + audioM->setTitle( tr("Audio") ); + audioM->setIcon( LXDG::findIcon("audio-x-generic","") ); + videoM->setTitle( tr("Video") ); + videoM->setIcon( LXDG::findIcon("video-x-generic","") ); + pictureM->setTitle( tr("Pictures") ); + pictureM->setIcon( LXDG::findIcon("image-x-generic","") ); + otherM->setTitle( tr("Other Files") ); + otherM->setIcon( LXDG::findIcon("unknown","") ); + docM->setTitle( tr("Documents") ); + docM->setIcon( LXDG::findIcon("x-office-document","") ); + fileB->setIcon( LXDG::findIcon("document-multiple", "") ); + fileB->setToolTip(tr("Favorite Files") ); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.h new file mode 100644 index 00000000..74f41230 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopbar/LDeskBar.h @@ -0,0 +1,88 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This plugin displays the contents of the user's home directory +// as organized within a couple buttons on the panel (apps, dirs, files) +//=========================================== +#ifndef _LUMINA_DESKTOP_DESKBAR_H +#define _LUMINA_DESKTOP_DESKBAR_H + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// libLumina includes +#include + +// local includes +//#include "../LTBWidget.h" +#include "../LPPlugin.h" + +class LDeskBarPlugin : public LPPlugin{ + Q_OBJECT +public: + LDeskBarPlugin(QWidget* parent=0, QString id = "desktopbar", bool horizontal=true); + ~LDeskBarPlugin(); + +private: + QFileSystemWatcher *watcher; + //Special toolbuttons and menus + QToolButton *appB, *fileB, *dirB; + QMenu *appM, *dirM, *audioM, *videoM, *pictureM, *fileM, *otherM, *docM; + QList APPLIST; + QDateTime lastHomeUpdate; + + void initializeDesktop(); + //bool readDesktopFile(QString path, QString &name, QString &iconpath); + + QAction* newAction(QString filepath, QString name, QString iconpath); + QAction* newAction(QString filepath, QString name, QIcon icon); + + //void updateMenu(QMenu* menu, QFileInfoList files, bool trim = true); + + +private slots: + void ActionTriggered(QAction* act); + void filechanged(QString); + void updateFiles(); + void updateIcons(); + +public slots: + void LocaleChange(){ + updateIcons(); + updateFiles(); + } + + void OrientationChange(){ + QSize sz; + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + sz = QSize(this->height(), this->height()); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + sz = QSize(this->width(), this->width()); + } + appB->setIconSize(sz); + fileB->setIconSize(sz); + dirB->setIconSize(sz); + for(int i=0; isetIconSize(sz); + } + this->layout()->update(); + } +}; + + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.cpp new file mode 100644 index 00000000..c51e4b4a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.cpp @@ -0,0 +1,142 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Susanne Jaeckel +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LDesktopSwitcher.h" +#include + +LDesktopSwitcher::LDesktopSwitcher(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal) { + iconOld = -1; + this->setStyleSheet( "QToolButton::menu-indicator{ image: none; } QToolButton{padding: 0px;}"); + //Setup the widget + label = new QToolButton(this); + label->setPopupMode(QToolButton::DelayedPopup); + label->setAutoRaise(true); + label->setToolButtonStyle(Qt::ToolButtonIconOnly); + label->setIcon( LXDG::findIcon("preferences-desktop-display-color", "") ); + label->setToolTip(QString("Workspace 1")); + connect(label, SIGNAL(clicked()), this, SLOT(openMenu())); + menu = new QMenu(this); + connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(menuActionTriggered(QAction*))); + connect(menu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + label->setMenu(menu); + this->layout()->addWidget(label); + + // Maybe a timer should be set to set the toolTip of the button, + // becasue the workspace could be switched via Keyboard-shortcuts ... + + QTimer::singleShot(500, this, SLOT(createMenu()) ); //needs a delay to make sure it works right the first time + QTimer::singleShot(0,this, SLOT(OrientationChange()) ); //adjust icon size +} + +LDesktopSwitcher::~LDesktopSwitcher(){ +} +/* MOVED THESE FUNCTIONS TO LIBLUMINA (LuminaX11.h) -- Ken Moore 5/9/14 +void LDesktopSwitcher::setNumberOfDesktops(int number) { + Display *display = QX11Info::display(); + Window rootWindow = QX11Info::appRootWindow(); + + Atom atom = XInternAtom(display, "_NET_NUMBER_OF_DESKTOPS", False); + XEvent xevent; + xevent.type = ClientMessage; + xevent.xclient.type = ClientMessage; + xevent.xclient.display = display; + xevent.xclient.window = rootWindow; + xevent.xclient.message_type = atom; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = number; + xevent.xclient.data.l[1] = CurrentTime; + xevent.xclient.data.l[2] = 0; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + XSendEvent(display, rootWindow, False, SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + + XFlush(display); +} + +void LDesktopSwitcher::setCurrentDesktop(int number) { + Display *display = QX11Info::display(); + Window rootWindow = QX11Info::appRootWindow(); + + Atom atom = XInternAtom(display, "_NET_CURRENT_DESKTOP", False); + XEvent xevent; + xevent.type = ClientMessage; + xevent.xclient.type = ClientMessage; + xevent.xclient.display = display; + xevent.xclient.window = rootWindow; + xevent.xclient.message_type = atom; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = number; + xevent.xclient.data.l[1] = CurrentTime; + xevent.xclient.data.l[2] = 0; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + XSendEvent(display, rootWindow, False, SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + + XFlush(display); +} + +int LDesktopSwitcher::getNumberOfDesktops() { + int number = -1; + Atom a = XInternAtom(QX11Info::display(), "_NET_NUMBER_OF_DESKTOPS", true); + Atom realType; + int format; + unsigned long num, bytes; + unsigned char *data = 0; + int status = XGetWindowProperty(QX11Info::display(), QX11Info::appRootWindow(), a, 0L, (~0L), + false, AnyPropertyType, &realType, &format, &num, &bytes, &data); + if( (status >= Success) && (num > 0) ){ + number = *data; + XFree(data); + } + return number; +} + +int LDesktopSwitcher::getCurrentDesktop() { + int number = -1; + Atom a = XInternAtom(QX11Info::display(), "_NET_CURRENT_DESKTOP", true); + Atom realType; + int format; + unsigned long num, bytes; + unsigned char *data = 0; + int status = XGetWindowProperty(QX11Info::display(), QX11Info::appRootWindow(), a, 0L, (~0L), + false, AnyPropertyType, &realType, &format, &num, &bytes, &data); + if( (status >= Success) && (num > 0) ){ + number = *data; + XFree(data); + } + return number; +} */ + +void LDesktopSwitcher::openMenu(){ + //Make sure the menu is refreshed right before it opens + createMenu(); + label->showMenu(); +} + +QAction* LDesktopSwitcher::newAction(int what, QString name) { + QAction *act = new QAction(LXDG::findIcon("preferences-desktop-display", ""), name, this); + act->setWhatsThis(QString::number(what)); + return act; +} + +void LDesktopSwitcher::createMenu() { + int cur = LSession::handle()->XCB->CurrentWorkspace(); //current desktop number + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); //total number of desktops + //qDebug() << "-- vor getCurrentDesktop SWITCH"; + qDebug() << "Virtual Desktops:" << tot << cur; + menu->clear(); + for (int i = 0; i < tot; i++) { + QString name = QString(tr("Workspace %1")).arg( QString::number(i+1) ); + if(i == cur){ name.prepend("*"); name.append("*");} //identify which desktop this is currently + menu->addAction(newAction(i, name)); + } +} + +void LDesktopSwitcher::menuActionTriggered(QAction* act) { + LSession::handle()->XCB->SetCurrentWorkspace(act->whatsThis().toInt()); + label->setToolTip(QString(tr("Workspace %1")).arg(act->whatsThis().toInt() +1)); + QTimer::singleShot(500, this, SLOT(createMenu()) ); //make sure the menu gets updated +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.h new file mode 100644 index 00000000..af9250b7 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/desktopswitcher/LDesktopSwitcher.h @@ -0,0 +1,72 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Susanne Jaeckel +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SWITCHER_H +#define _LUMINA_DESKTOP_SWITCHER_H + +#include +#include +#include +//#include +#include +#include + +#include +#include +#include + +//#include "../LTBWidget.h" +#include "../LPPlugin.h" + +//#include +//#include +//#include + +class LDesktopSwitcher : public LPPlugin{ + Q_OBJECT +public: + LDesktopSwitcher(QWidget *parent = 0, QString id = "desktopswitcher", bool horizontal=true); + ~LDesktopSwitcher(); + +private: + QTimer *timer; + QToolButton *label; + QMenu *menu; + int iconOld; + + //void setNumberOfDesktops(int); + //void setCurrentDesktop(int); + //int getNumberOfDesktops(); + //int getCurrentDesktop(); + + + QAction* newAction(int, QString); + +private slots: + void openMenu(); + void createMenu(); + void menuActionTriggered(QAction*); + +public slots: + void LocaleChange(){ + createMenu(); + } + + void OrientationChange(){ + QSize sz; + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + sz = QSize(this->height(), this->height()); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + sz = QSize(this->width(), this->width()); + } + label->setIconSize(sz); + this->layout()->update(); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.cpp new file mode 100644 index 00000000..14880f9b --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.cpp @@ -0,0 +1,35 @@ +//=========================================== +// Lumina Desktop source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "PPJsonMenu.h" +#include "../../JsonMenu.h" + +LPJsonMenu::LPJsonMenu(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + //Setup the button + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setPopupMode(QToolButton::InstantPopup); //make sure it runs the update routine first + //connect(button, SIGNAL(clicked()), this, SLOT(openMenu())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + //Parse the id and get the extra information needed for the plugin + QStringList info = id.section("---",0,0).split("::::"); //FORMAT:[ "jsonmenu---",exec,name, icon(optional)] + if(info.length()>=3){ + qDebug() << "Custom JSON Menu Loaded:" << info; + JsonMenu *menu = new JsonMenu(info[1], button); + button->setText(info[2]); + //connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(SystemApplication(QAction*)) ); + if(info.length()>=4){ button->setIcon( LXDG::findIcon(info[3],"run-build") ); } + else{ button->setIcon( LXDG::findIcon("run-build","") ); } + button->setMenu(menu); + } + //Now start up the widgets + QTimer::singleShot(0,this,SLOT(OrientationChange()) ); //update the sizing/icon +} + +LPJsonMenu::~LPJsonMenu(){ +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.h new file mode 100644 index 00000000..d0827fd2 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/jsonmenu/PPJsonMenu.h @@ -0,0 +1,42 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2016, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_PANEL_JSON_PLUGIN_H +#define _LUMINA_PANEL_JSON_PLUGIN_H + +#include "../../Globals.h" +#include "../LPPlugin.h" + + +class LPJsonMenu : public LPPlugin{ + Q_OBJECT +public: + LPJsonMenu(QWidget *parent = 0, QString id = "jsonmenu", bool horizontal=true); + ~LPJsonMenu(); + +private: + QToolButton *button; + +private slots: + //void SystemApplication(QAction*); + +public slots: + void LocaleChange(){ + } + + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + this->layout()->update(); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/line/LLine.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/line/LLine.h new file mode 100644 index 00000000..94de486e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/line/LLine.h @@ -0,0 +1,40 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is a generic line separator for the panel +//=========================================== +#ifndef _LUMINA_DESKTOP_PANEL_PLUGIN_LINE_H +#define _LUMINA_DESKTOP_PANEL_PLUGIN_LINE_H + +#include "../LPPlugin.h" +#include + +class LLinePlugin : public LPPlugin{ + Q_OBJECT +private: + QFrame *line; + +public: + LLinePlugin(QWidget *parent=0, QString id="spacer", bool horizontal=true) : LPPlugin(parent, id, horizontal){ + line = new QFrame(this); + line->setObjectName("LuminaPanelLine"); + this->layout()->addWidget(line); + OrientationChange(); + } + ~LLinePlugin(){} + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ //horizontal + line->setFrameShape(QFrame::VLine); + }else{ //vertical + line->setFrameShape(QFrame::HLine); + } + } +}; + + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/panel-plugins.pri b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/panel-plugins.pri new file mode 100644 index 00000000..c40c4725 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/panel-plugins.pri @@ -0,0 +1,57 @@ +#Lumina Desktop Panel Plugin files + +SOURCES += $$PWD/userbutton/LUserButton.cpp \ + $$PWD/userbutton/UserWidget.cpp \ + $$PWD/userbutton/UserItemWidget.cpp \ + $$PWD/desktopbar/LDeskBar.cpp \ + $$PWD/taskmanager/LTaskManagerPlugin.cpp \ + $$PWD/taskmanager/LTaskButton.cpp \ + $$PWD/systemtray/LSysTray.cpp \ + $$PWD/systemtray/TrayIcon.cpp \ + $$PWD/clock/LClock.cpp \ + $$PWD/battery/LBattery.cpp \ + $$PWD/desktopswitcher/LDesktopSwitcher.cpp \ + $$PWD/systemdashboard/LSysDashboard.cpp \ + $$PWD/systemdashboard/SysMenuQuick.cpp \ + $$PWD/showdesktop/LHomeButton.cpp \ + $$PWD/appmenu/LAppMenuPlugin.cpp \ + $$PWD/applauncher/AppLaunchButton.cpp \ + $$PWD/systemstart/LStartButton.cpp \ + $$PWD/systemstart/StartMenu.cpp \ + $$PWD/systemstart/ItemWidget.cpp \ + $$PWD/audioplayer/LPAudioPlayer.cpp \ + $$PWD/audioplayer/PPlayerWidget.cpp \ + $$PWD/jsonmenu/PPJsonMenu.cpp + +HEADERS += $$PWD/RotateToolButton.h \ + $$PWD/userbutton/LUserButton.h \ + $$PWD/userbutton/UserWidget.h \ + $$PWD/userbutton/UserItemWidget.h \ + $$PWD/desktopbar/LDeskBar.h \ + $$PWD/systemtray/LSysTray.h \ + $$PWD/systemtray/TrayIcon.h \ + $$PWD/spacer/LSpacer.h \ + $$PWD/line/LLine.h \ + $$PWD/clock/LClock.h \ + $$PWD/battery/LBattery.h \ + $$PWD/desktopswitcher/LDesktopSwitcher.h \ + $$PWD/taskmanager/LTaskManagerPlugin.h \ + $$PWD/taskmanager/LTaskButton.h \ + $$PWD/systemdashboard/LSysDashboard.h \ + $$PWD/systemdashboard/SysMenuQuick.h \ + $$PWD/showdesktop/LHomeButton.h \ + $$PWD/appmenu/LAppMenuPlugin.h \ + $$PWD/applauncher/AppLaunchButton.h \ + $$PWD/systemstart/LStartButton.h \ + $$PWD/systemstart/StartMenu.h \ + $$PWD/systemstart/ItemWidget.h \ + $$PWD/audioplayer/LPAudioPlayer.h \ + $$PWD/audioplayer/PPlayerWidget.h \ + $$PWD/jsonmenu/PPJsonMenu.h +# $$PWD/quickcontainer/QuickPPlugin.h + +FORMS += $$PWD/userbutton/UserWidget.ui \ + $$PWD/systemdashboard/SysMenuQuick.ui \ + $$PWD/systemstart/StartMenu.ui \ + $$PWD/audioplayer/PPlayerWidget.ui + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/quickcontainer/QuickPPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/quickcontainer/QuickPPlugin.h new file mode 100644 index 00000000..6f61c4d5 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/quickcontainer/QuickPPlugin.h @@ -0,0 +1,43 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is a simple container for a QtQuick plugin +//=========================================== +#ifndef _LUMINA_DESKTOP_PANEL_PLUGIN_QUICK_H +#define _LUMINA_DESKTOP_PANEL_PLUGIN_QUICK_H + +#include +#include +#include "../LPPlugin.h" + +#include +#include + +class QuickPPlugin : public LPPlugin{ + Q_OBJECT +public: + QuickPPlugin(QWidget* parent, QString ID, bool horizontal) : LPPlugin(parent, ID){ + container = new QQuickWidget(this); + container->setResizeMode(QQuickWidget::SizeRootObjectToView); + this->layout()->addWidget(container); + horizontal = true; //just to silence compiler warning + container->setSource(QUrl::fromLocalFile( LUtils::findQuickPluginFile(ID.section("---",0,0)) )); + } + + ~QuickPPlugin(){} + +private: + QQuickWidget *container; + +private slots: + void statusChange(QQuickWidget::Status status){ + if(status == QQuickWidget::Error){ + qDebug() << "Quick Widget Error:" << this->type(); + } + } + +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.cpp new file mode 100644 index 00000000..6c259b16 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.cpp @@ -0,0 +1,43 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LHomeButton.h" +#include "../../LSession.h" + +#include + +LHomeButtonPlugin::LHomeButtonPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + connect(button, SIGNAL(clicked()), this, SLOT(showDesktop())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes +} + +LHomeButtonPlugin::~LHomeButtonPlugin(){ + +} + +void LHomeButtonPlugin::updateButtonVisuals(){ + button->setIcon( LXDG::findIcon("user-desktop", "") ); +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void LHomeButtonPlugin::showDesktop(){ + QList wins = LSession::handle()->XCB->WindowList(); + for(int i=0; iXCB->WindowState(wins[i]) ){ + LSession::handle()->XCB->MinimizeWindow(wins[i]); + } + } +} + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.h new file mode 100644 index 00000000..74aaf4fb --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/showdesktop/LHomeButton.h @@ -0,0 +1,62 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin is a simple button to hide all windows so the desktop is visible +//=========================================== +#ifndef _LUMINA_DESKTOP_GO_HOME_PLUGIN_H +#define _LUMINA_DESKTOP_GO_HOME_PLUGIN_H + +// Qt includes +#include +#include +#include + + +// Lumina-desktop includes +#include "../LPPlugin.h" //main plugin widget + +// libLumina includes +#include "LuminaXDG.h" + +// PANEL PLUGIN BUTTON +class LHomeButtonPlugin : public LPPlugin{ + Q_OBJECT + +public: + LHomeButtonPlugin(QWidget *parent = 0, QString id = "homebutton", bool horizontal=true); + ~LHomeButtonPlugin(); + +private: + QToolButton *button; + + void updateButtonVisuals(); + +private slots: + void showDesktop(); + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + this->layout()->update(); + updateButtonVisuals(); + } + + void LocaleChange(){ + updateButtonVisuals(); + } + + void ThemeChange(){ + updateButtonVisuals(); + } +}; + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/spacer/LSpacer.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/spacer/LSpacer.h new file mode 100644 index 00000000..1e60c519 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/spacer/LSpacer.h @@ -0,0 +1,34 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This class is a generic invisible spacer for the panel +//=========================================== +#ifndef _LUMINA_DESKTOP_PANEL_PLUGIN_SPACER_H +#define _LUMINA_DESKTOP_PANEL_PLUGIN_SPACER_H + +#include "../LPPlugin.h" + +class LSpacerPlugin : public LPPlugin{ + Q_OBJECT +public: + LSpacerPlugin(QWidget *parent=0, QString id="spacer", bool horizontal=true) : LPPlugin(parent, id, horizontal){ + if(horizontal){ this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); } + else{ this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); } + } + ~LSpacerPlugin(){} + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ //horizontal + this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + }else{ //vertical + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + } + } +}; + + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.cpp new file mode 100644 index 00000000..267a7cb0 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.cpp @@ -0,0 +1,91 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LSysDashboard.h" + +LSysDashboard::LSysDashboard(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + upTimer = new QTimer(this); + upTimer->setInterval(10000); //10 second update ping + connect(upTimer, SIGNAL(timeout()), this, SLOT(updateIcon())); + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setPopupMode(QToolButton::DelayedPopup); //make sure it runs the update routine first + connect(button, SIGNAL(clicked()), this, SLOT(openMenu())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + menu = new QMenu(this); + connect(menu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + sysmenu = new LSysMenuQuick(this); + connect(sysmenu, SIGNAL(CloseMenu()), this, SLOT(closeMenu()) ); + mact = new QWidgetAction(this); + mact->setDefaultWidget(sysmenu); + menu->addAction(mact); + + button->setMenu(menu); + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes +} + +LSysDashboard::~LSysDashboard(){ + +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void LSysDashboard::updateIcon(bool force){ + //For the visual, show battery state only if important + static bool batcharging = false; + QPixmap pix; + button->setToolTip(tr("System Dashboard")); + if(LOS::hasBattery()){ + int bat = LOS::batteryCharge(); + bool charging = LOS::batteryIsCharging(); + //Set the icon as necessary + if(charging && !batcharging){ + //Charging and just plugged in + if(bat < 15){ button->setIcon( LXDG::findIcon("battery-charging-low","") ); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(bat < 30){ button->setIcon( LXDG::findIcon("battery-charging-caution","") ); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(force || button->icon().isNull()){ resetIcon(); } + }else if(!charging){ + //Not charging (critical level or just unplugged) + if(bat<5){ button->setIcon( LXDG::findIcon("battery-missing","") ); } + else if(bat < 15){ button->setIcon( LXDG::findIcon("battery-low","") ); QTimer::singleShot(5000, this, SLOT(resetIcon())); } + else if(bat < 30 && batcharging){ button->setIcon( LXDG::findIcon("battery-caution","") ); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(bat < 50 && batcharging){ button->setIcon( LXDG::findIcon("battery-040","")); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(bat < 70 && batcharging){ button->setIcon( LXDG::findIcon("battery-060","")); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(bat < 90 && batcharging){ button->setIcon( LXDG::findIcon("battery-080","")); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(batcharging){ button->setIcon( LXDG::findIcon("battery-100","")); QTimer::singleShot(5000, this, SLOT(resetIcon()));} + else if(force || button->icon().isNull()){ resetIcon(); } + }else if(force || button->icon().isNull()){ + //Otherwise just use the default icon + resetIcon(); + } + //Save the values for comparison later + batcharging = charging; + if( !upTimer->isActive() ){ upTimer->start(); } //only use the timer if a battery is present + + // No battery - just use/set the normal icon + }else if(force || button->icon().isNull()){ + resetIcon(); + if(upTimer->isActive() ){ upTimer->stop(); } //no battery available - no refresh timer needed + } + +} + +void LSysDashboard::resetIcon(){ + button->setIcon( LXDG::findIcon("dashboard-show","")); +} + +void LSysDashboard::openMenu(){ + sysmenu->UpdateMenu(); + button->showMenu(); +} + +void LSysDashboard::closeMenu(){ + menu->hide(); +} + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.h new file mode 100644 index 00000000..782fc4e6 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/LSysDashboard.h @@ -0,0 +1,76 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin allows the user to control different system settings +// For example: screen brightness, audio volume, workspace, and battery +//=========================================== +#ifndef _LUMINA_DESKTOP_SYSTEM_DASHBOARD_H +#define _LUMINA_DESKTOP_SYSTEM_DASHBOARD_H + +//Qt includes + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//libLumina includes +#include +#include + +//Local includes +#include "../LPPlugin.h" +#include "SysMenuQuick.h" + +class LSysDashboard : public LPPlugin{ + Q_OBJECT +public: + LSysDashboard(QWidget *parent = 0, QString id="systemdashboard", bool horizontal=true); + ~LSysDashboard(); + +private: + QMenu *menu; + QWidgetAction *mact; + LSysMenuQuick *sysmenu; + QToolButton *button; + QTimer *upTimer; + +private slots: + void updateIcon(bool force = false); + void resetIcon(); + void openMenu(); + void closeMenu(); + +public slots: + void LocaleChange(){ + updateIcon(true); + sysmenu->UpdateMenu(); + } + + void ThemeChange(){ + updateIcon(true); + sysmenu->UpdateMenu(); + } + + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + updateIcon(true); //force icon refresh + this->layout()->update(); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.cpp new file mode 100644 index 00000000..1d699ea9 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.cpp @@ -0,0 +1,211 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "SysMenuQuick.h" +#include "ui_SysMenuQuick.h" + +#include "../../LSession.h" +#include + +LSysMenuQuick::LSysMenuQuick(QWidget *parent) : QWidget(parent), ui(new Ui::LSysMenuQuick){ + ui->setupUi(this); + brighttimer = new QTimer(this); + brighttimer->setSingleShot(true); + brighttimer->setInterval(50); //50ms delay in setting the new value + //Now reset the initial saved settings (this is handles by the LOS/session now - 4/22/15) + firstrun = true; + UpdateMenu(); //do this once before all the signals/slots are connected below + firstrun = false; + //Now setup the connections + connect(ui->slider_volume, SIGNAL(valueChanged(int)), this, SLOT(volSliderChanged()) ); + connect(ui->slider_brightness, SIGNAL(valueChanged(int)), this, SLOT(brightSliderChanged()) ); + connect(ui->tool_wk_prev, SIGNAL(clicked()), this, SLOT(prevWorkspace()) ); + connect(ui->tool_wk_next, SIGNAL(clicked()), this, SLOT(nextWorkspace()) ); + connect(ui->tool_logout, SIGNAL(clicked()), this, SLOT(startLogout()) ); + connect(ui->tool_vol_mixer, SIGNAL(clicked()), this, SLOT(startMixer()) ); + connect(brighttimer, SIGNAL(timeout()), this, SLOT(setCurrentBrightness()) ); + connect(ui->combo_locale, SIGNAL(currentIndexChanged(int)), this, SLOT(changeLocale()) ); + //And setup the default icons + ui->label_bright_icon->setPixmap( LXDG::findIcon("preferences-system-power-management","").pixmap(ui->label_bright_icon->maximumSize()) ); + ui->tool_wk_prev->setIcon( LXDG::findIcon("go-previous-view","")); + ui->tool_wk_next->setIcon( LXDG::findIcon("go-next-view","") ); + ui->tool_logout->setIcon( LXDG::findIcon("system-log-out","") ); +} + +LSysMenuQuick::~LSysMenuQuick(){ + +} + +void LSysMenuQuick::UpdateMenu(){ + ui->retranslateUi(this); + //Audio Volume + int val = LOS::audioVolume(); + QIcon ico; + if(val > 66){ ico= LXDG::findIcon("audio-volume-high",""); } + else if(val > 33){ ico= LXDG::findIcon("audio-volume-medium",""); } + else if(val > 0){ ico= LXDG::findIcon("audio-volume-low",""); } + else{ ico= LXDG::findIcon("audio-volume-muted",""); } + bool hasMixer = LOS::hasMixerUtility(); + ui->label_vol_icon->setVisible(!hasMixer); + ui->tool_vol_mixer->setVisible(hasMixer); + if(!hasMixer){ ui->label_vol_icon->setPixmap( ico.pixmap(ui->label_vol_icon->maximumSize()) ); } + else{ ui->tool_vol_mixer->setIcon(ico); } + QString txt = QString::number(val)+"%"; + if(val<100){ txt.prepend(" "); } //make sure no widget resizing + ui->label_vol_text->setText(txt); + if(ui->slider_volume->value()!= val){ ui->slider_volume->setValue(val); } + //Screen Brightness + val = LOS::ScreenBrightness(); + if(val < 0){ + //No brightness control - hide it + ui->group_brightness->setVisible(false); + }else{ + ui->group_brightness->setVisible(true); + txt = QString::number(val)+"%"; + if(val<100){ txt.prepend(" "); } //make sure no widget resizing + ui->label_bright_text->setText(txt); + if(ui->slider_brightness->value()!=val){ ui->slider_brightness->setValue(val); } + } + + //Do any one-time checks + if(firstrun){ + hasBat = LOS::hasBattery(); //No need to check this more than once - will not change in the middle of a session + //Current Locale + QStringList locales = LUtils::knownLocales(); + ui->combo_locale->clear(); + QLocale curr; + for(int i=0; icombo_locale->addItem(loc.nativeLanguageName()+" ("+locales[i]+")", locales[i]); //Make the display text prettier later + if(locales[i] == curr.name() || locales[i] == curr.name().section("_",0,0) ){ + //Current Locale + ui->combo_locale->setCurrentIndex(ui->combo_locale->count()-1); //the last item in the list right now + } + } + ui->group_locale->setVisible(locales.length() > 1); + } + + //Battery Status + if(hasBat){ + ui->group_battery->setVisible(true); + val = LOS::batteryCharge(); + if(LOS::batteryIsCharging()){ + if(val < 15){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-low","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 30){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-caution","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 50){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-040","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 70){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-060","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 90){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging-080","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else{ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-charging","").pixmap(ui->label_bat_icon->maximumSize()) ); } + ui->label_bat_text->setText( QString("%1%\n(%2)").arg(QString::number(val), tr("connected")) ); + }else{ + if(val < 1){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-missing","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 15){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-low","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 30){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-caution","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 50){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-040","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 70){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-060","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else if(val < 90){ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-080","").pixmap(ui->label_bat_icon->maximumSize()) ); } + else{ ui->label_bat_icon->setPixmap( LXDG::findIcon("battery-100","").pixmap(ui->label_bat_icon->maximumSize()) ); } + ui->label_bat_text->setText( QString("%1%\n(%2)").arg(QString::number(val), getRemainingTime()) ); + } + }else{ + ui->group_battery->setVisible(false); + } + //Workspace + val = LSession::handle()->XCB->CurrentWorkspace(); + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); + ui->group_workspace->setVisible(val>=0 && tot>1); + ui->label_wk_text->setText( QString(tr("%1 of %2")).arg(QString::number(val+1), QString::number(tot)) ); +} + +void LSysMenuQuick::volSliderChanged(){ + int val = ui->slider_volume->value(); + LOS::setAudioVolume(val); + QString txt = QString::number(val)+"%"; + if(val<100){ txt.prepend(" "); } //make sure no widget resizing + ui->label_vol_text->setText( txt ); + if(val > 66){ ui->label_vol_icon->setPixmap( LXDG::findIcon("audio-volume-high","").pixmap(ui->label_vol_icon->maximumSize()) ); } + else if(val > 33){ ui->label_vol_icon->setPixmap( LXDG::findIcon("audio-volume-medium","").pixmap(ui->label_vol_icon->maximumSize()) ); } + else if(val > 0){ ui->label_vol_icon->setPixmap( LXDG::findIcon("audio-volume-low","").pixmap(ui->label_vol_icon->maximumSize()) ); } + else{ ui->label_vol_icon->setPixmap( LXDG::findIcon("audio-volume-muted","").pixmap(ui->label_vol_icon->maximumSize()) ); } +} + +void LSysMenuQuick::startMixer(){ + emit CloseMenu(); + LOS::startMixerUtility(); +} + +void LSysMenuQuick::brightSliderChanged(){ + //Brightness controls cannot operate extremely quickly - combine calls as necessary + if(brighttimer->isActive()){ brighttimer->stop(); } + brighttimer->start(); + //*DO* update the label right away + int val = ui->slider_brightness->value(); + QString txt = QString::number(val)+"%"; + if(val<100){ txt.prepend(" "); } //make sure no widget resizing + ui->label_bright_text->setText( txt ); +} + +void LSysMenuQuick::setCurrentBrightness(){ + int val = ui->slider_brightness->value(); + LOS::setScreenBrightness(val); + QString txt = QString::number(val)+"%"; + if(val<100){ txt.prepend(" "); } //make sure no widget resizing + ui->label_bright_text->setText( txt ); +} + +void LSysMenuQuick::nextWorkspace(){ + int cur = LSession::handle()->XCB->CurrentWorkspace(); + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); + //qDebug()<< "Next Workspace:" << cur << tot; + cur++; + if(cur>=tot){ cur = 0; } //back to beginning + //qDebug() << " - New Current:" << cur; + LSession::handle()->XCB->SetCurrentWorkspace(cur); +ui->label_wk_text->setText( QString(tr("%1 of %2")).arg(QString::number(cur+1), QString::number(tot)) ); +} + +void LSysMenuQuick::prevWorkspace(){ + int cur = LSession::handle()->XCB->CurrentWorkspace(); + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); + cur--; + if(cur<0){ cur = tot-1; } //back to last + LSession::handle()->XCB->SetCurrentWorkspace(cur); + ui->label_wk_text->setText( QString(tr("%1 of %2")).arg(QString::number(cur+1), QString::number(tot)) ); +} + +QString LSysMenuQuick::getRemainingTime(){ + int secs = LOS::batterySecondsLeft(); + if(secs < 0){ return "??"; } + QString rem; //remaining + if(secs > 3600){ + int hours = secs/3600; + rem.append( QString::number(hours)+"h "); + secs = secs - (hours*3600); + } + if(secs > 60){ + int min = secs/60; + rem.append( QString::number(min)+"m "); + secs = secs - (min*60); + } + if(secs > 0){ + rem.append( QString::number(secs)+"s"); + }else{ + rem.append( "0s" ); + } + return rem; +} + +void LSysMenuQuick::startLogout(){ + emit CloseMenu(); + LSession::handle()->systemWindow(); +} + +void LSysMenuQuick::changeLocale(){ + //Get the currently selected Locale + QString locale = ui->combo_locale->currentData().toString(); + emit CloseMenu(); + LSession::handle()->switchLocale(locale); +} \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.h new file mode 100644 index 00000000..a300b5b1 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.h @@ -0,0 +1,54 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin allows the user to control different system settings +// For example: screen brightness, audio volume, workspace, and battery +//=========================================== +#ifndef _LUMINA_PANEL_QUICK_SYSTEM_MENU_H +#define _LUMINA_PANEL_QUICK_SYSTEM_MENU_H + +#include +#include +#include + +#include +#include + +namespace Ui{ + class LSysMenuQuick; +}; + +class LSysMenuQuick : public QWidget{ + Q_OBJECT +public: + LSysMenuQuick(QWidget *parent=0); + ~LSysMenuQuick(); + + void UpdateMenu(); + +private: + Ui::LSysMenuQuick *ui; + QTimer *brighttimer; + bool firstrun, hasBat; + QString getRemainingTime(); //battery time left + +private slots: + void volSliderChanged(); + void brightSliderChanged(); //start the delay/collection timer + void setCurrentBrightness(); //perform the change + void startMixer(); + void nextWorkspace(); + void prevWorkspace(); + void startLogout(); + void changeLocale(); + + +signals: + void CloseMenu(); + +}; + +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.ui b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.ui new file mode 100644 index 00000000..26c32c74 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemdashboard/SysMenuQuick.ui @@ -0,0 +1,400 @@ + + + LSysMenuQuick + + + + 0 + 0 + 171 + 317 + + + + Form + + + /*QGroupBox{ +border-radius: 5px; +border: 1px solid grey; +margin-top: 1ex; +} +QGroupBox::title{ +subcontrol-origin: margin; +subcontrol-position: top center; +padding: 0 3px; +background-color: rgba(255,255,255,255); +border-radius: 5px; +font: bold; +}*/ + + + + 2 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + System Volume + + + false + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + + + + + + 30 + 30 + + + + + 32 + 32 + + + + Launch Audio Mixer + + + + + + + 30 + 30 + + + + true + + + + + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + 100% + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Screen Brightness + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + + + + + 10 + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + 100% + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Battery Status + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + + + + + + 0 + 0 + + + + 100% (Plugged in) + + + Qt::AlignCenter + + + + + + + + + + Workspace + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + prev + + + + + + + 1 of 2 + + + Qt::AlignCenter + + + + + + + next + + + + + + + + + + Locale + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + + + + + + 9 + + + + Log Out + + + + 22 + 22 + + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.cpp new file mode 100644 index 00000000..ea074a59 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.cpp @@ -0,0 +1,279 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "ItemWidget.h" +#include +#include +#include "../../LSession.h" + + +ItemWidget::ItemWidget(QWidget *parent, QString itemPath, QString type, bool goback) : QFrame(parent){ + createWidget(); + //Now fill it appropriately + bool inHome = type.endsWith("-home"); //internal code + if(inHome){ type = type.remove("-home"); } + if(itemPath.endsWith(".desktop") || type=="app"){ + XDGDesktop item(itemPath, this); + gooditem = item.isValid(); + //qDebug() << "Good Item:" << gooditem << itemPath; + if(gooditem){ + icon->setPixmap( LXDG::findIcon(item.icon, "preferences-system-windows-actions").pixmap(32,32) ); + iconPath = item.icon; + text = item.name; + if(!item.genericName.isEmpty() && item.name!=item.genericName){ text.append("
-- "+item.genericName+""); } + name->setText(text); + name->setToolTip(item.comment); + setupActions(&item); + }else{ + return; + } + }else if(type=="dir"){ + actButton->setVisible(false); + if(itemPath.endsWith("/")){ itemPath.chop(1); } + if(goback){ + icon->setPixmap( LXDG::findIcon("go-previous","").pixmap(64,64) ); + iconPath = "go-previous"; + text = tr("Go Back"); + name->setText( text ); + }else{ + icon->setPixmap( LXDG::findIcon("folder","").pixmap(64,64) ); + iconPath = "folder"; + name->setText( itemPath.section("/",-1)); + text = itemPath.section("/",-1); + } + }else if(type.startsWith("chcat::::")){ + //Category given + actButton->setVisible(false); + iconPath = LXDG::DesktopCatToIcon(type.section("::::",1,50)); + if(goback){ iconPath = "go-previous"; type = "chcat::::"; itemPath = "("+itemPath+")"; } + icon->setPixmap( LXDG::findIcon(iconPath,"applications-other").pixmap(64,64) ); + name->setText(itemPath); + text = itemPath; + icon->setWhatsThis(type); + linkPath = type; + }else{ + actButton->setVisible(false); + if(itemPath.endsWith("/")){ itemPath.chop(1); } + if(QFileInfo(itemPath).isDir()){ + type = "dir"; + icon->setPixmap( LXDG::findIcon("folder","").pixmap(64,64) ); + iconPath = "folder"; + }else if(LUtils::imageExtensions().contains(itemPath.section("/",-1).section(".",-1).toLower()) ){ + icon->setPixmap( QIcon(itemPath).pixmap(64,64) ); + }else{ + if( LUtils::isValidBinary(itemPath) ){ icon->setPixmap( LXDG::findIcon(type, "application-x-executable").pixmap(64,64) ); } + else{ icon->setPixmap( LXDG::findMimeIcon(itemPath.section("/",-1)).pixmap(64,64) ); } + } + name->setText( itemPath.section("/",-1) ); //this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); + text = itemPath.section("/",-1) ; + } + icon->setWhatsThis(itemPath); + if(!goback){ this->setWhatsThis(name->text()); } + isDirectory = (type=="dir"); //save this for later + if(LDesktopUtils::isFavorite(itemPath)){ + linkPath = itemPath; + isShortcut=true; + }else if( inHome ){//|| itemPath.section("/",0,-2)==QDir::homePath()+"/Desktop" ){ + isShortcut = true; + }else{ + isShortcut = false; + } + if(isShortcut && name->toolTip().isEmpty()){ + name->setToolTip(icon->whatsThis()); //also allow the user to see the full shortcut path + } + //Now setup the button appropriately + setupContextMenu(); +} + +// - Application constructor +ItemWidget::ItemWidget(QWidget *parent, XDGDesktop *item) : QFrame(parent){ + createWidget(); + if(item==0){ gooditem = false; return; } + isDirectory = false; + if(LDesktopUtils::isFavorite(item->filePath)){ + linkPath = item->filePath; + isShortcut=true; + }else if( item->filePath.section("/",0,-2)==QDir::homePath()+"/Desktop" ){ + isShortcut = true; + }else{ + isShortcut = false; + } + if(isShortcut){ + name->setToolTip(icon->whatsThis()); //also allow the user to see the full shortcut path + } + //Now fill it appropriately + icon->setPixmap( LXDG::findIcon(item->icon,"preferences-system-windows-actions").pixmap(64,64) ); + text = item->name; + if(!item->genericName.isEmpty() && item->name!=item->genericName){ text.append("
-- "+item->genericName+""); } + name->setText(text); + name->setToolTip(item->comment); + this->setWhatsThis(item->name); + icon->setWhatsThis(item->filePath); + iconPath = item->icon; + //Now setup the buttons appropriately + setupContextMenu(); + setupActions(item); +} + +ItemWidget::~ItemWidget(){ + icon->setPixmap(QPixmap()); //make sure the pixmap is cleared from memory too + actButton->deleteLater(); + contextMenu->clear(); + contextMenu->deleteLater(); + if(actButton->menu()!=0){ + for(int i=0; imenu()->actions().length(); i++){ + actButton->menu()->actions().at(i)->deleteLater(); + } + actButton->menu()->deleteLater(); + } + actButton->deleteLater(); + icon->deleteLater(); + name->deleteLater(); + menureset->deleteLater(); + linkPath.clear(); iconPath.clear(); text.clear(); +} + +void ItemWidget::triggerItem(){ + ItemClicked(); +} + +void ItemWidget::createWidget(){ + //Initialize the widgets + gooditem = true; + menuopen = false; + menureset = new QTimer(this); + menureset->setSingleShot(true); + menureset->setInterval(1000); //1 second + this->setContentsMargins(0,0,0,0); + contextMenu = new QMenu(this); + connect(contextMenu, SIGNAL(aboutToShow()), this, SLOT(actionMenuOpen()) ); + connect(contextMenu, SIGNAL(aboutToHide()), this, SLOT(actionMenuClosed()) ); + actButton = new QToolButton(this); + actButton->setPopupMode(QToolButton::InstantPopup); + actButton->setArrowType(Qt::DownArrow); + icon = new QLabel(this); + name = new QLabel(this); + name->setWordWrap(true); + name->setTextFormat(Qt::RichText); + name->setTextInteractionFlags(Qt::NoTextInteraction); + //Add them to the layout + this->setLayout(new QHBoxLayout(this)); + this->layout()->setContentsMargins(1,1,1,1); + this->layout()->addWidget(icon); + this->layout()->addWidget(actButton); + this->layout()->addWidget(name); + //Set a custom object name so this can be tied into the Lumina Theme stylesheets + this->setObjectName("LuminaItemWidget"); +} + +void ItemWidget::setupContextMenu(){ + //Now refresh the context menu + contextMenu->clear(); + if(!QFile::exists(QDir::homePath()+"/Desktop/"+icon->whatsThis().section("/",-1)) ){ + //Does not have a desktop link + contextMenu->addAction( LXDG::findIcon("preferences-desktop-icons",""), tr("Pin to Desktop"), this, SLOT(PinToDesktop()) ); + } + //Favorite Item + if( LDesktopUtils::isFavorite(icon->whatsThis()) ){ //Favorite Item - can always remove this + contextMenu->addAction( LXDG::findIcon("edit-delete",""), tr("Remove from Favorites"), this, SLOT(RemoveFavorite()) ); + }else{ + //This file does not have a shortcut yet -- allow the user to add it + contextMenu->addAction( LXDG::findIcon("bookmark-toolbar",""), tr("Add to Favorites"), this, SLOT(AddFavorite()) ); + } + //QuickLaunch Item + if(LSession::handle()->sessionSettings()->value("QuicklaunchApps",QStringList()).toStringList().contains(icon->whatsThis()) ){ //Favorite Item - can always remove this + contextMenu->addAction( LXDG::findIcon("edit-delete",""), tr("Remove from Quicklaunch"), this, SLOT(RemoveQL()) ); + }else{ + //This file does not have a shortcut yet -- allow the user to add it + contextMenu->addAction( LXDG::findIcon("quickopen",""), tr("Add to Quicklaunch"), this, SLOT(AddQL()) ); + } +} + +void ItemWidget::setupActions(XDGDesktop *app){ + if(app==0 || app->actions.isEmpty()){ actButton->setVisible(false); return; } + //Actions Available - go ahead and list them all + actButton->setMenu( new QMenu(this) ); + for(int i=0; iactions.length(); i++){ + QAction *act = new QAction(LXDG::findIcon(app->actions[i].icon, app->icon), app->actions[i].name, this); + act->setToolTip(app->actions[i].ID); + act->setWhatsThis(app->actions[i].ID); + actButton->menu()->addAction(act); + } + connect(actButton->menu(), SIGNAL(triggered(QAction*)), this, SLOT(actionClicked(QAction*)) ); + connect(actButton->menu(), SIGNAL(aboutToShow()), this, SLOT(actionMenuOpen()) ); + connect(actButton->menu(), SIGNAL(aboutToHide()), this, SLOT(actionMenuClosed()) ); + connect(menureset, SIGNAL(timeout()), this, SLOT(resetmenuflag()) ); +} + +void ItemWidget::updateItems(){ + //update the text/icon to match sizes + int H = 2.3*name->fontMetrics().height(); //make sure the height is large enough for two lines + icon->setFixedSize(QSize(H-4, H-4)); + actButton->setFixedSize( QSize( (H-4)/2, H-4) ); + QStringList newname = text.split("
"); + for(int i=0; ifontMetrics().elidedText(newname[i], Qt::ElideRight, name->width()); } + name->setText( newname.join("
") ); + //Now reload the icon if necessary + if(icon->pixmap()!=0){ + if(icon->pixmap()->size().height() < (H-4) ){ + if(iconPath.isEmpty()){ + //Use item path (thumbnail or mimetype) + if(LUtils::imageExtensions().contains(icon->whatsThis().section("/",-1).section(".",-1).toLower()) ){ + icon->setPixmap( QIcon(icon->whatsThis()).pixmap(H-4,H-4).scaledToHeight(H-4,Qt::SmoothTransformation) ); + }else{ + icon->setPixmap( LXDG::findMimeIcon(icon->whatsThis().section("/",-1)).pixmap(H-4,H-4).scaledToHeight(H-4,Qt::SmoothTransformation) ); + } + }else{ + icon->setPixmap( LXDG::findIcon(iconPath,"preferences-system-windows-actions").pixmap(H-4,H-4).scaledToHeight(H-4,Qt::SmoothTransformation) ); + } + }else if(icon->pixmap()->size().height() > (H-4) ){ + icon->setPixmap( icon->pixmap()->scaled(H-4, H-4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation) ); + } + } +} + +void ItemWidget::PinToDesktop(){ + qDebug() << "Create Link on Desktop:" << icon->whatsThis(); + bool ok = QFile::link(icon->whatsThis(), QDir::homePath()+"/Desktop/"+icon->whatsThis().section("/",-1)); + qDebug() << " - " << (ok ? "Success": "Failure"); +} + +void ItemWidget::RemoveFavorite(){ + LDesktopUtils::removeFavorite(icon->whatsThis()); + linkPath.clear(); + emit RemovedShortcut(); +} + +void ItemWidget::AddFavorite(){ + if( LDesktopUtils::addFavorite(icon->whatsThis()) ){ + linkPath = icon->whatsThis(); + emit NewShortcut(); + } + +} +void ItemWidget::RemoveQL(){ + qDebug() << "Remove QuickLaunch Button:" << icon->whatsThis(); + emit toggleQuickLaunch(icon->whatsThis(), false); +} + +void ItemWidget::AddQL(){ + qDebug() << "Add QuickLaunch Button:" << icon->whatsThis(); + emit toggleQuickLaunch(icon->whatsThis(), true); +} + + +void ItemWidget::ItemClicked(){ + if(!linkPath.isEmpty()){ emit RunItem(linkPath); } + else{ emit RunItem(icon->whatsThis()); } +} + +void ItemWidget::actionClicked(QAction *act){ + actButton->menu()->hide(); + QString cmd = "lumina-open -action \""+act->whatsThis()+"\" \"%1\""; + if(!linkPath.isEmpty()){ cmd = cmd.arg(linkPath); } + else{ cmd = cmd.arg(icon->whatsThis()); } + emit RunItem(cmd); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.h new file mode 100644 index 00000000..11394dd6 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/ItemWidget.h @@ -0,0 +1,98 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This item widget manages a single file/directory +//=========================================== +#ifndef _LUMINA_PANEL_SYSTEM_START_ITEM_WIDGET_H +#define _LUMINA_PANEL_SYSTEM_START_ITEM_WIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class ItemWidget : public QFrame{ + Q_OBJECT +public: + //Overloaded Constructors for various uses + // - Favorites (path/type) + ItemWidget(QWidget *parent=0, QString itemPath="", QString type="unknown", bool goback=false); + // - Generic Apps + ItemWidget(QWidget *parent=0, XDGDesktop *item= 0); + + ~ItemWidget(); + + bool gooditem; + + void triggerItem(); //trigger this item - just as if it was clicked on + +private: + QToolButton *actButton; + QMenu *contextMenu; + QLabel *icon, *name; + bool isDirectory, isShortcut, menuopen; + QString linkPath, iconPath, text; + QTimer *menureset; + + void createWidget(); + + void setupContextMenu(); + void setupActions(XDGDesktop*); + + void updateItems(); //update the text/icon to match sizes + +private slots: + void PinToDesktop(); + void RemoveFavorite(); + void AddFavorite(); + void RemoveQL(); + void AddQL(); + void ItemClicked(); + void actionClicked(QAction*); + //Functions to fix the submenu open/close issues + void actionMenuOpen(){ + if(menureset->isActive()){ menureset->stop(); } + menuopen = true; + } + void resetmenuflag(){ menuopen = false; } //tied to the "menureset" timer + void actionMenuClosed(){ menureset->start(); } + + +protected: + void mouseReleaseEvent(QMouseEvent *event){ + if(menuopen){ resetmenuflag(); } //skip this event if a submenu was open + else if(event->button() == Qt::RightButton && !icon->whatsThis().startsWith("chcat::::") ){ + menuopen = true; + setupContextMenu(); + contextMenu->popup(event->globalPos()); + }else if(event->button() != Qt::NoButton){ ItemClicked(); } + } + + void resizeEvent(QResizeEvent *ev){ + updateItems(); //update the sizing of everything + QFrame::resizeEvent(ev); // do the normal procedures + } + +signals: + void NewShortcut(); + void RemovedShortcut(); + void RunItem(QString cmd); + void toggleQuickLaunch(QString path, bool ok); + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.cpp new file mode 100644 index 00000000..f44add77 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.cpp @@ -0,0 +1,137 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LStartButton.h" +#include "../../LSession.h" + +#include +#include //This contains the "ResizeMenu" class + +LStartButtonPlugin::LStartButtonPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setPopupMode(QToolButton::DelayedPopup); //make sure it runs the update routine first + connect(button, SIGNAL(clicked()), this, SLOT(openMenu())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + menu = new ResizeMenu(this); + menu->setContentsMargins(1,1,1,1); + connect(menu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + connect(menu, SIGNAL(MenuResized(QSize)), this, SLOT(SaveMenuSize(QSize)) ); + startmenu = new StartMenu(this); + connect(startmenu, SIGNAL(CloseMenu()), this, SLOT(closeMenu()) ); + connect(startmenu, SIGNAL(UpdateQuickLaunch(QStringList)), this, SLOT(updateQuickLaunch(QStringList))); + menu->setContents(startmenu); + QSize saved = LSession::handle()->DesktopPluginSettings()->value("panelPlugs/"+this->type()+"/MenuSize", QSize(0,0)).toSize(); + if(!saved.isNull()){ startmenu->setFixedSize(saved); } //re-load the previously saved value + + button->setMenu(menu); + connect(menu, SIGNAL(aboutToHide()), this, SLOT(updateButtonVisuals()) ); + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes + QTimer::singleShot(0, startmenu, SLOT(ReLoadQuickLaunch()) ); + //Setup the global shortcut handling for opening the start menu + connect(QApplication::instance(), SIGNAL(StartButtonActivated()), this, SLOT(shortcutActivated()) ); + LSession::handle()->registerStartButton(this->type()); +} + +LStartButtonPlugin::~LStartButtonPlugin(){ + LSession::handle()->unregisterStartButton(this->type()); +} + +void LStartButtonPlugin::updateButtonVisuals(){ + button->setToolTip(tr("")); + button->setText( SYSTEM::user() ); + button->setIcon( LXDG::findIcon("start-here-lumina","Lumina-DE") ); //force icon refresh +} + +void LStartButtonPlugin::updateQuickLaunch(QStringList apps){ + //First clear any obsolete apps + QStringList old; + //qDebug() << "Update QuickLaunch Buttons"; + for(int i=0; iwhatsThis()) ){ + //App was removed + QUICKL.takeAt(i)->deleteLater(); + i--; + }else{ + //App still listed - update the button + old << QUICKL[i]->whatsThis(); //add the list of current buttons + LFileInfo info(QUICKL[i]->whatsThis()); + QUICKL[i]->setIcon( LXDG::findIcon(info.iconfile(),"unknown") ); + if(info.isDesktopFile()){ QUICKL[i]->setToolTip( info.XDG()->name ); } + else{ QUICKL[i]->setToolTip( info.fileName() ); } + } + } + //Now go through and create any new buttons + for(int i=0; isetIcon( LXDG::findIcon( info.iconfile() ) ); + if(info.isDesktopFile()){ tmp->setToolTip( info.XDG()->name ); } + else{ tmp->setToolTip( info.fileName() ); } + //Now add the button to the layout and connect the signal/slots + this->layout()->insertWidget(i+1,tmp); //"button" is always in slot 0 + connect(tmp, SIGNAL(Launch(QString)), this, SLOT(LaunchQuick(QString)) ); + connect(tmp, SIGNAL(Remove(QString)), this, SLOT(RemoveQuick(QString)) ); + } + } + //qDebug() << " - Done updateing QuickLaunch Buttons"; + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes +} + +void LStartButtonPlugin::LaunchQuick(QString file){ + //Need to get which button was clicked + //qDebug() << "Quick Launch triggered:" << file; + if(!file.isEmpty()){ + LSession::LaunchApplication("lumina-open \""+file+"\""); + emit MenuClosed(); + } +} + +void LStartButtonPlugin::RemoveQuick(QString file){ + //qDebug() << "Remove Quicklaunch Button:" << file; + if(!file.isEmpty()){ + startmenu->UpdateQuickLaunch(file, false); //always a removal + emit MenuClosed(); + } +} + +void LStartButtonPlugin::SaveMenuSize(QSize sz){ + //Save this size for the menu + LSession::handle()->DesktopPluginSettings()->setValue("panelPlugs/"+this->type()+"/MenuSize", sz); +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void LStartButtonPlugin::openMenu(){ + if(menu->isVisible()){ return; } //don't re-show it - already open + //TESTING CODE TO SEE IF THIS MAKES IT RECOVER MEMORY + /*StartMenu *old = startmenu; + startmenu = new StartMenu(this); + connect(startmenu, SIGNAL(CloseMenu()), this, SLOT(closeMenu()) ); + connect(startmenu, SIGNAL(UpdateQuickLaunch(QStringList)), this, SLOT(updateQuickLaunch(QStringList))); + menu->setContents(startmenu); + if(old!=0){ old->deleteLater(); }*/ +//-------- + startmenu->UpdateMenu(); + button->showMenu(); +} + +void LStartButtonPlugin::closeMenu(){ + menu->hide(); +} + +void LStartButtonPlugin::shortcutActivated(){ + if(LSession::handle()->registerStartButton(this->type())){ + if(menu->isVisible()){ closeMenu(); } + else{ this->activateWindow(); openMenu(); } + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.h new file mode 100644 index 00000000..d46bb1be --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/LStartButton.h @@ -0,0 +1,113 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin is the main button that allow the user to run +// applications or logout of the desktop +//=========================================== +#ifndef _LUMINA_DESKTOP_START_MENU_PLUGIN_H +#define _LUMINA_DESKTOP_START_MENU_PLUGIN_H + +// Qt includes +#include +#include +#include +#include +#include +#include + +// Lumina-desktop includes +//#include "../../Globals.h" +#include "../LPPlugin.h" //main plugin widget + +// libLumina includes +#include +#include +#include + +#include "StartMenu.h" + +//Simple Tool Button For QuickLaunch Items +class LQuickLaunchButton : public QToolButton{ + Q_OBJECT +signals: + void Launch(QString); + void Remove(QString); + +private slots: + void rmentry(){ + emit Remove(this->whatsThis()); + } + void launchentry(){ + emit Launch(this->whatsThis()); + } + +public: + LQuickLaunchButton(QString path, QWidget* parent = 0) : QToolButton(parent){ + this->setWhatsThis(path); + this->setMenu(new QMenu(this)); + this->setContextMenuPolicy(Qt::CustomContextMenu); + this->menu()->addAction( LXDG::findIcon("edit-delete",""), tr("Remove from Quicklaunch"), this, SLOT(rmentry()) ); + connect(this, SIGNAL(clicked()), this, SLOT(launchentry()) ); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu()) ); + } + ~LQuickLaunchButton(){} + +}; + +// PANEL PLUGIN BUTTON +class LStartButtonPlugin : public LPPlugin{ + Q_OBJECT + +public: + LStartButtonPlugin(QWidget *parent = 0, QString id = "systemstart", bool horizontal=true); + ~LStartButtonPlugin(); + +private: + ResizeMenu *menu; + //QWidgetAction *mact; + StartMenu *startmenu; + QToolButton *button; + QList QUICKL; + +private slots: + void openMenu(); + void closeMenu(); + void shortcutActivated(); + + void updateButtonVisuals(); + + void updateQuickLaunch(QStringList); + void LaunchQuick(QString); + void RemoveQuick(QString); + void SaveMenuSize(QSize); + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + for(int i=0; isetIconSize(QSize(this->height(), this->height())); } + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + for(int i=0; isetIconSize(QSize(this->width(), this->width())); } + } + this->layout()->update(); + updateButtonVisuals(); + } + + void LocaleChange(){ + updateButtonVisuals(); + if(startmenu!=0){ startmenu->UpdateAll(); } + } + + void ThemeChange(){ + updateButtonVisuals(); + if(startmenu!=0){ startmenu->UpdateAll(); } + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.cpp new file mode 100644 index 00000000..d05ba22f --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.cpp @@ -0,0 +1,720 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "StartMenu.h" +#include "ui_StartMenu.h" +//#include + +#include +#include "../../LSession.h" +#include +#include + +#include "ItemWidget.h" +//#define SSAVER QString("xscreensaver-demo") + +StartMenu::StartMenu(QWidget *parent) : QWidget(parent), ui(new Ui::StartMenu){ + ui->setupUi(this); //load the designer file + this->setMouseTracking(true); + searchTimer = new QTimer(this); + searchTimer->setInterval(300); //~1/3 second + searchTimer->setSingleShot(true); + connect(searchTimer, SIGNAL(timeout()), this, SLOT(startSearch()) ); + connect(LSession::handle()->applicationMenu(), SIGNAL(AppMenuUpdated()), this, SLOT(UpdateApps()) ); + connect(LSession::handle(), SIGNAL(FavoritesChanged()), this, SLOT(UpdateFavs()) ); + //Need to load the last used setting of the application list + QString state = LSession::handle()->DesktopPluginSettings()->value("panelPlugs/systemstart/showcategories", "partial").toString(); + if(state=="partial"){ui->check_apps_showcats->setCheckState(Qt::PartiallyChecked); } + else if(state=="true"){ ui->check_apps_showcats->setCheckState(Qt::Checked); } + else{ ui->check_apps_showcats->setCheckState(Qt::Unchecked); } + connect(ui->check_apps_showcats, SIGNAL(stateChanged(int)), this, SLOT(catViewChanged()) ); + UpdateAll(); + QTimer::singleShot(10, this,SLOT(UpdateApps())); + QTimer::singleShot(10, this, SLOT(UpdateFavs())); +} + +StartMenu::~StartMenu(){ + +} + +void StartMenu::UpdateAll(){ + //Update all the icons/text on all the pages + + // Update Text + ui->retranslateUi(this); + + //Update Icons + ui->tool_goto_apps->setIcon(LXDG::findIcon("system-run","")); + ui->tool_goto_settings->setIcon(LXDG::findIcon("preferences-system","")); + ui->tool_launch_fm->setIcon(LXDG::findIcon("user-home","")); + ui->tool_launch_desksettings->setIcon(LXDG::findIcon("preferences-desktop","")); + ui->tool_lock->setIcon(LXDG::findIcon("system-lock-screen","")); + ui->tool_goto_logout->setIcon(LXDG::findIcon("system-log-out","")); + ui->tool_back->setIcon(LXDG::findIcon("go-previous","")); + ui->tool_launch_deskinfo->setIcon(LXDG::findIcon("system-help","")); + + ui->tool_launch_mixer->setIcon( LXDG::findIcon("preferences-desktop-sound","") ); + ui->label_bright_icon->setPixmap( LXDG::findIcon("preferences-system-power-management","").pixmap(ui->tool_goto_apps->iconSize()) ); + ui->label_locale_icon->setPixmap( LXDG::findIcon("preferences-desktop-locale","").pixmap(ui->tool_goto_apps->iconSize()) ); + ui->tool_set_nextwkspace->setIcon(LXDG::findIcon("go-next-view","")); + ui->tool_set_prevwkspace->setIcon(LXDG::findIcon("go-previous-view","")); + ui->tool_logout->setIcon(LXDG::findIcon("system-log-out","")); + ui->tool_restart->setIcon(LXDG::findIcon("system-reboot","")); + ui->tool_shutdown->setIcon(LXDG::findIcon("system-shutdown","")); + ui->tool_suspend->setIcon(LXDG::findIcon("system-suspend","")); + + //Update Visibility of system/session/OS options + // -- Control Panel + QString tmp = LOS::ControlPanelShortcut(); + if(QFile::exists(tmp)){ + ui->tool_launch_controlpanel->setWhatsThis(tmp); + //Now read the file to see which icon to use + XDGDesktop desk(tmp); + if(desk.isValid()){ + ui->tool_launch_controlpanel->setIcon(LXDG::findIcon(desk.icon,"preferences-other")); + }else{ ui->tool_launch_controlpanel->setVisible(false); } + }else{ ui->tool_launch_controlpanel->setVisible(false); } + // -- App Store + tmp = LOS::AppStoreShortcut(); + if(QFile::exists(tmp)){ + ui->tool_launch_store->setWhatsThis(tmp); + //Now read the file to see which icon to use + XDGDesktop desk(tmp); + if(desk.isValid()){ + ui->tool_launch_store->setIcon(LXDG::findIcon(desk.icon,"utilities-file-archiver")); + }else{ ui->tool_launch_store->setVisible(false); } + }else{ ui->tool_launch_store->setVisible(false); } + //Audio Controls + ui->frame_audio->setVisible( LOS::audioVolume() >=0 ); + //Brightness controls + ui->frame_bright->setVisible( LOS::ScreenBrightness() >=0 ); + //Shutdown/restart + bool ok = LOS::userHasShutdownAccess(); + ui->frame_leave_system->setWhatsThis(ok ? "allowed": ""); + ui->frame_leave_system->setVisible(ok); + //Battery Availability + ok = LOS::hasBattery(); + ui->label_status_battery->setWhatsThis(ok ? "allowed": ""); + ui->label_status_battery->setVisible(ok); + //Locale availability + QStringList locales = LUtils::knownLocales(); + ui->stackedWidget->setCurrentWidget(ui->page_main); //need to ensure the settings page is not active + ui->combo_locale->clear(); + QString curr = LUtils::currentLocale(); + //qDebug() << "Update Locales:" << locales; + //qDebug() << "Current Locale:" << curr; + for(int i=0; icombo_locale->addItem(loc.nativeLanguageName() +" ("+locales[i]+")", locales[i]); //Make the display text prettier later + if(locales[i] == curr || locales[i] == curr.section("_",0,0) ){ + //Current Locale + ui->combo_locale->setCurrentIndex(ui->combo_locale->count()-1); //the last item in the list right now + } + } + ui->frame_locale->setVisible(locales.length() > 1); + //User Name in status bar + ui->label_status->setText( QString::fromLocal8Bit(getlogin()) ); + //Lumina Utilities + ui->tool_launch_desksettings->setVisible(LUtils::isValidBinary("lumina-config")); + ui->tool_launch_deskinfo->setVisible(LUtils::isValidBinary("lumina-info")); + +} + +void StartMenu::UpdateMenu(bool forceall){ + //qDebug() << "Update Menu" << forceall; + ui->line_search->clear(); + if(forceall){ UpdateAll(); } + //Quick update routine before the menu is made visible + //qDebug() << "update favs"; + //UpdateFavs(); + //qDebug() << "check page"; + if(ui->stackedWidget->currentWidget() != ui->page_main){ + ui->stackedWidget->setCurrentWidget(ui->page_main); //just show the main page + }else{ + on_stackedWidget_currentChanged(0); //refresh/update the main page every time + } + //qDebug() << "done"; +} + +void StartMenu::ReLoadQuickLaunch(){ + emit UpdateQuickLaunch( LSession::handle()->sessionSettings()->value("QuicklaunchApps",QStringList()).toStringList() ); +} + +void StartMenu::UpdateQuickLaunch(QString path, bool keep){ + QStringList QL = LSession::handle()->sessionSettings()->value("QuicklaunchApps",QStringList()).toStringList(); + if(keep){QL << path; } + else{ QL.removeAll(path); } + QL.removeDuplicates(); + LSession::handle()->sessionSettings()->setValue("QuicklaunchApps",QL); + //LSession::handle()->sessionSettings()->sync(); + emit UpdateQuickLaunch(QL); +} + +// ========================== +// PRIVATE FUNCTIONS +// ========================== +/*void StartMenu::deleteChildren(QWidget *obj){ + if(obj->layout()==0){ + for(int i=0; ichildren().count(); i++){ + obj->children().at(i)->deleteLater(); + } + }else{ + + } +}*/ + +void StartMenu::ClearScrollArea(QScrollArea *area){ + //QWidget *old = area->takeWidget(); + //qDebug() << "Clear Scroll Area:"; + //if(old->layout()!=0){ qDebug() << " - Number of items in layout:" << old->layout()->count(); } + //qDebug() << " - Number of Children:" << old->children().count(); + //deleteChildren(old); //make sure we *fully* delete these items to save memory + //old->deleteLater(); + if(area == ui->scroll_favs){ + area->takeWidget()->deleteLater(); + } + if(area->widget()==0){ + area->setWidget( new QWidget(area) ); //create a new widget in the scroll area + } + if(area->widget()->layout()==0){ + QVBoxLayout *layout = new QVBoxLayout(area->widget()); + layout->setSpacing(2); + layout->setContentsMargins(3,1,3,1); + layout->setDirection(QBoxLayout::TopToBottom); + layout->setAlignment(Qt::AlignTop); + area->widget()->setContentsMargins(0,0,0,0); + area->widget()->setLayout(layout); + } + //Now clear the items in the layout + while( area->widget()->layout()->count() >0 ){ + QLayoutItem *it = area->widget()->layout()->takeAt(0); + //Need to delete both the widget and the layout item + if(it->widget()!=0){ it->widget()->deleteLater(); } + delete it; + } +} + +void StartMenu::SortScrollArea(QScrollArea *area){ + //qDebug() << "Sorting Scroll Area:"; + //Sort all the items in the scroll area alphabetically + QLayout *lay = area->widget()->layout(); + QStringList items; + for(int i=0; icount(); i++){ + items << lay->itemAt(i)->widget()->whatsThis(); + } + + items.sort(); + //qDebug() << " - Sorted Items:" << items; + for(int i=0; icount(); j++){ + //Find this item + if(lay->itemAt(j)->widget()->whatsThis()==items[i]){ + //Found it - now move it if necessary + //qDebug() << "Found Item:" << items[i] << i << j; + lay->addItem( lay->takeAt(j) ); + break; + } + } + } +} + +void StartMenu::do_search(QString search, bool force){ + search = search.simplified(); //remove unneccesary whitespace + if(search == CSearch && !force){ + //nothing new - just ensure the page is visible + if(ui->stackedWidget->currentWidget()!=ui->page_search ){ ui->stackedWidget->setCurrentWidget(ui->page_search); } + return; + }else if(search.isEmpty()){ + CSearch.clear(); + if(ui->stackedWidget->currentWidget()==ui->page_search ){ on_tool_back_clicked(); } + return; + } + //Got a search term - check it + CSearch = search; //save this for comparison later + qDebug() << "Search for term:" << search; + ClearScrollArea(ui->scroll_search); + topsearch.clear(); + //Now find any items which match the search + QStringList found; //syntax: [::::::::] + QString tmp = search; + if(LUtils::isValidBinary(tmp)){ found << "0::::application/x-executable::::"+tmp; } + QList apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); + for(int i=0; iname.toLower()==search.toLower()){ priority = 10; } + else if(apps[i]->name.startsWith(search, Qt::CaseInsensitive)){ priority = 15; } + else if(apps[i]->name.contains(search, Qt::CaseInsensitive)){ priority = 19; } + else if(apps[i]->genericName.contains(search, Qt::CaseInsensitive)){ priority = 20; } + else if(apps[i]->comment.contains(search, Qt::CaseInsensitive)){ priority = 30; } + //Can add other filters here later + + if(priority>0){ + found << QString::number(priority)+"::::app::::"+apps[i]->filePath; + } + } + found.sort(Qt::CaseInsensitive); //sort by priority/type (lower numbers are higher on list) + //qDebug() << "Sorted Items:" << found; + //Now add the items to the menu in order + for(int i=0; iscroll_favs->widget(), &item); } + }else{ + it = new ItemWidget(ui->scroll_favs->widget(), found[i].section("::::",2,-1), found[i].section("::::",1,1) ); + } + if(it==0){ continue; } + if(!it->gooditem){ it->deleteLater(); continue; } //invalid for some reason + ui->scroll_search->widget()->layout()->addWidget(it); + connect(it, SIGNAL(NewShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(toggleQuickLaunch(QString, bool)), this, SLOT(UpdateQuickLaunch(QString, bool)) ); + if(i%3==0){ + QApplication::processEvents(); + if(searchTimer->isActive()){ return; } //search changed - go ahead and stop here + } + } + ui->stackedWidget->setCurrentWidget(ui->page_search); +} + +bool StartMenu::promptAboutUpdates(bool &skip){ + QString pending = LOS::systemPendingUpdates(); + if(pending.isEmpty()){ skip = false; } //continue without skip + else{ + QMessageBox dlg(QMessageBox::Question, tr("Apply Updates?"), tr("You have system updates waiting to be applied! Do you wish to install them now?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, this); + dlg.setDetailedText(pending); + dlg.setDefaultButton(QMessageBox::Yes); + dlg.show(); + int ret = dlg.exec(); + if(ret == QMessageBox::Cancel){ return false; } //do not continue + else{ skip = (ret==QMessageBox::No); } + } + return true; +} + +// ======================== +// PRIVATE SLOTS +// ======================== +void StartMenu::LaunchItem(QString path, bool fix){ + if(path.startsWith("chcat::::")){ + ChangeCategory(path.section("::::",1,50)); + return; + } + qDebug() << "Launching Item:" << path << fix; + if(!path.isEmpty()){ + qDebug() << "Launch Application:" << path; + if( fix && !path.startsWith("lumina-open") ){ LSession::LaunchApplication("lumina-open \""+path+"\""); } + else{ LSession::LaunchApplication(path); } + emit CloseMenu(); //so the menu container will close + } +} + +void StartMenu::ChangeCategory(QString cat){ + //This only happens on user interaction - make sure to run the update routine in a separate thread + CCat = cat; + UpdateApps(); + //QtConcurrent::run(this, &StartMenu::UpdateApps); +} + +//Listing Update routines +void StartMenu::UpdateApps(){ + ClearScrollArea(ui->scroll_apps); + //Now assemble the apps list + //qDebug() << "Update Apps:";// << CCat << ui->check_apps_showcats->checkState(); + if(ui->check_apps_showcats->checkState() == Qt::PartiallyChecked){ + //qDebug() << " - Partially Checked"; + //Show a single page of apps, but still divided up by categories + CCat.clear(); + QStringList cats = LSession::handle()->applicationMenu()->currentAppHash()->keys(); + cats.sort(); + cats.removeAll("All"); + for(int c=0; c apps = LSession::handle()->applicationMenu()->currentAppHash()->value(cats[c]); + if(apps.isEmpty()){ continue; } + //Add the category label to the scroll + QLabel *catlabel = new QLabel(""+cats[c]+"",ui->scroll_apps->widget()); + catlabel->setAlignment(Qt::AlignCenter); + ui->scroll_apps->widget()->layout()->addWidget(catlabel); + //Now add all the apps for this category + for(int i=0; iscroll_apps->widget(), apps[i] ); + if(!it->gooditem){ qDebug() << "Invalid Item:"; it->deleteLater(); continue; } //invalid for some reason + ui->scroll_apps->widget()->layout()->addWidget(it); + connect(it, SIGNAL(NewShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(toggleQuickLaunch(QString, bool)), this, SLOT(UpdateQuickLaunch(QString, bool)) ); + } + } + + }else if(ui->check_apps_showcats->checkState() == Qt::Checked){ + //qDebug() << " - Checked"; + //Only show categories to start with - and have the user click-into a cat to see apps + if(CCat.isEmpty()){ + //No cat selected yet - show cats only + QStringList cats = LSession::handle()->applicationMenu()->currentAppHash()->keys(); + cats.sort(); + cats.removeAll("All"); //This is not a "real" category + for(int c=0; cscroll_apps->widget(), cats[c], "chcat::::"+cats[c] ); + if(!it->gooditem){ qDebug() << "Invalid Item:";it->deleteLater(); continue; } //invalid for some reason + ui->scroll_apps->widget()->layout()->addWidget(it); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + } + }else{ + //qDebug() << "Show Apps For category:" << CCat; + //Show the "go back" button + ItemWidget *it = new ItemWidget(ui->scroll_apps->widget(), CCat, "chcat::::"+CCat, true); + //if(!it->gooditem){ continue; } //invalid for some reason + ui->scroll_apps->widget()->layout()->addWidget(it); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + //Show apps for this cat + QList apps = LSession::handle()->applicationMenu()->currentAppHash()->value(CCat); + for(int i=0; iscroll_apps->widget(), apps[i] ); + if(!it->gooditem){ qDebug() << "Invalid Item:"; it->deleteLater(); continue; } //invalid for some reason + ui->scroll_apps->widget()->layout()->addWidget(it); + connect(it, SIGNAL(NewShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(toggleQuickLaunch(QString, bool)), this, SLOT(UpdateQuickLaunch(QString, bool)) ); + } + } + + }else{ + //qDebug() << " - Not Checked"; + //No categories at all - just alphabetize all the apps + QList apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); + CCat.clear(); + //Now add all the apps for this category + for(int i=0; iscroll_apps->widget(), apps[i] ); + if(!it->gooditem){ it->deleteLater(); continue; } //invalid for some reason + ui->scroll_apps->widget()->layout()->addWidget(it); + connect(it, SIGNAL(NewShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(toggleQuickLaunch(QString, bool)), this, SLOT(UpdateQuickLaunch(QString, bool)) ); + } + } + + +} + +void StartMenu::UpdateFavs(){ + //SYNTAX NOTE: (per-line) "::::[dir/app/]::::" + QStringList newfavs = LDesktopUtils::listFavorites(); + if(favs == newfavs){ return; } //nothing to do - same as before + favs = newfavs; + ClearScrollArea(ui->scroll_favs); + favs.sort(); + //Iterate over types of favorites + QStringList rest = favs; + QStringList tmp; + for(int type = 0; type<3; type++){ + if(type==0){ tmp = favs.filter("::::app::::"); } //apps first + else if(type==1){ tmp = favs.filter("::::dir::::"); } //dirs next + else{ tmp = rest; } //everything left over + if(type==1){ + SortScrollArea(ui->scroll_favs); + //Need to run a special routine for sorting the apps (already in the widget) + //qDebug() << "Sort App Widgets..."; + // Since each app actually might have a different name listed within the file + /*QLayout *lay = ui->scroll_favs->widget()->layout(); + QStringList items; + for(int i=0; icount(); i++){ + items << lay->itemAt(i)->widget()->whatsThis().toLower(); + } + + items.sort(); + // qDebug() << " - Sorted Items:" << items; + for(int i=0; icount(); j++){ + //Find this item + if(lay->itemAt(j)->widget()->whatsThis().toLower()==items[i]){ + //Found it - now move it if necessary + //qDebug() << "Found Item:" << items[i] << i << j; + lay->addItem( lay->takeAt(j) ); + break; + } + } + }*/ + + }//end of special app sorting routine + tmp.sort(); //Sort alphabetically by name (dirs/files) + for(int i=0; iscroll_favs->widget(), &item); } + }else{ + it = new ItemWidget(ui->scroll_favs->widget(), tmp[i].section("::::",2,-1), tmp[i].section("::::",1,1) ); + } + if(it==0){ continue; } + if(!it->gooditem){ it->deleteLater(); continue; } //invalid for some reason + ui->scroll_favs->widget()->layout()->addWidget(it); + connect(it, SIGNAL(NewShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(UpdateFavs()) ); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(toggleQuickLaunch(QString, bool)), this, SLOT(UpdateQuickLaunch(QString, bool)) ); + } + //QApplication::processEvents(); + } //end loop over types + ui->scroll_favs->update(); + //qDebug() << "End updateFavs"; +} + +// Page update routines +void StartMenu::on_stackedWidget_currentChanged(int val){ + QWidget *page = ui->stackedWidget->widget(val); + ui->tool_back->setVisible(page != ui->page_main); + ui->line_search->setFocus(); + + //Now the page specific updates + if(page == ui->page_main){ + if(!ui->label_status_battery->whatsThis().isEmpty()){ + //Battery available - update the status button + int charge = LOS::batteryCharge(); + QString TT, ICON; + if(charge<=5){ ICON="-caution"; } + else if(charge<=20){ ICON="-040"; } + else if(charge<=70){ ICON="-060"; } + else if(charge<=90){ ICON="-080"; } + else{ ICON="-100"; } + if(LOS::batteryIsCharging()){ + if(charge>=80){ ICON.clear(); } //for charging, there is no suffix to the icon name over 80% + ICON.prepend("battery-charging"); + TT = QString(tr("%1% (Plugged In)")).arg(QString::number(charge)); + }else{ + ICON.prepend("battery"); + int secs = LOS::batterySecondsLeft(); + if(secs>1){ TT = QString(tr("%1% (%2 Estimated)")).arg(QString::number(charge), LUtils::SecondsToDisplay(secs)); } + else{ TT = QString(tr("%1% Remaining")).arg(QString::number(charge)); } + } + //qDebug() << " Battery Icon:" << ICON << val << TT + ui->label_status_battery->setPixmap( LXDG::findIcon(ICON,"").pixmap(ui->tool_goto_apps->iconSize()/2) ); + ui->label_status_battery->setToolTip(TT); + } + //Network Status + ui->label_status_network->clear(); //not implemented yet + }else if(page == ui->page_apps){ + //Nothing needed for this page explicitly + }else if( page == ui->page_settings){ + // -- Workspaces + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); + if(tot>1){ + ui->frame_wkspace->setVisible(true); + int cur = LSession::handle()->XCB->CurrentWorkspace(); + ui->label_wkspace->setText( QString(tr("Workspace %1/%2")).arg(QString::number(cur+1), QString::number(tot)) ); + }else{ + ui->frame_wkspace->setVisible(false); + } + // -- Brightness Controls + int tmp = LOS::ScreenBrightness(); + ui->frame_bright->setVisible(tmp >= 0); + if(tmp >= 0){ ui->slider_bright->setValue(tmp); } + // -- Audio Controls + tmp = LOS::audioVolume(); + ui->frame_audio->setVisible(tmp >= 0); + if(tmp >= 0){ ui->slider_volume->setValue(tmp); } + }else if(page == ui->page_leave){ + if( !ui->frame_leave_system->whatsThis().isEmpty() ){ + //This frame is allowed/visible - need to adjust the shutdown detection + bool updating = LOS::systemPerformingUpdates(); + ui->tool_restart->setEnabled(!updating); + ui->tool_shutdown->setEnabled(!updating); + ui->label_updating->setVisible(updating); //to let the user know *why* they can't shutdown/restart right now + } + ui->frame_leave_suspend->setVisible( LOS::systemCanSuspend() ); + } + +} + +void StartMenu::catViewChanged(){ + QString state; + switch(ui->check_apps_showcats->checkState()){ + case Qt::Checked: + state = "true"; + break; + case Qt::PartiallyChecked: + state = "partial"; + break; + default: + state = "false"; + } + LSession::handle()->DesktopPluginSettings()->setValue("panelPlugs/systemstart/showcategories", state); + //Now kick off the reload of the apps list + UpdateApps(); + //QtConcurrent::run(this, &StartMenu::UpdateApps); //this was a direct user change - keep it thread safe +} +//Page Change Buttons +void StartMenu::on_tool_goto_apps_clicked(){ + ui->stackedWidget->setCurrentWidget(ui->page_apps); +} + +void StartMenu::on_tool_goto_settings_clicked(){ + ui->stackedWidget->setCurrentWidget(ui->page_settings); +} + +void StartMenu::on_tool_goto_logout_clicked(){ + ui->stackedWidget->setCurrentWidget(ui->page_leave); +} + +void StartMenu::on_tool_back_clicked(){ + ui->stackedWidget->setCurrentWidget(ui->page_main); +} + + +//Launch Buttons +void StartMenu::on_tool_launch_controlpanel_clicked(){ + LaunchItem(ui->tool_launch_controlpanel->whatsThis()); +} + +void StartMenu::on_tool_launch_fm_clicked(){ + LaunchItem(QDir::homePath()); +} + +void StartMenu::on_tool_launch_store_clicked(){ + LaunchItem(ui->tool_launch_store->whatsThis()); +} + +void StartMenu::on_tool_launch_desksettings_clicked(){ + LaunchItem("lumina-config", false); +} + +void StartMenu::on_tool_launch_deskinfo_clicked(){ + LaunchItem("lumina-info",false); +} + +//Logout Buttons +void StartMenu::on_tool_lock_clicked(){ + //QProcess::startDetached("xscreensaver-command -lock"); + LaunchItem("xscreensaver-command -lock",false); +} + +void StartMenu::on_tool_logout_clicked(){ + emit CloseMenu(); + LSession::handle()->StartLogout(); +} + +void StartMenu::on_tool_restart_clicked(){ + emit CloseMenu(); + QCoreApplication::processEvents(); + bool skipupdates = false; + if( !promptAboutUpdates(skipupdates) ){ return; } + LSession::handle()->StartReboot(skipupdates); +} + +void StartMenu::on_tool_shutdown_clicked(){ + emit CloseMenu(); + QCoreApplication::processEvents(); + bool skipupdates = false; + if( !promptAboutUpdates(skipupdates) ){ return; } + LSession::handle()->StartShutdown(skipupdates); +} + +void StartMenu::on_tool_suspend_clicked(){ + //Make sure to lock the system first (otherwise anybody can access it again) + emit CloseMenu(); + LUtils::runCmd("xscreensaver-command -lock"); + LOS::systemSuspend(); +} + + +//Audio Volume +void StartMenu::on_slider_volume_valueChanged(int val){ + ui->label_vol->setText(QString::number(val)+"%"); + LOS::setAudioVolume(val); + //Also adjust the icon for the volume + if(val<1){ ui->tool_mute_audio->setIcon(LXDG::findIcon("audio-volume-muted","")); } + else if(val<33){ ui->tool_mute_audio->setIcon(LXDG::findIcon("audio-volume-low","")); } + else if(val<66){ ui->tool_mute_audio->setIcon(LXDG::findIcon("audio-volume-medium","")); } + else{ ui->tool_mute_audio->setIcon(LXDG::findIcon("audio-volume-high","")); } +} + +void StartMenu::on_tool_launch_mixer_clicked(){ + if(LOS::hasMixerUtility()){ + emit CloseMenu(); + LOS::startMixerUtility(); + } +} + +void StartMenu::on_tool_mute_audio_clicked(){ + static int lastvol = 50; + if(ui->slider_volume->value()==0){ ui->slider_volume->setValue(lastvol); } + else{ + lastvol = ui->slider_volume->value(); + ui->slider_volume->setValue(0); + } +} + +//Screen Brightness +void StartMenu::on_slider_bright_valueChanged(int val){ + ui->label_bright->setText(QString::number(val)+"%"); + LOS::setScreenBrightness(val); +} + + +//Workspace +void StartMenu::on_tool_set_nextwkspace_clicked(){ + int cur = LSession::handle()->XCB->CurrentWorkspace(); + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); + //qDebug()<< "Next Workspace:" << cur << tot; + cur++; + if(cur>=tot){ cur = 0; } //back to beginning + //qDebug() << " - New Current:" << cur; + LSession::handle()->XCB->SetCurrentWorkspace(cur); + ui->label_wkspace->setText( QString(tr("Workspace %1/%2")).arg(QString::number(cur+1), QString::number(tot)) ); +} + +void StartMenu::on_tool_set_prevwkspace_clicked(){ + int cur = LSession::handle()->XCB->CurrentWorkspace(); + int tot = LSession::handle()->XCB->NumberOfWorkspaces(); + //qDebug()<< "Next Workspace:" << cur << tot; + cur--; + if(cur<0){ cur = tot-1; } //back to end + //qDebug() << " - New Current:" << cur; + LSession::handle()->XCB->SetCurrentWorkspace(cur); + ui->label_wkspace->setText( QString(tr("Workspace %1/%2")).arg(QString::number(cur+1), QString::number(tot)) ); +} + + +//Locale +void StartMenu::on_combo_locale_currentIndexChanged(int){ + //Get the currently selected Locale + if(ui->stackedWidget->currentWidget()!=ui->page_settings){ return; } + QString locale = ui->combo_locale->currentData().toString(); + emit CloseMenu(); + LSession::processEvents(); + LSession::handle()->switchLocale(locale); +} + + +//Search +void StartMenu::on_line_search_textEdited(QString){ + if(searchTimer->isActive()){ searchTimer->stop(); } + searchTimer->start(); +} + +void StartMenu::startSearch(){ + if(!this->isVisible()){ return; } //menu closed while timer was active + do_search(ui->line_search->text(),false); //auto-launched +} + +void StartMenu::on_line_search_returnPressed(){ + if(topsearch.isEmpty()){ return; } + LaunchItem(topsearch); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.h new file mode 100644 index 00000000..8ab04d94 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.h @@ -0,0 +1,105 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SYSTEM_START_PANEL_PLUGIN_H +#define _LUMINA_DESKTOP_SYSTEM_START_PANEL_PLUGIN_H + +#include +#include +#include + +#include + +namespace Ui{ + class StartMenu; +}; + +class StartMenu : public QWidget{ + Q_OBJECT +public: + StartMenu(QWidget *parent = 0); + ~StartMenu(); + +public slots: + void UpdateAll(); //for re-translation/icon changes + void UpdateMenu(bool forceall = false); //for item changes + + void ReLoadQuickLaunch(); + void UpdateQuickLaunch(QString, bool); + +private: + Ui::StartMenu *ui; + QStringList favs; + QString CCat, CSearch, topsearch; //current category/search + QTimer *searchTimer; + + //Simple utility functions + //void deleteChildren(QWidget *obj); //recursive function + void ClearScrollArea(QScrollArea *area); + void SortScrollArea(QScrollArea *area); + void do_search(QString search, bool force); + + bool promptAboutUpdates(bool &skip); + +private slots: + void LaunchItem(QString path, bool fix = true); + + //Application/Favorite Listings + void ChangeCategory(QString cat); + void UpdateApps(); + void UpdateFavs(); + + // Page update routines + void on_stackedWidget_currentChanged(int); //page changed + void catViewChanged(); //application categorization view mode changed + + //Page Change Buttons + void on_tool_goto_apps_clicked(); + void on_tool_goto_settings_clicked(); + void on_tool_goto_logout_clicked(); + void on_tool_back_clicked(); + + //Launch Buttons + void on_tool_launch_controlpanel_clicked(); + void on_tool_launch_fm_clicked(); + void on_tool_launch_store_clicked(); + void on_tool_launch_desksettings_clicked(); + void on_tool_launch_deskinfo_clicked(); + + //Logout Buttons + void on_tool_lock_clicked(); + void on_tool_logout_clicked(); + void on_tool_restart_clicked(); + void on_tool_shutdown_clicked(); + void on_tool_suspend_clicked(); + + //Audio Volume + void on_slider_volume_valueChanged(int); + void on_tool_launch_mixer_clicked(); + void on_tool_mute_audio_clicked(); + + //Screen Brightness + void on_slider_bright_valueChanged(int); + + //Workspace + void on_tool_set_nextwkspace_clicked(); + void on_tool_set_prevwkspace_clicked(); + + //Locale + void on_combo_locale_currentIndexChanged(int); + + //Search + void on_line_search_textEdited(QString); + void startSearch(); + void on_line_search_returnPressed(); + +signals: + void CloseMenu(); + void UpdateQuickLaunch(QStringList); + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.ui b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.ui new file mode 100644 index 00000000..74f61d7f --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemstart/StartMenu.ui @@ -0,0 +1,1148 @@ + + + StartMenu + + + + 0 + 0 + 181 + 405 + + + + Form + + + QScrollArea{background: transparent; border: none; } + + + + 2 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + Type to search + + + + + + + 4 + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + bat% + + + + + + + netstat + + + + + + + + 0 + 0 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + Qt::CustomContextMenu + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 179 + 177 + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Browse Files + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Browse Applications + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Control Panel + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Preferences + + + + 32 + 32 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + QToolButton::menu-arrow{ image: rightarrow-icon; } + + + Leave + + + + 32 + 32 + + + + QToolButton::DelayedPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + Qt::NoArrow + + + + + + + Qt::NoFocus + + + lock + + + + 32 + 32 + + + + true + + + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Manage Applications + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + + + + Qt::NoFocus + + + Show Categories + + + true + + + true + + + + + + + Qt::Horizontal + + + + + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + 0 + 0 + 179 + 284 + + + + + + + + + Qt::Horizontal + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Configure Desktop + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::NoFocus + + + info + + + + 32 + 32 + + + + true + + + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::NoFocus + + + + + + + 32 + 32 + + + + true + + + + + + + + + Qt::NoFocus + + + 100 + + + Qt::Horizontal + + + + + + + vol% + + + Qt::AlignCenter + + + + + + + + + Qt::NoFocus + + + + + + + 32 + 32 + + + + false + + + + + + + + + Qt::Horizontal + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + + + Qt::AlignCenter + + + + + + + Qt::NoFocus + + + 10 + + + 100 + + + Qt::Horizontal + + + + + + + br% + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::NoFocus + + + prev + + + + 32 + 32 + + + + + + + + workspace # + + + Qt::AlignCenter + + + + + + + Qt::NoFocus + + + next + + + + 32 + 32 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Locale: + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + + + + + + + + Qt::Vertical + + + + 20 + 161 + + + + + + + + Qt::Horizontal + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 185 + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Suspend System + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + QFrame#frame_leave_system{border: none; background: transparent; } + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Restart System + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Power Off System + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + (System Performing Updates) + + + Qt::AlignCenter + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Sign Out User + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + + true + + + + + 0 + 0 + 161 + 332 + + + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Back + + + + 32 + 32 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + Qt::NoArrow + + + + + + + + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.cpp new file mode 100644 index 00000000..a71fd57e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.cpp @@ -0,0 +1,167 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LSysTray.h" +#include "../../LSession.h" + +LSysTray::LSysTray(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + frame = new QFrame(this); + frame->setContentsMargins(0,0,0,0); + //frame->setStyleSheet("QFrame{ background: transparent; border: 1px solid transparent; border-radius: 3px; }"); + LI = new QBoxLayout( this->layout()->direction()); + frame->setLayout(LI); + LI->setAlignment(Qt::AlignCenter); + LI->setSpacing(0); + LI->setContentsMargins(0,0,0,0); + this->layout()->addWidget(frame); + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + //TrayID=0; + upTimer = new QTimer(this); + upTimer->setInterval(300000); //maximum time between refreshes is 5 minutes + connect(upTimer, SIGNAL(timeout()), this, SLOT(checkAll()) ); + isRunning = false; stopping = false; checking = false; pending = false; + QTimer::singleShot(100, this, SLOT(start()) ); + //Also do one extra check a minute or so after startup (just in case something got missed in the initial flood of registrations) + QTimer::singleShot(90000,this, SLOT(checkAll()) ); + connect(LSession::handle(), SIGNAL(TrayListChanged()), this, SLOT(checkAll()) ); + connect(LSession::handle(), SIGNAL(TrayIconChanged(WId)), this, SLOT(UpdateTrayWindow(WId)) ); + connect(LSession::handle(), SIGNAL(VisualTrayAvailable()), this, SLOT(start()) ); +} + +LSysTray::~LSysTray(){ + if(isRunning){ + this->stop(); + } +} + +void LSysTray::start(){ + if(isRunning || stopping){ return; } //already running + isRunning = LSession::handle()->registerVisualTray(this->winId()); + qDebug() << "Visual Tray Started:" << this->type() << isRunning; + if(isRunning){ + //upTimer->start(); + QTimer::singleShot(0,this, SLOT(checkAll()) ); + } +} + +void LSysTray::stop(){ + if(!isRunning){ return; } + stopping = true; + upTimer->stop(); + //Now close down the system tray registry + qDebug() << "Stop visual system tray:" << this->type(); + //LX11::closeSystemTray(TrayID); + //TrayID = 0; + disconnect(this); //remove any signals/slots + isRunning = false; + //Release all the tray applications and delete the containers + if( !LSession::handle()->currentTrayApps(this->winId()).isEmpty() ){ + qDebug() << " - Remove tray applications"; + //This overall system tray is not closed down - go ahead and release them here + for(int i=(trayIcons.length()-1); i>=0; i--){ + trayIcons[i]->detachApp(); + TrayIcon *cont = trayIcons.takeAt(i); + LI->removeWidget(cont); + cont->deleteLater(); + } + } + //Now let some other visual tray take over + LSession::handle()->unregisterVisualTray(this->winId()); + qDebug() << "Done stopping visual tray"; +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void LSysTray::checkAll(){ + if(!isRunning || stopping || checking){ pending = true; return; } //Don't check if not running at the moment + checking = true; + pending = false; + //Make sure this tray should handle the windows (was not disabled in the backend) + bool TrayRunning = LSession::handle()->registerVisualTray(this->winId()); + //qDebug() << "System Tray: Check tray apps"; + QList wins = LSession::handle()->currentTrayApps(this->winId()); + for(int i=0; iappID()); + if(index < 0){ + //Tray Icon no longer exists: remove it + qDebug() << " - Visual System Tray: Remove Icon:" << trayIcons[i]->appID(); + TrayIcon *cont = trayIcons.takeAt(i); + cont->cleanup(); + LI->removeWidget(cont); + cont->deleteLater(); + i--; //List size changed + //Re-adjust the maximum widget size to account for what is left + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setMaximumSize( trayIcons.length()*this->height(), 10000); + }else{ + this->setMaximumSize(10000, trayIcons.length()*this->width()); + } + }else{ + //Tray Icon already exists + //qDebug() << " - SysTray: Update Icon"; + trayIcons[i]->update(); + wins.removeAt(index); //Already found - remove from the list + } + } + //Now go through any remaining windows and add them + for(int i=0; iaddWidget(cont); + //qDebug() << " - Update tray layout"; + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + int sz = this->height()-2-2*frame->frameWidth(); + if(sz>64){ sz = 64; } + cont->setSizeSquare(sz); //horizontal tray + this->setMaximumSize( trayIcons.length()*this->height(), 10000); + }else{ + int sz = this->width()-2-2*frame->frameWidth(); + if(sz>64){ sz = 64; } + cont->setSizeSquare(sz); //vertical tray + this->setMaximumSize(10000, trayIcons.length()*this->width()); + } + //LSession::processEvents(); + //qDebug() << " - Attach tray app"; + cont->attachApp(wins[i]); + if(cont->appID()==0){ + //could not attach window - remove the widget + qDebug() << " - Invalid Tray App: Could Not Embed:"; + trayIcons.takeAt(trayIcons.length()-1); //Always at the end + LI->removeWidget(cont); + cont->deleteLater(); + continue; + } + LI->update(); //make sure there is no blank space in the layout + } + /*if(listChanged){ + //Icons got moved around: be sure to re-draw all of them to fix visuals + for(int i=0; iupdate(); + } + }*/ + //qDebug() << " - System Tray: check done"; + checking = false; + if(pending){ QTimer::singleShot(0,this, SLOT(checkAll()) ); } +} + +void LSysTray::UpdateTrayWindow(WId win){ + if(!isRunning || stopping || checking){ return; } + for(int i=0; iappID()==win){ + //qDebug() << "System Tray: Update Window " << win; + trayIcons[i]->repaint(); //don't use update() because we need an instant repaint (not a cached version) + return; //finished now + } + } + //Could not find tray in the list, run the checkall routine to make sure we are not missing any + //qDebug() << "System Tray: Missing Window - check all"; + QTimer::singleShot(0,this, SLOT(checkAll()) ); +} + diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.h new file mode 100644 index 00000000..7db307c6 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/LSysTray.h @@ -0,0 +1,74 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2012-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_SYSTRAY_H +#define _LUMINA_DESKTOP_SYSTRAY_H + +//Qt includes +#include +#include +#include +#include +#include + +//Local includes +#include "../LPPlugin.h" +#include "TrayIcon.h" + +//SYSTEM TRAY STANDARD DEFINITIONS +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +class LSysTray : public LPPlugin{ + Q_OBJECT +public: + LSysTray(QWidget *parent = 0, QString id="systemtray", bool horizontal=true); + ~LSysTray(); + + virtual void AboutToClose(){ + this->stop(); + } + +private: + bool isRunning, stopping, checking, pending; + QList trayIcons; + QFrame *frame; + QBoxLayout *LI; //layout items + QTimer *upTimer; //manual timer to force refresh of all items + +private slots: + void checkAll(); + void UpdateTrayWindow(WId win); + + //void removeTrayIcon(WId win); + +public slots: + void start(); + void stop(); + + virtual void OrientationChange(){ + //make sure the internal layout has the same orientation as the main widget + LI->setDirection( this->layout()->direction() ); + //Re-adjust the maximum widget size + int sz; + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setMaximumSize( trayIcons.length()*this->height(), 10000); + sz = this->height()-2*frame->frameWidth(); + }else{ + this->setMaximumSize(10000, trayIcons.length()*this->width()); + sz = this->width()-2*frame->frameWidth(); + } + if(sz>64){ sz = 64; } //many tray icons can't go larger than this anyway + for(int i=0; isetSizeSquare(sz); + trayIcons[i]->repaint(); + } + } + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.cpp new file mode 100644 index 00000000..9fdbce50 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.cpp @@ -0,0 +1,128 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "TrayIcon.h" + +#include +#include +#include + +TrayIcon::TrayIcon(QWidget *parent) : QWidget(parent){ + AID = 0; //nothing yet + IID = 0; + dmgID = 0; + badpaints = 0; + //this->setLayout(new QHBoxLayout); + //this->layout()->setContentsMargins(0,0,0,0); +} + +TrayIcon::~TrayIcon(){ +} + +void TrayIcon::cleanup(){ + AID = IID = 0; +} + +WId TrayIcon::appID(){ + return AID; +} + +void TrayIcon::attachApp(WId id){ + if(id==0){ return; } //nothing to attach + else if(AID!=0){ qWarning() << "Tray Icon is already attached to a window!"; return; } + AID = id; + //WIN = QWindow::fromWinId(AID); + //connect(WIN, SIGNAL( + //this->layout()->addWidget( QWidget::createWindowContainer(WIN, this) ); + IID = this->winId(); //embed directly into this widget + dmgID = LSession::handle()->XCB->EmbedWindow(AID, IID); + if( dmgID != 0 ){ + LSession::handle()->XCB->RestoreWindow(AID); //make it visible + //qDebug() << "New System Tray App:" << AID; + QTimer::singleShot(1000, this, SLOT(updateIcon()) ); + }else{ + //qWarning() << "Could not Embed Tray Application:" << AID; + IID = 0; + AID = 0; + } +} + +void TrayIcon::setSizeSquare(int side){ + //qDebug() << " Set Fixed Systray size:" << side; + this->setFixedSize( QSize(side, side) ); +} + +// ============== +// PUBLIC SLOTS +// ============== +void TrayIcon::detachApp(){ + if(AID==0){ return; } //already detached + //qDebug() << "Detach App:" << AID; + //Temporarily move the AID, so that internal slots do not auto-run + WId tmp = AID; + AID = 0; + //Now detach the application window and clean up + //qDebug() << " - Unembed"; + //WIN->setParent(0); //Reset parentage back to the main stack + LSession::handle()->XCB->UnembedWindow(tmp); + //qDebug() << " - finished app:" << tmp; + IID = 0; +} + +// ============== +// PRIVATE SLOTS +// ============== +void TrayIcon::updateIcon(){ + if(AID==0){ return; } + //Make sure the icon is square + QSize icosize = this->size(); + LSession::handle()->XCB->ResizeWindow(AID, icosize.width(), icosize.height()); + QTimer::singleShot(500, this, SLOT(update()) ); //make sure to re-draw the window in a moment +} + +// ============= +// PROTECTED +// ============= +void TrayIcon::paintEvent(QPaintEvent *event){ + QWidget::paintEvent(event); //make sure the background is already painted + if(AID!=0){ + //Update the background on the tray app + //qDebug() << "Paint Tray Background"; + //LSession::handle()->XCB->SetWindowBackground(this, this->geometry(), AID); + //qDebug() << "Paint Tray:" << AID; + QPainter painter(this); + //Now paint the tray app on top of the background + //qDebug() << " - Draw tray:" << AID << IID << this->winId(); + //qDebug() << " - - " << event->rect().x() << event->rect().y() << event->rect().width() << event->rect().height(); + //qDebug() << " - Get image:" << AID; + QPixmap pix = LSession::handle()->XCB->TrayImage(AID); //= WIN->icon().pixmap(this->size()); + + //qDebug() << " - Pix size:" << pix.size().width() << pix.size().height(); + //qDebug() << " - Geom:" << this->geometry().x() << this->geometry().y() << this->geometry().width() << this->geometry().height(); + if(!pix.isNull()){ + if(this->size() != pix.size()){ QTimer::singleShot(10, this, SLOT(updateIcon())); } + painter.drawPixmap(0,0,this->width(), this->height(), pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation) ); + badpaints = 0; //good paint + }else{ + badpaints++; + if(badpaints>5){ + qWarning() << " - - No Tray Icon/Image found!" << "ID:" << AID; + AID = 0; //reset back to nothing + IID = 0; + emit BadIcon(); //removed/destroyed in some non-valid way? + } + } + //qDebug() << " - Done"; + } +} + +void TrayIcon::resizeEvent(QResizeEvent *event){ + //qDebug() << "Resize Event:" << event->size().width() << event->size().height(); + if(AID!=0){ + LSession::handle()->XCB->ResizeWindow(AID, event->size()); + QTimer::singleShot(500, this, SLOT(update()) ); //make sure to re-draw the window in a moment + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.h new file mode 100644 index 00000000..5d072cc1 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/systemtray/TrayIcon.h @@ -0,0 +1,55 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// Note: The basic idea behind this class that that it puts the app window +// in the same spot as the tray icon (to directly pass mouse events and such), +// while keeping the tray icon visual in sync with the app window +//=========================================== +#ifndef _LUMINA_PANEL_PLUGIN_SYSTEM_TRAY_ICON_H +#define _LUMINA_PANEL_PLUGIN_SYSTEM_TRAY_ICON_H + +//Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +//#include +// libLumina includes +//#include + +class TrayIcon : public QWidget{ + Q_OBJECT +public: + TrayIcon(QWidget* parent = 0); + ~TrayIcon(); + + void cleanup(); //about to be removed after window was detroyed + + WId appID(); //the ID for the attached application + void attachApp(WId id); + void setSizeSquare(int side); + +public slots: + void detachApp(); + void updateIcon(); + +private: + WId IID, AID; //icon ID and app ID + int badpaints; + uint dmgID; + +protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + +signals: + void BadIcon(); +}; +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.cpp new file mode 100644 index 00000000..0dd68bb0 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.cpp @@ -0,0 +1,271 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LTaskButton.h" +#include "LSession.h" + +#ifndef DEBUG +#define DEBUG 0 +#endif + +LTaskButton::LTaskButton(QWidget *parent, bool smallDisplay) : LTBWidget(parent){ + actMenu = new QMenu(this); + winMenu = new QMenu(this); + UpdateMenus(); + showText = !smallDisplay; + this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + this->setAutoRaise(false); //make sure these always look like buttons + this->setContextMenuPolicy(Qt::CustomContextMenu); + this->setFocusPolicy(Qt::NoFocus); + this->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + winMenu->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openActionMenu()) ); + connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); + connect(winMenu, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openActionMenu()) ); + connect(winMenu, SIGNAL(triggered(QAction*)), this, SLOT(winClicked(QAction*)) ); + connect(winMenu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + connect(actMenu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); +} + +LTaskButton::~LTaskButton(){ + +} + +//=========== +// PUBLIC +//=========== +QList LTaskButton::windows(){ + QList list; + for(int i=0; iisVisible()){ return; } //skip this if the window menu is currently visible for now + bool statusOnly = (WINLIST.length() == LWINLIST.length()); + LWINLIST = WINLIST; + + winMenu->clear(); + LXCB::WINDOWVISIBILITY showstate = LXCB::IGNORE; + for(int i=0; isetIcon(WINLIST[i].icon(noicon)); + cname = WINLIST[i].Class(); + if(cname.isEmpty()){ + //Special case (chrome/chromium does not register *any* information with X except window title) + cname = WINLIST[i].text(); + if(cname.contains(" - ")){ cname = cname.section(" - ",-1); } + } + this->setToolTip(cname); + } + bool junk; + QAction *tmp = winMenu->addAction( WINLIST[i].icon(junk), WINLIST[i].text() ); + tmp->setData(i); //save which number in the WINLIST this entry is for + LXCB::WINDOWVISIBILITY stat = WINLIST[i].status(true); //update the saved state for the window + if(statactiveWindow()){ stat = LXCB::ACTIVE; } + if(stat > showstate){ showstate = stat; } //higher priority + } + //Now setup the button appropriately + // - visibility + if(showstate == LXCB::IGNORE || WINLIST.length() < 1){ this->setVisible(false); } + else{ this->setVisible(true); } + // - functionality + if(WINLIST.length() == 1){ + //single window + this->setPopupMode(QToolButton::DelayedPopup); + this->setMenu(actMenu); + if(showText){ + QString txt = WINLIST[0].text(); + if(txt.length()>30){ txt.truncate(27); txt.append("..."); } + else if(txt.length()<30){ txt = txt.leftJustified(30, ' '); } + this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); this->setText(txt); + }else if(noicon){ this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); this->setText( cname ); } + else{ this->setToolButtonStyle(Qt::ToolButtonIconOnly); this->setText(""); } + this->setToolTip(WINLIST[0].text()); + }else if(WINLIST.length() > 1){ + //multiple windows + this->setPopupMode(QToolButton::InstantPopup); + this->setMenu(winMenu); + this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + if(noicon || showText){ "("+QString::number(WINLIST.length())+") "+cname; } + else{ this->setText("("+QString::number(WINLIST.length())+")"); } + } + this->setState(showstate); //Make sure this is after the button setup so that it properly sets the margins/etc + cstate = showstate; //save this for later +} + +void LTaskButton::UpdateMenus(){ + //Action menu should be auto-created for the state of the current window (cWin/cstate) + actMenu->clear(); + if(cstate!=LXCB::ACTIVE){ + actMenu->addAction( LXDG::findIcon("edit-select",""), tr("Activate Window"), this, SLOT(triggerWindow()) ); + } + if(cstate!=LXCB::INVISIBLE){ + actMenu->addAction( LXDG::findIcon("view-close",""), tr("Minimize Window"), this, SLOT(minimizeWindow()) ); + if(LSession::handle()->XCB->WindowIsMaximized(cWin.windowID()) ){ + actMenu->addAction( LXDG::findIcon("view-restore",""), tr("Restore Window"), this, SLOT(maximizeWindow()) ); + }else{ + actMenu->addAction( LXDG::findIcon("view-fullscreen",""), tr("Maximize Window"), this, SLOT(maximizeWindow()) ); + } + } + actMenu->addAction( LXDG::findIcon("window-close",""), tr("Close Window"), this, SLOT(closeWindow()) ); + if(WINLIST.length()>1 && !winMenu->isVisible()){ + actMenu->addSeparator(); + actMenu->addAction( LXDG::findIcon("layer-visible-on",""), tr("Show All Windows"), this, SLOT(showAllWindows()) ); + actMenu->addAction( LXDG::findIcon("layer-visible-off",""), tr("Minimize All Windows"), this, SLOT(hideAllWindows()) ); + actMenu->addAction( LXDG::findIcon("window-close",""), tr("Close All Windows"), this, SLOT(closeAllWindows()) ); + } +} + +//============= +// PRIVATE SLOTS +//============= +void LTaskButton::buttonClicked(){ + if(WINLIST.length() > 1){ + winMenu->popup(QCursor::pos()); + }else{ + triggerWindow(); + } +} + +void LTaskButton::closeWindow(){ + if(DEBUG){ qDebug() << "Close Window:" << this->text(); } + if(winMenu->isVisible()){ winMenu->hide(); } + LWinInfo win = currentWindow(); + LSession::handle()->XCB->CloseWindow(win.windowID()); + cWin = LWinInfo(); //clear the current +} + +void LTaskButton::maximizeWindow(){ + if(DEBUG){ qDebug() << "Maximize Window:" << this->text(); } + if(winMenu->isVisible()){ winMenu->hide(); } + LWinInfo win = currentWindow(); + LSession::handle()->XCB->MaximizeWindow(win.windowID()); + //LSession::handle()->adjustWindowGeom(win.windowID(), true); //run this for now until the WM works properly + cWin = LWinInfo(); //clear the current +} + +void LTaskButton::minimizeWindow(){ + if(DEBUG){ qDebug() << "Minimize Window:" << this->text(); } + if(winMenu->isVisible()){ winMenu->hide(); } + LWinInfo win = currentWindow(); + LSession::handle()->XCB->MinimizeWindow(win.windowID()); + cWin = LWinInfo(); //clear the current + QTimer::singleShot(100, this, SLOT(UpdateButton()) ); //make sure to update this button if losing active status +} + +void LTaskButton::showAllWindows(){ + for(int i=WINLIST.length()-1; i>=0; i--){ + if(WINLIST[i].status()==LXCB::INVISIBLE){ + LSession::handle()->XCB->RestoreWindow(WINLIST[i].windowID()); + } + } +} + +void LTaskButton::hideAllWindows(){ + for(int i=WINLIST.length()-1; i>=0; i--){ + LXCB::WINDOWVISIBILITY state = WINLIST[i].status(); + if(state==LXCB::VISIBLE || state==LXCB::ACTIVE){ + LSession::handle()->XCB->MinimizeWindow(WINLIST[i].windowID()); + } + } +} + +void LTaskButton::closeAllWindows(){ + for(int i=WINLIST.length()-1; i>=0; i--){ + LSession::handle()->XCB->CloseWindow(WINLIST[i].windowID()); + } +} + +void LTaskButton::triggerWindow(){ + LWinInfo win = currentWindow(); + //Check which state the window is currently in and flip it to the other + //LXCB::WINDOWSTATE state = cstate; + //if(DEBUG){ qDebug() << "Window State: " << state; } + if(cstate == LXCB::ACTIVE){ + if(DEBUG){ qDebug() << "Minimize Window:" << this->text(); } + LSession::handle()->XCB->MinimizeWindow(win.windowID()); + QTimer::singleShot(100, this, SLOT(UpdateButton()) ); //make sure to update this button if losing active status + }else{ + if(DEBUG){ qDebug() << "Activate Window:" << this->text(); } + LSession::handle()->XCB->ActivateWindow(win.windowID()); + } + cWin = LWinInfo(); //clear the current +} + +void LTaskButton::winClicked(QAction* act){ + //Get the window from the action + if(act->data().toInt() < WINLIST.length()){ + cWin = WINLIST[act->data().toInt()]; + cstate = cWin.status(); + }else{ cWin = LWinInfo(); } //clear it + //Now trigger the window + triggerWindow(); +} + +void LTaskButton::openActionMenu(){ + //Get the Window the mouse is currently over + QPoint curpos = QCursor::pos(); + QAction *act = winMenu->actionAt(winMenu->mapFromGlobal(curpos)); + //qDebug() << "Button Context Menu:" << curpos.x() << curpos.y() << winMenu->geometry().x() << winMenu->geometry().y() << winMenu->geometry().width() << winMenu->geometry().height(); + cWin = WINLIST[0]; //default to the first window + if( act != 0 && winMenu->isVisible() ){ + //Get the window from the action + //qDebug() << "Found Action:" << act->data().toInt(); + if(act->data().toInt() < WINLIST.length()){ + cWin = WINLIST[act->data().toInt()]; + } + } + cstate = cWin.status(); + //Now show the action menu + UpdateMenus(); + actMenu->popup(QCursor::pos()); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.h new file mode 100644 index 00000000..6b171c6a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskButton.h @@ -0,0 +1,73 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_TASK_BUTTON_H +#define _LUMINA_DESKTOP_TASK_BUTTON_H + +// Qt includes +#include +#include +#include +#include +#include +#include +#include + +// libLumina includes +#include +#include + +// Local includes +#include "../../LWinInfo.h" +#include "../LTBWidget.h" + +class LTaskButton : public LTBWidget{ + Q_OBJECT +public: + LTaskButton(QWidget *parent=0, bool smallDisplay = true); + ~LTaskButton(); + + //Window Information + QList windows(); + QString classname(); + bool isActive(); + + //Window Management + void addWindow(WId win); //Add a window to this button + void rmWindow(WId win); //Remove a window from this button + +private: + QList WINLIST; + QList LWINLIST; + QMenu *actMenu; // action menu (custom context menu) + QMenu *winMenu; // window menu (if more than 1) + LWinInfo cWin; + QString cname; //class name for the entire button + bool noicon, showText; + + LWinInfo currentWindow(); //For getting the currently-active window + LXCB::WINDOWVISIBILITY cstate; //current state of the button + +public slots: + void UpdateButton(); //re-sync the current window infomation + void UpdateMenus(); //re-create the menus (text + icons) + +private slots: + void buttonClicked(); + void closeWindow(); //send the signal to close a window + void maximizeWindow(); //send the signal to maximize/restore a window + void minimizeWindow(); //send the signal to minimize a window (iconify) + void showAllWindows(); + void hideAllWindows(); + void closeAllWindows(); + void triggerWindow(); //change b/w visible and invisible + void winClicked(QAction*); + void openActionMenu(); + +signals: + void MenuClosed(); +}; +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.cpp new file mode 100644 index 00000000..79c5dd36 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.cpp @@ -0,0 +1,141 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LTaskManagerPlugin.h" +#include "../../LSession.h" + +LTaskManagerPlugin::LTaskManagerPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(10); // 1/100 second + connect(timer, SIGNAL(timeout()), this, SLOT(UpdateButtons()) ); + usegroups = true; //backwards-compatible default value + if(id.contains("-nogroups")){ usegroups = false; } + connect(LSession::handle(), SIGNAL(WindowListEvent()), this, SLOT(checkWindows()) ); + connect(LSession::handle(), SIGNAL(WindowListEvent(WId)), this, SLOT(UpdateButton(WId)) ); + this->layout()->setContentsMargins(0,0,0,0); + QTimer::singleShot(0,this, SLOT(UpdateButtons()) ); //perform an initial sync + //QTimer::singleShot(100,this, SLOT(OrientationChange()) ); //perform an initial sync +} + +LTaskManagerPlugin::~LTaskManagerPlugin(){ + +} + +//============== +// PRIVATE SLOTS +//============== +void LTaskManagerPlugin::UpdateButtons(){ + updating = QDateTime::currentDateTime(); //global time stamp + QDateTime ctime = updating; //current thread time stamp + + //Get the current window list + QList winlist = LSession::handle()->XCB->WindowList(); + // Ignore the windows which don't want to be listed + for (int i = 0; i < winlist.length(); i++) { + QList states = LSession::handle()->XCB->WM_Get_Window_States(winlist[i]); + for (int j = 0; j < states.length(); j++) { + if (states[j] == LXCB::S_SKIP_TASKBAR) { + // Skip taskbar window + winlist.removeAt(i); + i--; + break; + } + } + } + //Do not change the status of the previously active window if it just changed to a non-visible window + //WId activeWin = LSession::handle()->XCB->ActiveWindow(); + //bool skipActive = !winlist.contains(activeWin); + //qDebug() << "Update Buttons:" << winlist; + if(updating > ctime){ return; } //another thread kicked off already - stop this one + //Now go through all the current buttons first + for(int i=0; i WI = BUTTONS[i]->windows(); + bool updated=false; + if(updating > ctime){ return; } //another thread kicked off already - stop this one + //Loop over all the windows for this button + for(int w=0; w ctime){ return; } //another thread kicked off already - stop this one + if( winlist.contains( WI[w] ) ){ + //Still current window - update it later + winlist.removeAll(WI[w] ); //remove this window from the list since it is done + }else{ + //Window was closed - remove it + if(WI.length()==1){ + //Remove the entire button + //qDebug() << "Window Closed: Remove Button" ; + this->layout()->takeAt(i); //remove from the layout + BUTTONS.takeAt(i)->deleteLater(); + i--; + updated = true; //prevent updating a removed button + break; //break out of the button->window loop + }else{ + //qDebug() << "Window Closed: Remove from button:" << WI[w].windowID() << "Button:" << w; + BUTTONS[i]->rmWindow(WI[w]); // one of the multiple windows for the button + WI.removeAt(w); //remove this window from the list + w--; + } + updated=true; //button already changed + } + if(updating > ctime){ return; } //another thread kicked off already - stop this one + } + if(!updated){ + //qDebug() << "Update Button:" << i; + if(updating > ctime){ return; } //another thread kicked off already - stop this one + //if(!skipActive || !BUTTONS[i]->isActive()){ + QTimer::singleShot(1,BUTTONS[i], SLOT(UpdateButton()) ); //keep moving on + //} + } + } + //Now go through the remaining windows + for(int i=0; i ctime){ return; } //another thread kicked off already - stop this one + //Check for a button that this can just be added to + QString ctxt = LSession::handle()->XCB->WindowClass(winlist[i]); + bool found = false; + for(int b=0; b ctime){ return; } //another thread kicked off already - stop this one + if(BUTTONS[b]->classname()== ctxt && usegroups){ + //This adds a window to an existing group + found = true; + //qDebug() << "Add Window to Button:" << b; + BUTTONS[b]->addWindow(winlist[i]); + break; + } + } + if(!found){ + if(updating > ctime){ return; } //another thread kicked off already - stop this one + //No group, create a new button + //qDebug() << "New Button"; + LTaskButton *but = new LTaskButton(this, usegroups); + but->addWindow( winlist[i] ); + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + but->setIconSize(QSize(this->height(), this->height())); + }else{ + but->setIconSize(QSize(this->width(), this->width())); + } + this->layout()->addWidget(but); + connect(but, SIGNAL(MenuClosed()), this, SIGNAL(MenuClosed())); + BUTTONS << but; + } + } +} + +void LTaskManagerPlugin::UpdateButton(WId win){ + for(int i=0; iwindows().contains(win)){ + qDebug() << "Update Task Manager Button (single window ping)"; + QTimer::singleShot(0,BUTTONS[i], SLOT(UpdateButton()) ); + break; + } + } +} + +void LTaskManagerPlugin::checkWindows(){ + timer->start(); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.h new file mode 100644 index 00000000..e6371f34 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/taskmanager/LTaskManagerPlugin.h @@ -0,0 +1,71 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#ifndef _LUMINA_DESKTOP_TASK_MANAGER_PLUGIN_H +#define _LUMINA_DESKTOP_TASK_MANAGER_PLUGIN_H + +// Qt includes +#include +#include +#include +#include +#include +#include +#include + +// libLumina includes +#include + +// Local includes +#include "LTaskButton.h" +#include "LWinInfo.h" +#include "../LPPlugin.h" + +class LTaskManagerPlugin : public LPPlugin{ + Q_OBJECT +public: + LTaskManagerPlugin(QWidget *parent=0, QString id="taskmanager", bool horizontal=true); + ~LTaskManagerPlugin(); + +private: + QList BUTTONS; //to keep track of the current buttons + QTimer *timer; + QDateTime updating; //quick flag for if it is currently working + bool usegroups; + +private slots: + void UpdateButtons(); + void UpdateButton(WId win); + void checkWindows(); + +public slots: + void LocaleChange(){ + UpdateButtons(); + } + void ThemeChange(){ + UpdateButtons(); + } + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ //horizontal + this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + this->layout()->setAlignment(Qt::AlignLeft); + QSize sz(this->height(), this->height()); + for(int i=0; isetToolButtonStyle(Qt::ToolButtonTextBesideIcon); + BUTTONS[i]->setIconSize(sz); + } + }else{ //vertical + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + this->layout()->setAlignment(Qt::AlignTop); + QSize sz(this->width(), this->width()); + for(int i=0; isetToolButtonStyle(Qt::ToolButtonTextUnderIcon); + BUTTONS[i]->setIconSize(sz); + } + } + } +}; +#endif \ No newline at end of file diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.cpp new file mode 100644 index 00000000..acb27135 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.cpp @@ -0,0 +1,67 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "LUserButton.h" +#include "../../LSession.h" + +LUserButtonPlugin::LUserButtonPlugin(QWidget *parent, QString id, bool horizontal) : LPPlugin(parent, id, horizontal){ + button = new QToolButton(this); + button->setAutoRaise(true); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setPopupMode(QToolButton::DelayedPopup); //make sure it runs the update routine first + connect(button, SIGNAL(clicked()), this, SLOT(openMenu())); + this->layout()->setContentsMargins(0,0,0,0); + this->layout()->addWidget(button); + menu = new QMenu(this); + menu->setContentsMargins(1,1,1,1); + connect(menu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed())); + usermenu = new UserWidget(this); + connect(usermenu, SIGNAL(CloseMenu()), this, SLOT(closeMenu()) ); + mact = new QWidgetAction(this); + mact->setDefaultWidget(usermenu); + menu->addAction(mact); + + button->setMenu(menu); + connect(menu, SIGNAL(aboutToHide()), this, SLOT(updateButtonVisuals()) ); + //Setup the global shortcut handling for opening the start menu + connect(QApplication::instance(), SIGNAL(StartButtonActivated()), this, SLOT(shortcutActivated()) ); + LSession::handle()->registerStartButton(this->type()); + + QTimer::singleShot(0,this, SLOT(OrientationChange())); //Update icons/sizes +} + +LUserButtonPlugin::~LUserButtonPlugin(){ + +} + +void LUserButtonPlugin::updateButtonVisuals(){ + button->setToolTip(tr("Quickly launch applications or open files")); + button->setText( SYSTEM::user() ); + if( QFile::exists(QDir::homePath()+"/.loginIcon.png") ){ + button->setIcon( QIcon(QDir::homePath()+"/.loginIcon.png") ); + }else{ + button->setIcon( LXDG::findIcon("user-identity", ":/images/default-user.png") ); //force icon refresh + } +} + +// ======================== +// PRIVATE FUNCTIONS +// ======================== +void LUserButtonPlugin::openMenu(){ + usermenu->UpdateMenu(); + button->showMenu(); +} + +void LUserButtonPlugin::closeMenu(){ + menu->hide(); +} + +void LUserButtonPlugin::shortcutActivated(){ + if(LSession::handle()->registerStartButton(this->type())){ + if(menu->isVisible()){ closeMenu(); } + else{ openMenu(); } + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.h new file mode 100644 index 00000000..8d5e5040 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/LUserButton.h @@ -0,0 +1,75 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin is the main button that allow the user to run +// applications or logout of the desktop +//=========================================== +#ifndef _LUMINA_DESKTOP_USER_MENU_PLUGIN_H +#define _LUMINA_DESKTOP_USER_MENU_PLUGIN_H + +// Qt includes +#include +#include +#include +#include +#include + + +// Lumina-desktop includes +//#include "../../Globals.h" +#include "../LPPlugin.h" //main plugin widget + +// libLumina includes +#include "LuminaXDG.h" + +#include "UserWidget.h" + +// PANEL PLUGIN BUTTON +class LUserButtonPlugin : public LPPlugin{ + Q_OBJECT + +public: + LUserButtonPlugin(QWidget *parent = 0, QString id = "userbutton", bool horizontal=true); + ~LUserButtonPlugin(); + +private: + QMenu *menu; + QWidgetAction *mact; + UserWidget *usermenu; + QToolButton *button; + +private slots: + void openMenu(); + void closeMenu(); + void shortcutActivated(); + + void updateButtonVisuals(); + +public slots: + void OrientationChange(){ + if(this->layout()->direction()==QBoxLayout::LeftToRight){ + this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + button->setIconSize( QSize(this->height(), this->height()) ); + }else{ + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + button->setIconSize( QSize(this->width(), this->width()) ); + } + this->layout()->update(); + updateButtonVisuals(); + } + + void LocaleChange(){ + updateButtonVisuals(); + usermenu->UpdateAll(); + } + + void ThemeChange(){ + updateButtonVisuals(); + usermenu->UpdateAll(); + } +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.cpp new file mode 100644 index 00000000..8d7dab7a --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.cpp @@ -0,0 +1,205 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "UserItemWidget.h" +#include +#include +#include + +#define TEXTCUTOFF 165 +UserItemWidget::UserItemWidget(QWidget *parent, QString itemPath, QString type, bool goback) : QFrame(parent){ + createWidget(); + //Now fill it appropriately + bool inHome = type.endsWith("-home"); //internal code + if(inHome){ type = type.remove("-home"); } + if(itemPath.endsWith(".desktop") || type=="app"){ + XDGDesktop item(itemPath); + if( item.isValid() ){ + icon->setPixmap( LXDG::findIcon(item.icon, "preferences-system-windows-actions").pixmap(32,32) ); + name->setText( this->fontMetrics().elidedText(item.name, Qt::ElideRight, TEXTCUTOFF) ); + setupActions(&item); + }else{ + gooditem = false; + return; + } + }else if(type=="dir"){ + actButton->setVisible(false); + if(itemPath.endsWith("/")){ itemPath.chop(1); } + if(goback){ + icon->setPixmap( LXDG::findIcon("go-previous","").pixmap(32,32) ); + name->setText( tr("Go Back") ); + }else{ + icon->setPixmap( LXDG::findIcon("folder","").pixmap(32,32) ); + name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); + } + }else{ + actButton->setVisible(false); + if(itemPath.endsWith("/")){ itemPath.chop(1); } + if(QFileInfo(itemPath).isDir()){ + type = "dir"; + icon->setPixmap( LXDG::findIcon("folder","").pixmap(32,32) ); + }else if(LUtils::imageExtensions().contains(itemPath.section("/",-1).section(".",-1).toLower()) ){ + icon->setPixmap( QIcon(itemPath).pixmap(32,32) ); + }else{ + icon->setPixmap( LXDG::findMimeIcon(itemPath.section("/",-1)).pixmap(32,32) ); + } + name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, TEXTCUTOFF) ); + } + icon->setWhatsThis(itemPath); + if(!goback){ this->setWhatsThis(name->text()); } + isDirectory = (type=="dir"); //save this for later + if(LDesktopUtils::isFavorite(itemPath)){ + linkPath = itemPath; + isShortcut=true; + }else if( inHome ){//|| itemPath.section("/",0,-2)==QDir::homePath()+"/Desktop" ){ + isShortcut = true; + }else{ + isShortcut = false; + } + //Now setup the button appropriately + setupButton(goback); +} + +UserItemWidget::UserItemWidget(QWidget *parent, XDGDesktop *item) : QFrame(parent){ + if(item==0){ return; } + createWidget(); + isDirectory = false; + if(LDesktopUtils::isFavorite(item->filePath)){ + linkPath = item->filePath; + isShortcut=true; + }else if( item->filePath.section("/",0,-2)==QDir::homePath()+"/Desktop" ){ + isShortcut = true; + }else{ + isShortcut = false; + } + //Now fill it appropriately + icon->setPixmap( LXDG::findIcon(item->icon,"preferences-system-windows-actions").pixmap(32,32) ); + name->setText( this->fontMetrics().elidedText(item->name, Qt::ElideRight, TEXTCUTOFF) ); + this->setWhatsThis(name->text()); + icon->setWhatsThis(item->filePath); + //Now setup the buttons appropriately + setupButton(); + setupActions(item); +} + +UserItemWidget::~UserItemWidget(){ + delete button; + delete icon; + delete name; +} + + +void UserItemWidget::createWidget(){ + //Initialize the widgets + gooditem = true; + menuopen = false; + menureset = new QTimer(this); + menureset->setSingleShot(true); + menureset->setInterval(1000); //1 second + this->setContentsMargins(0,0,0,0); + button = new QToolButton(this); + button->setIconSize( QSize(14,14) ); + button->setAutoRaise(true); + actButton = new QToolButton(this); + actButton->setPopupMode(QToolButton::InstantPopup); + actButton->setFixedSize( QSize(17,34) ); + actButton->setArrowType(Qt::DownArrow); + icon = new QLabel(this); + icon->setFixedSize( QSize(34,34) ); + name = new QLabel(this); + //Add them to the layout + this->setLayout(new QHBoxLayout()); + this->layout()->setContentsMargins(1,1,1,1); + this->layout()->addWidget(icon); + this->layout()->addWidget(actButton); + this->layout()->addWidget(name); + this->layout()->addWidget(button); + //Set a custom object name so this can be tied into the Lumina Theme stylesheets + this->setObjectName("LuminaUserItemWidget"); + //Install the stylesheet + //this->setStyleSheet("UserItemWidget{ background: transparent; border-radius: 5px;} UserItemWidget::hover{ background: rgba(255,255,255,200); border-radius: 5px; }"); +} + +void UserItemWidget::setupButton(bool disable){ + //if(isDirectory){ qDebug() << "Directory Entry:" << isShortcut << linkPath << icon->whatsThis(); } + + if(disable){ + button->setVisible(false); + }else if(isShortcut && !linkPath.isEmpty()){ //Favorite Item - can always remove this + button->setWhatsThis("remove"); + button->setIcon( LXDG::findIcon("list-remove","") ); + button->setToolTip(tr("Remove Shortcut")); + connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); + }else if(isShortcut){ //Physical File/Dir - can remove + button->setWhatsThis("remove"); + button->setIcon( LXDG::findIcon("user-trash","") ); + button->setToolTip(tr("Delete File")); + connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); + }else if(!isShortcut){// if( !QFile::exists( QDir::homePath()+"/Desktop/"+icon->whatsThis().section("/",-1) ) && !LUtils::isFavorite(icon->whatsThis() ) ){ + //This file does not have a shortcut yet -- allow the user to add it + button->setWhatsThis("add"); + button->setIcon( LXDG::findIcon("bookmark-toolbar","") ); + button->setToolTip(tr("Create Shortcut")); + connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); + }else{ + //This already has a desktop shortcut -- no special actions + button->setVisible(false); + } + if(isShortcut){ + name->setToolTip(icon->whatsThis()); //also allow the user to see the full shortcut path + } +} + +void UserItemWidget::setupActions(XDGDesktop *app){ + if(app==0 || app->actions.isEmpty()){ actButton->setVisible(false); return; } + //Actions Available - go ahead and list them all + actButton->setMenu( new QMenu(this) ); + for(int i=0; iactions.length(); i++){ + QAction *act = new QAction(LXDG::findIcon(app->actions[i].icon, app->icon), app->actions[i].name, this); + act->setToolTip(app->actions[i].ID); + act->setWhatsThis(app->actions[i].ID); + actButton->menu()->addAction(act); + } + connect(actButton->menu(), SIGNAL(triggered(QAction*)), this, SLOT(actionClicked(QAction*)) ); + connect(actButton->menu(), SIGNAL(aboutToShow()), this, SLOT(actionMenuOpen()) ); + connect(actButton->menu(), SIGNAL(aboutToHide()), this, SLOT(actionMenuClosed()) ); + connect(menureset, SIGNAL(timeout()), this, SLOT(resetmenuflag()) ); +} + +void UserItemWidget::buttonClicked(){ + button->setVisible(false); + if(button->whatsThis()=="add"){ + LDesktopUtils::addFavorite(icon->whatsThis()); + //QFile::link(icon->whatsThis(), QDir::homePath()+"/.lumina/favorites/"+icon->whatsThis().section("/",-1) ); + emit NewShortcut(); + }else if(button->whatsThis()=="remove"){ + if(linkPath.isEmpty()){ + //This is a desktop file + if(isDirectory){ + QProcess::startDetached("rm -r \""+icon->whatsThis()+"\""); + }else{ + QFile::remove(icon->whatsThis()); + } + //Don't emit the RemovedShortcut signal here - the automatic ~/Desktop watcher will see the change when finished + }else{ + LDesktopUtils::removeFavorite(icon->whatsThis()); //This is a favorite + emit RemovedShortcut(); + } + } +} + +void UserItemWidget::ItemClicked(){ + if(!linkPath.isEmpty()){ emit RunItem(linkPath); } + else{ emit RunItem(icon->whatsThis()); } +} + +void UserItemWidget::actionClicked(QAction *act){ + actButton->menu()->hide(); + QString cmd = "lumina-open -action \""+act->whatsThis()+"\" \"%1\""; + if(!linkPath.isEmpty()){ cmd = cmd.arg(linkPath); } + else{ cmd = cmd.arg(icon->whatsThis()); } + emit RunItem(cmd); +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.h new file mode 100644 index 00000000..0b212f10 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserItemWidget.h @@ -0,0 +1,72 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This item widget manages a single file/directory +//=========================================== +#ifndef _LUMINA_PANEL_USER_ITEM_WIDGET_H +#define _LUMINA_PANEL_USER_ITEM_WIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class UserItemWidget : public QFrame{ + Q_OBJECT +public: + UserItemWidget(QWidget *parent=0, QString itemPath="", QString type="unknown", bool goback=false); + UserItemWidget(QWidget *parent=0, XDGDesktop *item= 0); + ~UserItemWidget(); + + bool gooditem; +private: + QToolButton *button, *actButton; + QLabel *icon, *name; + bool isDirectory, isShortcut, menuopen; + QString linkPath; + QTimer *menureset; + + void createWidget(); + void setupButton(bool disable = false); + void setupActions(XDGDesktop*); + +private slots: + void buttonClicked(); + void ItemClicked(); + void actionClicked(QAction*); + //Functions to fix the submenu open/close issues + void actionMenuOpen(){ + if(menureset->isActive()){ menureset->stop(); } + menuopen = true; + } + void resetmenuflag(){ menuopen = false; } //tied to the "menureset" timer + void actionMenuClosed(){ menureset->start(); } + + +protected: + void mouseReleaseEvent(QMouseEvent *event){ + if(menuopen){ resetmenuflag(); } //skip this event if a submenu was open + else if(event->button() != Qt::NoButton){ ItemClicked(); } + } + +signals: + void NewShortcut(); + void RemovedShortcut(); + void RunItem(QString cmd); + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.cpp b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.cpp new file mode 100644 index 00000000..a0ba8996 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.cpp @@ -0,0 +1,393 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014-2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "UserWidget.h" +#include "ui_UserWidget.h" +#include "../../LSession.h" +#include "../../AppMenu.h" + +UserWidget::UserWidget(QWidget* parent) : QTabWidget(parent), ui(new Ui::UserWidget){ + ui->setupUi(this); + updatingfavs = false; + if(parent!=0){ parent->setMouseTracking(true); } + this->setMouseTracking(true); + sysapps = LSession::handle()->applicationMenu()->currentAppHash(); //get the raw info + + //Connect the signals/slots + connect(ui->tool_desktopsettings, SIGNAL(clicked()), this, SLOT(openDeskSettings()) ); + connect(ui->tool_config_screensaver, SIGNAL(clicked()), this, SLOT(openScreenSaverConfig()) ); + connect(ui->tool_config_screensettings, SIGNAL(clicked()), this, SLOT(openScreenConfig()) ); + connect(ui->tool_fav_apps, SIGNAL(clicked()), this, SLOT(FavChanged()) ); + connect(ui->tool_fav_files, SIGNAL(clicked()), this, SLOT(FavChanged()) ); + connect(ui->tool_fav_dirs, SIGNAL(clicked()), this, SLOT(FavChanged()) ); + connect(ui->combo_app_cats, SIGNAL(currentIndexChanged(int)), this, SLOT(updateApps()) ); + connect(ui->tool_home_gohome, SIGNAL(clicked()), this, SLOT(slotGoHome()) ); + connect(ui->tool_home_browse, SIGNAL(clicked()), this, SLOT(slotOpenDir()) ); + connect(ui->tool_home_search, SIGNAL(clicked()), this, SLOT(slotOpenSearch()) ); + connect(ui->tool_config_about, SIGNAL(clicked()), this, SLOT(openLuminaInfo()) ); + + //Setup the special buttons + connect(ui->tool_app_store, SIGNAL(clicked()), this, SLOT(openStore()) ); + connect(ui->tool_controlpanel, SIGNAL(clicked()), this, SLOT(openControlPanel()) ); + //connect(ui->tool_qtconfig, SIGNAL(clicked()), this, SLOT(openQtConfig()) ); + + lastUpdate = QDateTime(); //make sure it refreshes + + connect(LSession::handle()->applicationMenu(), SIGNAL(AppMenuUpdated()), this, SLOT(UpdateMenu()) ); + connect(QApplication::instance(), SIGNAL(DesktopFilesChanged()), this, SLOT(updateFavItems()) ); + QTimer::singleShot(10,this, SLOT(UpdateAll())); //make sure to load this once after initialization +} + +UserWidget::~UserWidget(){ +} + +//=========== +// PRIVATE +//=========== +void UserWidget::ClearScrollArea(QScrollArea *area){ + QWidget *wgt = area->takeWidget(); + wgt->deleteLater(); //delete the widget and all children + area->setWidget( new QWidget() ); //create a new widget in the scroll area + area->widget()->setContentsMargins(0,0,0,0); + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing(2); + layout->setContentsMargins(3,1,3,1); + layout->setDirection(QBoxLayout::TopToBottom); + layout->setAlignment(Qt::AlignTop); + area->widget()->setLayout(layout); +} + +void UserWidget::SortScrollArea(QScrollArea *area){ + //qDebug() << "Sorting Scroll Area:"; + //Sort all the items in the scroll area alphabetically + QLayout *lay = area->widget()->layout(); + QStringList items; + for(int i=0; icount(); i++){ + items << lay->itemAt(i)->widget()->whatsThis().toLower(); + } + + items.sort(); + //qDebug() << " - Sorted Items:" << items; + for(int i=0; icount(); j++){ + //Find this item + if(lay->itemAt(j)->widget()->whatsThis().toLower()==items[i]){ + //Found it - now move it if necessary + //qDebug() << "Found Item:" << items[i] << i << j; + lay->addItem( lay->takeAt(j) ); + break; + } + } + } + +} + +QIcon UserWidget::rotateIcon(QIcon ico){ + //Rotate the given icon to appear vertical in the tab widget + QPixmap pix = ico.pixmap(32,32); + QTransform tran; + tran.rotate(+90); //For tabs on the left/West + pix = pix.transformed(tran); + ico = QIcon(pix); + return ico; +} + +//============ +// PRIVATE SLOTS +//============ +void UserWidget::UpdateAll(){ + ui->retranslateUi(this); + //Setup the Icons + // - favorites tab + this->setTabIcon(0, rotateIcon(LXDG::findIcon("folder-favorites","")) ); + this->setTabText(0,""); + // - apps tab + this->setTabIcon(1, rotateIcon(LXDG::findIcon("system-run","")) ); + this->setTabText(1,""); + // - home tab + this->setTabIcon(2, rotateIcon(LXDG::findIcon("user-home","")) ); + this->setTabText(2,""); + // - config tab + this->setTabIcon(3, rotateIcon(LXDG::findIcon("preferences-system","")) ); + this->setTabText(3,""); + ui->tool_fav_apps->setIcon( LXDG::findIcon("system-run","") ); + ui->tool_fav_dirs->setIcon( LXDG::findIcon("folder","") ); + ui->tool_fav_files->setIcon( LXDG::findIcon("document-multiple","") ); + ui->tool_desktopsettings->setIcon( LXDG::findIcon("preferences-desktop","") ); + ui->tool_config_screensaver->setIcon( LXDG::findIcon("preferences-desktop-screensaver","") ); + ui->tool_config_screensettings->setIcon( LXDG::findIcon("preferences-other","") ); + ui->tool_home_gohome->setIcon( LXDG::findIcon("go-home","") ); + ui->tool_home_browse->setIcon( LXDG::findIcon("document-open","") ); + ui->tool_home_search->setIcon( LXDG::findIcon("system-search","") ); + ui->tool_config_about->setIcon( LXDG::findIcon("lumina","") ); + + //Setup the special buttons + QString APPSTORE = LOS::AppStoreShortcut(); + if(QFile::exists(APPSTORE) && !APPSTORE.isEmpty()){ + //Now load the info + XDGDesktop store(APPSTORE); + bool ok = store.isValid(); + if(ok){ + ui->tool_app_store->setIcon( LXDG::findIcon(store.icon, "") ); + ui->tool_app_store->setText( store.name ); + } + ui->tool_app_store->setVisible(ok); //availability + }else{ + ui->tool_app_store->setVisible(false); //not available + } + QString CONTROLPANEL = LOS::ControlPanelShortcut(); + if(QFile::exists(CONTROLPANEL) && !CONTROLPANEL.isEmpty()){ + //Now load the info + XDGDesktop cpan(CONTROLPANEL); + bool ok = cpan.isValid(); + if(ok){ + ui->tool_controlpanel->setIcon( LXDG::findIcon(cpan.icon, "") ); + } + ui->tool_controlpanel->setVisible(ok); //availability + }else{ + ui->tool_controlpanel->setVisible(false); //not available + } + /*QString QTCONFIG = LOS::QtConfigShortcut(); + if(QFile::exists(QTCONFIG) && !QTCONFIG.isEmpty()){ + ui->tool_qtconfig->setVisible(true); + ui->tool_qtconfig->setIcon( LXDG::findIcon("preferences-desktop-theme","") ); + }else{ + ui->tool_qtconfig->setVisible(false); + }*/ + //Now update the menus + UpdateMenu(); +} + +void UserWidget::UpdateMenu(bool forceall){ + this->setCurrentWidget(ui->tab_fav); + ui->tool_fav_apps->setChecked(true); + ui->tool_fav_dirs->setChecked(false); + ui->tool_fav_files->setChecked(false); + cfav = 0; //favorite apps + FavChanged(); + QString cdir = ui->label_home_dir->whatsThis(); + if(cdir.isEmpty() || !QFile::exists(cdir)){ + //Directory deleted or nothing loaded yet + ui->label_home_dir->setWhatsThis(QDir::homePath()); + QTimer::singleShot(0,this, SLOT(updateHome()) ); + }else if( lastUpdate < QFileInfo(cdir).lastModified() || forceall){ + //Directory contents changed - reload it + QTimer::singleShot(0,this, SLOT(updateHome()) ); + } + if(lastUpdate < LSession::handle()->applicationMenu()->lastHashUpdate || lastUpdate.isNull() || forceall){ + updateAppCategories(); + QTimer::singleShot(0,this, SLOT(updateApps()) ); + } + lastUpdate = QDateTime::currentDateTime(); +} + +void UserWidget::LaunchItem(QString path, bool fix){ + if(!path.isEmpty()){ + qDebug() << "Launch Application:" << path; + if( fix && !path.startsWith("lumina-open") ){ LSession::LaunchApplication("lumina-open \""+path+"\""); } + else{ LSession::LaunchApplication(path); } + emit CloseMenu(); //so the menu container will close + } +} + +void UserWidget::FavChanged(){ + //uncheck the current item for a moment + int oldfav = cfav; + if(cfav==0){ ui->tool_fav_apps->setChecked(false); } + else if(cfav==1){ ui->tool_fav_dirs->setChecked(false); } + if(cfav==2){ ui->tool_fav_files->setChecked(false); } + //Now check what other item is now the only one checked + if(ui->tool_fav_apps->isChecked() && !ui->tool_fav_dirs->isChecked() && !ui->tool_fav_files->isChecked() ){ + cfav = 0; + }else if(!ui->tool_fav_apps->isChecked() && ui->tool_fav_dirs->isChecked() && !ui->tool_fav_files->isChecked() ){ + cfav = 1; + }else if(!ui->tool_fav_apps->isChecked() && !ui->tool_fav_dirs->isChecked() && ui->tool_fav_files->isChecked() ){ + cfav = 2; + }else{ + //Re-check the old item (something invalid) + ui->tool_fav_apps->setChecked(cfav==0); + ui->tool_fav_dirs->setChecked(cfav==1); + ui->tool_fav_files->setChecked(cfav==2); + } + updateFavItems(oldfav!=cfav); +} + +void UserWidget::updateFavItems(bool newfilter){ + if(updatingfavs){ return; } + updatingfavs = true; + //qDebug() << "Updating User Favorite Items"; + QStringList newfavs = LDesktopUtils::listFavorites(); + //qDebug() << "Favorites:" << newfavs; + if(lastHomeUpdate.isNull() || (QFileInfo(QDir::homePath()+"/Desktop").lastModified() > lastHomeUpdate) || newfavs!=favs ){ + favs = newfavs; + + homefiles = LSession::handle()->DesktopFiles(); + lastHomeUpdate = QDateTime::currentDateTime(); + }else if(!newfilter){ updatingfavs = false; return; } //nothing new to change - stop now + //qDebug() << " - Passed Smoke Test..."; + QStringList favitems; + //Remember for format for favorites: ::::[app/dir/]:::: + if(ui->tool_fav_apps->isChecked()){ + favitems = favs.filter("::::app::::"); + for(int i=0; itool_fav_dirs->isChecked()){ + favitems = favs.filter("::::dir::::"); + for(int i=0; iscroll_fav); + //qDebug() << " - Sorting Items"; + favitems.sort(); //sort them alphabetically + //qDebug() << " - Creating Items:" << favitems; + for(int i=0; iscroll_fav->widget(), favitems[i].section("::::",2,50), favitems[i].section("::::",1,1) ); + if(!it->gooditem){ it->deleteLater(); continue; } + ui->scroll_fav->widget()->layout()->addWidget(it); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(NewShortcut()), this, SLOT(updateFavItems()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(updateFavItems()) ); + QApplication::processEvents(); //keep the UI snappy - might be a number of these + } + SortScrollArea(ui->scroll_fav); + updatingfavs = false; + //qDebug() << " - Done"; +} + +//Apps Tab +void UserWidget::updateAppCategories(){ + ui->combo_app_cats->clear(); + QStringList cats = sysapps->keys(); + cats.sort(); + for(int i=0; icombo_app_cats->addItem( LXDG::findIcon(icon,""), name, cats[i] ); + } +} + +void UserWidget::updateApps(){ + if(ui->combo_app_cats->currentIndex() < 0){ return; } //no cat + QString cat = ui->combo_app_cats->itemData( ui->combo_app_cats->currentIndex() ).toString(); + QList items = sysapps->value(cat); + ClearScrollArea(ui->scroll_apps); + for(int i=0; iscroll_apps->widget(), items[i]); + ui->scroll_apps->widget()->layout()->addWidget(it); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(LaunchItem(QString)) ); + connect(it, SIGNAL(NewShortcut()), this, SLOT(updateFavItems()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(updateFavItems()) ); + QApplication::processEvents(); //keep the UI snappy - might be a number of these + } +} + +//Home Tab +void UserWidget::updateHome(){ + qDebug() << "Update Home"; + ClearScrollArea(ui->scroll_home); + qDebug() << " - dir:" << ui->label_home_dir->whatsThis(); + QDir homedir(ui->label_home_dir->whatsThis()); + QStringList items; + if(QDir::homePath() == homedir.absolutePath()){ + ui->label_home_dir->setText(tr("Home")); + ui->tool_home_gohome->setVisible(false); + }else{ + qDebug() << " - Show the back button"; + ui->tool_home_gohome->setVisible(true); + ui->label_home_dir->setText( this->fontMetrics().elidedText(homedir.dirName(), Qt::ElideRight, ui->label_home_dir->width())); + //Now make sure to put a "go back" button at the top of the list + QString dir = ui->label_home_dir->whatsThis(); + if(dir.endsWith("/")){ dir.chop(1); } + dir.chop( dir.section("/",-1).length() ); + items << dir; + } + qDebug() << " - Load items"; + ui->label_home_dir->setToolTip(ui->label_home_dir->whatsThis()); + items << homedir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::DirsFirst); + QString type = ""; + if(homedir.absolutePath() == QDir::homePath()+"/Desktop"){ type.append("-home"); }//internal code + for(int i=0; iscroll_home->widget(), items[i], "dir", true); } //go-back button + else{ it = new UserItemWidget(ui->scroll_home->widget(), homedir.absoluteFilePath(items[i]), type, false); } + qDebug() << " - Add to layout"; + ui->scroll_home->widget()->layout()->addWidget(it); + connect(it, SIGNAL(RunItem(QString)), this, SLOT(slotGoToDir(QString)) ); + connect(it, SIGNAL(NewShortcut()), this, SLOT(updateFavItems()) ); + connect(it, SIGNAL(RemovedShortcut()), this, SLOT(updateFavItems()) ); + qDebug() << " - process events"; + QApplication::processEvents(); //keep the UI snappy - may be a lot of these to load + } + qDebug() << " - Done"; +} + +void UserWidget::slotGoToDir(QString dir){ + if(!QFileInfo(dir).isDir()){ + LaunchItem(dir); + }else{ + ui->label_home_dir->setWhatsThis(dir); + updateHome(); + } +} + +void UserWidget::slotGoHome(){ + slotGoToDir(QDir::homePath()); +} + +void UserWidget::slotOpenDir(){ + LaunchItem(ui->label_home_dir->whatsThis()); +} + +void UserWidget::slotOpenSearch(){ + LaunchItem("lumina-search -dir \""+ui->label_home_dir->whatsThis()+"\"", false); //use command as-is +} + +void UserWidget::mouseMoveEvent( QMouseEvent *event){ + QTabBar *wid = tabBar(); + if(wid==0){ return; } //invalid widget found + QPoint relpos = wid->mapFromGlobal( this->mapToGlobal(event->pos()) ); + //qDebug() << "Mouse Move Event: " << event->pos().x() << event->pos().y() << relpos.x() << relpos.y() << wid->width() << wid->height(); + if(wid && wid->tabAt(relpos) != -1){ + qDebug() << " - Mouse over tab"; + this->setCurrentIndex( wid->tabAt(relpos) ); + } +} diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.h b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.h new file mode 100644 index 00000000..8b03c489 --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.h @@ -0,0 +1,101 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2014, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +// This panel plugin allows the user to quickly access user favorites and applications +//=========================================== +#ifndef _LUMINA_PANEL_USER_BUTTON_WIDGET_H +#define _LUMINA_PANEL_USER_BUTTON_WIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "UserItemWidget.h" + +#define SSAVER QString("xscreensaver-demo") + +namespace Ui{ + class UserWidget; +}; + +class UserWidget : public QTabWidget{ + Q_OBJECT +public: + UserWidget(QWidget *parent=0); + ~UserWidget(); + +public slots: + void UpdateAll(); //for re-translation/icon changes + void UpdateMenu(bool forceall = false); //for item changes + +private: + Ui::UserWidget *ui; + QHash > *sysapps; + QDateTime lastUpdate, lastHomeUpdate; + QStringList favs; + QFileInfoList homefiles; + int cfav; //current favorite category + void ClearScrollArea(QScrollArea *area); + void SortScrollArea(QScrollArea *area); + QIcon rotateIcon(QIcon); + bool updatingfavs; + +private slots: + void LaunchItem(QString path, bool fix = true); + + //Favorites Tab + void FavChanged(); //for ensuring radio-button-like behaviour + void updateFavItems(bool newfilter = true); //if false, will only update if filesystem changes + + //Apps Tab + void updateAppCategories(); + void updateApps(); + + //Home Tab + void updateHome(); + void slotGoToDir(QString dir); + void slotGoHome(); + void slotOpenDir(); + void slotOpenSearch(); + + //Slots for the special buttons + void openStore(){ + LaunchItem(LOS::AppStoreShortcut()); + } + void openControlPanel(){ + LaunchItem(LOS::ControlPanelShortcut()); + } + void openDeskSettings(){ + LaunchItem("lumina-config", false); + } + void openScreenSaverConfig(){ + LaunchItem(SSAVER, false); + } + void openScreenConfig(){ + LaunchItem("lumina-xconfig",false); + } + void openLuminaInfo(){ + LaunchItem("lumina-info",false); + } + +protected: + void mouseMoveEvent( QMouseEvent *event); + +signals: + void CloseMenu(); + +}; + +#endif diff --git a/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.ui b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.ui new file mode 100644 index 00000000..9ef5af7e --- /dev/null +++ b/src-qt5/core/lumina-desktop-unified/src-DE/panel-plugins/userbutton/UserWidget.ui @@ -0,0 +1,593 @@ + + + UserWidget + + + + 0 + 0 + 294 + 289 + + + + UserWidget + + + QTabWidget::West + + + 0 + + + + Favorites + + + Favorites + + + + 2 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + ArrowCursor + + + Favorite Applications + + + Applications + + + + 20 + 20 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + Favorite Directories + + + Places + + + + 20 + 20 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + Favorite FIles + + + Files + + + + 20 + 20 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + + true + + + + + 0 + 0 + 259 + 247 + + + + + + + + + + Apps + + + Applications + + + + 2 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + 12 + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 30 + 30 + + + + + + + + 20 + 20 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + true + + + + + 0 + 0 + 98 + 28 + + + + + + + + + + Home + + + Home Directory + + + + 2 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + 4 + + + 1 + + + + + Search this Directory + + + + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + Open Directory + + + Browse + + + + 20 + 20 + + + + Qt::ToolButtonIconOnly + + + + + + + + 30 + 30 + + + + Go back to home directory + + + home + + + + 20 + 20 + + + + true + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + + 10 + 75 + true + + + + QFrame::NoFrame + + + <current dir> + + + Qt::AlignCenter + + + true + + + 0 + + + 0 + + + Qt::NoTextInteraction + + + + + + + + + true + + + + + 0 + 0 + 98 + 28 + + + + + + + + + + Config + + + Desktop Preferences + + + + + + + 0 + 0 + + + + Control Panel + + + + 20 + 20 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Desktop Appearance/Plugins + + + + 20 + 20 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Screen Configuration + + + + 20 + 20 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Screensaver Settings + + + + 20 + 20 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Vertical + + + + 243 + 162 + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + About the Lumina Desktop + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + -- cgit