diff options
53 files changed, 923 insertions, 139 deletions
@@ -1,2 +1,15 @@ global.h *.qm +Makefile +*.o +moc_*.cpp +ui_*.h +qrc_*.cpp +lumina-config/lumina-config +lumina-desktop/Lumina-DE +lumina-fm/lumina-fm +lumina-info/lumina-info +lumina-open/lumina-open +lumina-screenshot/lumina-screenshot +lumina-search/lumina-search +libLumina/libLuminaUtils.so.1.0 diff --git a/DeveloperGuidelines.txt b/DeveloperGuidelines.txt new file mode 100644 index 00000000..3ad23291 --- /dev/null +++ b/DeveloperGuidelines.txt @@ -0,0 +1,47 @@ +Notes and guidelines for developers/contributors to the Lumina desktop environment +---------------------------------------------------------------- + +1) General Organization: + a) libLumina: This is where all frameworks or general-purpose functions should be placed + LuminaOS: Any operating-system specific routines (usually running 3rd-party or OS-specific utilities) + LuminaX11: Any X11/XCB interactions. This keeps it separate for the future conversion to other graphics subsystems (Wayland?) + LuminaXDG: Any FreeDesktop/XDG standards functionality. + LuminaUtils: Any static functions/utilities that are useful for multiple utilities/plugins. + Lumina<X>: The class/framework for functionality <X> (Examples: LuminaThemes, LuminaSingleApplication) + + b) Utility naming: + Make sure that the binary names for utilities start with "lumina-". This ensures that it is easy for a command-line user to find/list lumina-specific utilities + + c) Utility Combinations: + In order to avoid adding tons of extra utilities to Lumina, see if that functionality can be easily added to existing utilities first. + For example, "lumina-open" contains not just the file detection/usage, but also the application crash handler and small/fast system interactions (for keyboard shortcut usage). + + d) 3rd-party dependencies + Try to avoid using 3rd-party dependencies whenever possible. Qt provides almost all the functionaliaty necessary (in a cross-OS capability) for 95% of what is necessary. + If another utility *is* required, place it within the LuminaOS class, and also provide a function for checking if that functionality is possible (is the utility installed?). + If the utility is *not* installed, just disable/hide that functionality within the Lumina utility and move on. + +2) General Coding Guidelines + a) Keep it simple! Try to have many small functions instead of a couple large ones. This makes it easier to maintain/update later on, and allows for a lot more "re-use" of functionality. + b) Keep it readable! Remember that other people may need to make changes in the future. + c) Keep is translatable! Try to put all the text-setting routines (for a static form) within a single function that can be re-run if the locale changes. + d) Comment what you are doing! It is very helpful to have the comments form an "outline" of the functionality so somebody reading the code can easily get an idea of what is happening. + NOTE: See reference [1] for a nice synopsis of good practices for coding. + +3) Specific Qt/Lumina Coding Guidelines + a) When using a Qt designer form (recommended for any UI with more than a couple widgets), make sure it is loaded under the "ui" namespace/variable + This ensures that when browsing the *.cpp code, it is easy to distinguish the widgets which should be configured/modified in the Qt designer utility. + b) When naming widgets, try to put a descriptor of the widget type at the front of the object name. + For example, a QComboBox should be named "combo_<something>", a QLabel would be "label_<something>". This makes it very easy to see in the code what type of widget is being used/modified at any given time. + c) Try to make function/variable names descriptive of what it manages. + For example, a QSpinBox which handles the maximum number of items could be named "spin_maxItems". If there are multiple widgets with similar functionality, make sure to distinguish them in the name (Example: spin_maxDesktopItems, spin_maxPanelItems) + d) Make any custom dialogs/windows a separate class, and ensure the filenames (*.cpp/h) are the same as the class name. + This makes it easy for someone to determine which file(s) contain the particular piece of information that is needed. + e) Don't break the event loop unless necessary! This is especially true for the lumina-desktop (plugins included) since you want to ensure that the UI remains responsive and does not "freeze" while waiting on some user-input. + f) When designing a UI, try to remember that the user might not have a mouse/keyboard when using the utility (it could be a touchscreen on a small device for instance). + g) Don't forget to update any packaging lists (pkg-plist) if you add new binaries/files that get installed. This makes it easier for packagers/distributors to know exactly what should be getting installed on the target system. + +4) When in doubt about something, just ask! + Ken Moore is usually available/active on the "Lumina-DE" channel on the Freenode IRC server, or you can contact him via GitHub or email. + +[1] https://www.codecogs.com/pages/standards/programming.htm diff --git a/debian/Lumina-DE b/debian/Lumina-DE new file mode 100755 index 00000000..96e6832a --- /dev/null +++ b/debian/Lumina-DE @@ -0,0 +1,9 @@ +#!/bin/sh + +. /etc/default/lumina-qt5ct + +if test -f /usr/bin/qt5ct && test ${LUMINA_USE_QT5CT} = TRUE; then + export QT_QPA_PLATFORMTHEME=qt5ct +fi + +/usr/bin/Lumina-DE.real diff --git a/debian/changelog b/debian/changelog index b9d3b36c..a06b640f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,18 @@ +lumina-desktop (0.8.3.372-1nano) unstable; urgency=low + + * new GIT snapshot + - add build dependency on libxcb-image0-dev + - add Lumina-DE startup wrapper. If qt5-configuration-tool exists + and LUMINA_USE_QT5CT is set to TRUE in /etc/default/lumina-qt5ct, + then export QT_QPA_PLATFORMTHEME=qt5ct, so that it works in Lumina + - add lumina-data arch-indep package containing data files + - drop lumina-core package + - change lumina-desktop from arch-any to arch-indep + - make lumina-desktop what lumina-core was before (keeping dependencies + on all lumina-* packages) + + -- Christopher Roy Bratusek <nano@jpberlin.de> Thu, 12 Mar 2015 18:11:17 +0100 + lumina-desktop (0.8.3.347-1nano) unstable; urgency=low * new GIT snapshot diff --git a/debian/control b/debian/control index 14bce48d..fda9e4c0 100644 --- a/debian/control +++ b/debian/control @@ -6,15 +6,17 @@ Build-Depends: debhelper (>= 9), qt5-qmake, qtbase5-dev, qtmultimedia5-dev, libxcb1-dev, libx11-xcb-dev, libxcb-ewmh-dev, make, g++, libx11-dev, libxrender-dev, libxcomposite-dev, libxdamage-dev, libxcb-icccm4-dev, libxcb-damage0-dev, libxcb-util0-dev, - libqt5x11extras5-dev, qttools5-dev-tools + libqt5x11extras5-dev, qttools5-dev-tools, libxcb-image0-dev Standards-Version: 3.9.5 Homepage: https://github.com/pcbsd/lumina Package: lumina-desktop -Architecture: all -Depends: libluminautils1, lumina-core, lumina-config, lumina-fm, +Architecture: any +Replaces: lumina-core (<< 0.8.3.372) +Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}), + libluminautils1, lumina-config, lumina-fm, oxygen-icon-theme, lumina-open, lumina-screenshot, lumina-search, lumina-info, - lxpolkit + lxpolkit, lumina-data, fluxbox, numlockx, xbacklight, xscreensaver Recommends: qt5-configuration-tool Description: Lightweight Qt5-based desktop environment Metapackage depending on all other lumina packages. @@ -38,23 +40,10 @@ Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils-dev (= ${binary:Vers Description: Debugging symbols for lumina desktop environment Debugging symbols for libluminautils1 -Package: lumina-core -Architecture: any -Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}), - fluxbox, numlockx, xbacklight, xscreensaver, oxygen-icon-theme -Description: Core package for the lumina desktop environment - This package provides the basic components of lumina - - session manager - - panel with plugins - - wallpaper setter - - fluxbox configurator - - desktop icon manager and menu - - wallpapers - Package: lumina-config Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: Configuration utility for the lumina desktop environment lumina-config allows to change various aspects of lumina and fluxbox, like the wallpaper beeing used, startup-applications, desktop-menu and more. @@ -62,7 +51,7 @@ Description: Configuration utility for the lumina desktop environment Package: lumina-fm Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: Filemanager for the lumina desktop environment Simple filemanager for lumina with support for multiple view modes and integrated slideshow-based picture viewer. @@ -70,7 +59,7 @@ Description: Filemanager for the lumina desktop environment Package: lumina-open Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: xdg-open like utilityfor the lumina desktop environment lumina-open handles opening of files and urls according to the system wide mime type association. It also provides an optional selector if more than one @@ -79,7 +68,7 @@ Description: xdg-open like utilityfor the lumina desktop environment Package: lumina-screenshot Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: Screenshot utility for the lumina desktop environment Simple screenshot utility that allows to snapshot the whole desktop or a single window after a configurable delay. @@ -87,7 +76,7 @@ Description: Screenshot utility for the lumina desktop environment Package: lumina-search Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: Search utility for the lumina desktop environment Simple search utility that allows to search for applications or files and directories in the user's HOME directory. @@ -95,7 +84,7 @@ Description: Search utility for the lumina desktop environment Package: lumina-info Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: Basic information utility for Lumina lumina-info display various information about the Lumina installation, like paths or version. @@ -103,7 +92,17 @@ Description: Basic information utility for Lumina Package: lumina-xconfig Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}) -Replaces: lumina-core (<< 0.8.2) +Replaces: lumina-core (<< 0.8.3.372) Description: Display configuration tool for the lumina desktop environment - lumina-info display various information about the Lumina installation, - like paths or version. + Simple multi-head aware display configuration tool for Lumina + +Package: lumina-data +Architecture: all +Depends: ${misc:Depends}, ${shlibs:Depends}, libluminautils1 (= ${binary:Version}), lumina-desktop (= ${binary:Version}) +Replaces: lumina-core (<< 0.8.3.372) +Description: Data files for the lumina Desktop environment + - Lumina Wallpapers + - Lumina Themes + - Lumina Translations + - Lumina Sounds + - Fallback fluxbox configuration diff --git a/debian/lumina-core.install b/debian/lumina-data.install index d1d143e8..cbf45f34 100644 --- a/debian/lumina-core.install +++ b/debian/lumina-data.install @@ -1,4 +1,3 @@ -usr/bin/Lumina-DE usr/share/pixmaps/Lumina-DE.png usr/share/Lumina-DE/colors/ usr/share/Lumina-DE/themes/ diff --git a/debian/lumina-desktop.install b/debian/lumina-desktop.install new file mode 100644 index 00000000..748ddff0 --- /dev/null +++ b/debian/lumina-desktop.install @@ -0,0 +1,2 @@ +usr/bin/Lumina-DE + diff --git a/debian/lumina-qt5ct b/debian/lumina-qt5ct new file mode 100644 index 00000000..9553f94c --- /dev/null +++ b/debian/lumina-qt5ct @@ -0,0 +1,7 @@ +# +# if LUMINA_USE_QT5CT is TRUE Lumina Desktop +# will use qt5-configuration-tool for theming +# Qt. Please note that this will override some +# of the Lumina settings in 'lumina-config' + +LUMINA_USE_QT5CT=FALSE diff --git a/debian/rules b/debian/rules index f86bf5e3..d15b44d9 100755 --- a/debian/rules +++ b/debian/rules @@ -38,11 +38,18 @@ override_dh_strip: override_dh_install: dh_install --fail-missing - mkdir $(CURDIR)/debian/lumina-core/etc/ + mkdir $(CURDIR)/debian/lumina-desktop/etc/ install -m644 $(CURDIR)/debian/luminaDesktop.conf \ - $(CURDIR)/debian/lumina-core/etc/luminaDesktop.conf + $(CURDIR)/debian/lumina-desktop/etc/luminaDesktop.conf echo "/usr/lib/$(DEB_HOST_MULTIARCH)/lxpolkit" > \ - $(CURDIR)/debian/lumina-core/etc/luminaStartapps + $(CURDIR)/debian/lumina-desktop/etc/luminaStartapps + mv $(CURDIR)/debian/lumina-desktop/usr/bin/Lumina-DE \ + $(CURDIR)/debian/lumina-desktop/usr/bin/Lumina-DE.real + install -m755 $(CURDIR)/debian/Lumina-DE \ + $(CURDIR)/debian/lumina-desktop/usr/bin/Lumina-DE + mkdir -p $(CURDIR)/debian/lumina-desktop/etc/default + install -m644 $(CURDIR)/debian/lumina-qt5ct \ + $(CURDIR)/debian/lumina-desktop/etc/default/lumina-qt5ct override_dh_auto_install: INSTALL_ROOT=$(CURDIR)/debian/tmp/ $(MAKE) install diff --git a/libLumina/LuminaOS-Debian.cpp b/libLumina/LuminaOS-Debian.cpp index 8228d7c4..4f2032fa 100644 --- a/libLumina/LuminaOS-Debian.cpp +++ b/libLumina/LuminaOS-Debian.cpp @@ -193,4 +193,9 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return 0; //not implemented yet for Linux } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + return QStringList(); +} + #endif diff --git a/libLumina/LuminaOS-DragonFly.cpp b/libLumina/LuminaOS-DragonFly.cpp index dd9320fc..35bff04c 100644 --- a/libLumina/LuminaOS-DragonFly.cpp +++ b/libLumina/LuminaOS-DragonFly.cpp @@ -171,4 +171,9 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return LUtils::getCmdOutput("apm -t").join("").toInt(); } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + return QStringList(); +} + #endif diff --git a/libLumina/LuminaOS-FreeBSD.cpp b/libLumina/LuminaOS-FreeBSD.cpp index c58d2397..d454ce22 100644 --- a/libLumina/LuminaOS-FreeBSD.cpp +++ b/libLumina/LuminaOS-FreeBSD.cpp @@ -152,12 +152,12 @@ bool LOS::userHasShutdownAccess(){ //System Shutdown void LOS::systemShutdown(){ //start poweroff sequence - QProcess::startDetached("shutdown -p now"); + QProcess::startDetached("shutdown -po now"); } //System Restart void LOS::systemRestart(){ //start reboot sequence - QProcess::startDetached("shutdown -r now"); + QProcess::startDetached("shutdown -ro now"); } //Battery Availability @@ -183,4 +183,16 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return LUtils::getCmdOutput("apm -t").join("").toInt(); } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + QStringList info = LUtils::getCmdOutput("md5 \""+filepaths.join("\" \"")+"\""); + for(int i=0; i<info.length(); i++){ + if( !info[i].contains(" = ") ){ info.removeAt(i); i--; } + else{ + //Strip out the extra information + info[i] = info[i].section(" = ",1,1); + } + } + return info; +} #endif diff --git a/libLumina/LuminaOS-Linux.cpp b/libLumina/LuminaOS-Linux.cpp index f0f427f4..7587a29b 100644 --- a/libLumina/LuminaOS-Linux.cpp +++ b/libLumina/LuminaOS-Linux.cpp @@ -190,4 +190,8 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return 0; //not implemented yet for Linux } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + return QStringList(); +} #endif diff --git a/libLumina/LuminaOS-OpenBSD.cpp b/libLumina/LuminaOS-OpenBSD.cpp index 2c86995a..b6996795 100644 --- a/libLumina/LuminaOS-OpenBSD.cpp +++ b/libLumina/LuminaOS-OpenBSD.cpp @@ -190,4 +190,21 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return (min * 60); } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + //on OpenBSD md5 has the following layout + //>md5 LuminaThemes.o LuminaUtils.o + //MD5 (LuminaThemes.o) = 50006505d9d7e54e5154eeb095555055 + //MD5 (LuminaUtils.o) = d490878ee8866e55e5af571b98b4d448 + + QStringList info = LUtils::getCmdOutput("md5 \""+filepaths.join("\" \"")+"\""); + for(int i=0; i<info.length(); i++){ + if( !info[i].contains(" = ") ){ info.removeAt(i); i--; } + else{ + //Strip out the extra information + info[i] = info[i].section(" = ",1,1); + } + } + return info; +} #endif diff --git a/libLumina/LuminaOS-kFreeBSD.cpp b/libLumina/LuminaOS-kFreeBSD.cpp index ab9f9827..16c8e6eb 100644 --- a/libLumina/LuminaOS-kFreeBSD.cpp +++ b/libLumina/LuminaOS-kFreeBSD.cpp @@ -153,6 +153,10 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return 0; //not implemented yet for Linux } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + return QStringList(); +} + #endif #endif - diff --git a/libLumina/LuminaOS-template.cpp b/libLumina/LuminaOS-template.cpp index d481984f..c06b8b11 100644 --- a/libLumina/LuminaOS-template.cpp +++ b/libLumina/LuminaOS-template.cpp @@ -101,4 +101,9 @@ int LOS::batterySecondsLeft(){ //Returns: estimated number of seconds remaining return 0; //not implemented yet } +//File Checksums +QStringList LOS::Checksums(QStringList filepaths){ //Return: checksum of the input file + return QStringList(); +} + #endif diff --git a/libLumina/LuminaOS.h b/libLumina/LuminaOS.h index e0eb2895..92a7dc7e 100644 --- a/libLumina/LuminaOS.h +++ b/libLumina/LuminaOS.h @@ -80,6 +80,9 @@ public: static bool batteryIsCharging(); //Battery Time Remaining static int batterySecondsLeft(); //Returns: estimated number of seconds remaining + + //Get the checksum for a file + static QStringList Checksums(QStringList filepaths); //Return: checksum of each input file (same order) }; #endif diff --git a/libLumina/LuminaThemes.cpp b/libLumina/LuminaThemes.cpp index 269af259..e4c13ae7 100644 --- a/libLumina/LuminaThemes.cpp +++ b/libLumina/LuminaThemes.cpp @@ -177,10 +177,14 @@ LuminaThemeEngine::LuminaThemeEngine(QApplication *app){ theme = current[0]; colors=current[1]; icons=current[2]; font=current[3]; fontsize=current[4]; application->setStyleSheet( LTHEME::assembleStyleSheet(theme, colors, font, fontsize) ); QIcon::setThemeName(icons); //make sure this sets set within this environment + syncTimer = new QTimer(this); + syncTimer->setSingleShot(true); + syncTimer->setInterval(500); //wait 1/2 second before re-loading the files watcher = new QFileSystemWatcher(this); watcher->addPath( QDir::homePath()+"/.lumina/themesettings.cfg" ); watcher->addPaths( QStringList() << theme << colors ); //also watch these files for changes connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherChange()) ); + connect(syncTimer, SIGNAL(timeout()), this, SLOT(reloadFiles()) ); } LuminaThemeEngine::~LuminaThemeEngine(){ @@ -188,6 +192,11 @@ LuminaThemeEngine::~LuminaThemeEngine(){ } void LuminaThemeEngine::watcherChange(){ + if(syncTimer->isActive()){ syncTimer->stop(); } + syncTimer->start(); +} + +void LuminaThemeEngine::reloadFiles(){ QStringList current = LTHEME::currentSettings(); application->setStyleSheet( LTHEME::assembleStyleSheet(current[0], current[1], current[3], current[4]) ); diff --git a/libLumina/LuminaThemes.h b/libLumina/LuminaThemes.h index e4bbd208..9b5ad5bc 100644 --- a/libLumina/LuminaThemes.h +++ b/libLumina/LuminaThemes.h @@ -16,6 +16,7 @@ #include <QString> #include <QFile> #include <QDir> +#include <QTimer> class LTHEME{ public: @@ -63,9 +64,11 @@ private: QApplication *application; QFileSystemWatcher *watcher; QString theme,colors,icons, font, fontsize; //current settings + QTimer *syncTimer; private slots: void watcherChange(); + void reloadFiles(); signals: void updateIcons(); diff --git a/libLumina/LuminaUtils.cpp b/libLumina/LuminaUtils.cpp index 9b8a1860..f7f5db74 100644 --- a/libLumina/LuminaUtils.cpp +++ b/libLumina/LuminaUtils.cpp @@ -144,6 +144,50 @@ void LUtils::LoadTranslation(QApplication *app, QString appname){ QTextCodec::setCodecForLocale( QTextCodec::codecForName(langEnc.toUtf8()) ); } +QStringList LUtils::listFavorites(){ + QStringList fav = LUtils::readFile(QDir::homePath()+"/.lumina/favorites/fav.list"); + return fav; +} + +bool LUtils::saveFavorites(QStringList list){ + return LUtils::writeFile(QDir::homePath()+"/.lumina/favorites/fav.list", list, true); +} + +bool LUtils::isFavorite(QString path){ + QStringList fav = LUtils::listFavorites(); + for(int i=0; i<fav.length(); i++){ + if(fav[i].endsWith("::::"+path)){ return true; } + } + return false; +} + +bool LUtils::addFavorite(QString path, QString name){ + //Generate the type of favorite this is + QFileInfo info(path); + QString type = "file"; + if(info.isDir()){ type="dir"; } + else if(info.suffix()=="desktop"){ type="app"; } + //Assign a name if none given + if(name.isEmpty()){ name = info.fileName(); } + //Now add it to the list + QStringList fav = LUtils::listFavorites(); + bool found = false; + for(int i=0; i<fav.length(); i++){ + if(fav[i].endsWith("::::"+path)){ fav[i] = name+"::::"+type+"::::"+path; } + } + if(!found){ fav << name+"::::"+type+"::::"+path; } + return LUtils::saveFavorites(fav); +} + +void LUtils::removeFavorite(QString path){ + QStringList fav = LUtils::listFavorites(); + bool changed = false; + for(int i=0; i<fav.length(); i++){ + if(fav[i].endsWith("::::"+path)){ fav.removeAt(i); i--; changed=true;} + } + if(changed){ LUtils::saveFavorites(fav); } +} + void LUtils::LoadSystemDefaults(bool skipOS){ //Will create the Lumina configuration files based on the current system template (if any) QStringList sysDefaults; diff --git a/libLumina/LuminaUtils.h b/libLumina/LuminaUtils.h index ee716167..d0c8b3ad 100644 --- a/libLumina/LuminaUtils.h +++ b/libLumina/LuminaUtils.h @@ -44,6 +44,16 @@ public: //Load a translation file for a Lumina Project static void LoadTranslation(QApplication *app, QString appname); + + //Various functions for the favorites sub-system + // Formatting Note: "<name>::::[dir/file/app]::::<path>" + // the <name> field might not be used for "app" flagged entries + static QStringList listFavorites(); + static bool saveFavorites(QStringList); + static bool isFavorite(QString path); + static bool addFavorite(QString path, QString name = ""); + static void removeFavorite(QString path); + //Load the default setup for the system static void LoadSystemDefaults(bool skipOS = false); diff --git a/libLumina/LuminaX11.cpp b/libLumina/LuminaX11.cpp index e73f124c..43d4e577 100644 --- a/libLumina/LuminaX11.cpp +++ b/libLumina/LuminaX11.cpp @@ -25,6 +25,7 @@ #include <xcb/xproto.h> #include <xcb/xcb_ewmh.h> #include <xcb/xcb_icccm.h> +#include <xcb/xcb_image.h> //===== WindowList() ======== @@ -425,6 +426,8 @@ bool LX11::EmbedWindow(WId win, WId container){ //qDebug() << "Embed Window:" << win << container; XReparentWindow(disp, win, container,0,0); XSync(disp, false); + //Map the window + XMapRaised(disp, win); //make it visible again and raise it to the top //Check that the window has _XEMBED_INFO //qDebug() << " - check for _XEMBED_INFO"; Atom embinfo = XInternAtom(disp, "_XEMBED_INFO",false); @@ -436,7 +439,7 @@ bool LX11::EmbedWindow(WId win, WId container){ return false; //Embedding error (no info?) } if(data){ XFree(data); } // clean up any data found - + //Now send the embed event to the app //qDebug() << " - send _XEMBED event"; XEvent ev; @@ -515,7 +518,7 @@ QString LX11::WindowVisibleIconName(WId win){ } // ===== WindowIcon() ===== -QIcon LX11::WindowIcon(WId win){ +/*QIcon LX11::WindowIcon(WId win){ //Use the _NET_WM_ICON value instead of the WMHints pixmaps // - the pixmaps are very unstable and erratic QIcon icon; @@ -544,11 +547,11 @@ QIcon LX11::WindowIcon(WId win){ XFree(data); } return icon; -} +}*/ // ===== WindowImage() ===== -QPixmap LX11::WindowImage(WId win, bool useleader){ +/*QPixmap LX11::WindowImage(WId win, bool useleader){ QPixmap pix; Display *disp = QX11Info::display(); WId leader = LX11::leaderWindow(win); //check for an alternate window that contains the image @@ -565,7 +568,7 @@ QPixmap LX11::WindowImage(WId win, bool useleader){ } //Return the pixmap return pix; -} +}*/ // ===== GetNumberOfDesktops() ===== int LX11::WindowDesktop(WId win){ @@ -1009,7 +1012,7 @@ LXCB::WINDOWSTATE LXCB::WindowState(WId win){ } // === WindowVisibleIconName() === -QString LXCB::WindowVisibleIconName(WId win){ //_WM_VISIBLE_ICON_NAME +QString LXCB::WindowVisibleIconName(WId win){ //_NET_WM_VISIBLE_ICON_NAME QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_icon_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } @@ -1021,7 +1024,7 @@ QString LXCB::WindowVisibleIconName(WId win){ //_WM_VISIBLE_ICON_NAME } // === WindowIconName() === -QString LXCB::WindowIconName(WId win){ //_WM_ICON_NAME +QString LXCB::WindowIconName(WId win){ //_NET_WM_ICON_NAME QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } @@ -1033,7 +1036,7 @@ QString LXCB::WindowIconName(WId win){ //_WM_ICON_NAME } // === WindowVisibleName() === -QString LXCB::WindowVisibleName(WId win){ //_WM_VISIBLE_NAME +QString LXCB::WindowVisibleName(WId win){ //_NET_WM_VISIBLE_NAME QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } @@ -1045,7 +1048,7 @@ QString LXCB::WindowVisibleName(WId win){ //_WM_VISIBLE_NAME } // === WindowName() === -QString LXCB::WindowName(WId win){ //_WM_NAME +QString LXCB::WindowName(WId win){ //_NET_WM_NAME QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } @@ -1056,6 +1059,32 @@ QString LXCB::WindowName(WId win){ //_WM_NAME return out; } +// === OldWindowName() === +QString LXCB::OldWindowName(WId win){ //WM_NAME (old standard) + xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name_unchecked(QX11Info::connection(), win); + xcb_icccm_get_text_property_reply_t reply; + if(1 == xcb_icccm_get_wm_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){ + QString name = QString::fromLocal8Bit(reply.name); + xcb_icccm_get_text_property_reply_wipe(&reply); + return name; + }else{ + return ""; + } +} + +// === OldWindowIconName() === +QString LXCB::OldWindowIconName(WId win){ //WM_ICON_NAME (old standard) + xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_icon_name_unchecked(QX11Info::connection(), win); + xcb_icccm_get_text_property_reply_t reply; + if(1 == xcb_icccm_get_wm_icon_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){ + QString name = QString::fromLocal8Bit(reply.name); + xcb_icccm_get_text_property_reply_wipe(&reply); + return name; + }else{ + return ""; + } +} + // === WindowIsMaximized() === bool LXCB::WindowIsMaximized(WId win){ //See if the _NET_WM_STATE_MAXIMIZED_[VERT/HORZ] flags are set on the window @@ -1074,6 +1103,56 @@ bool LXCB::WindowIsMaximized(WId win){ return false; } +// === WindowIcon() === +QIcon LXCB::WindowIcon(WId win){ + //Fetch the _NET_WM_ICON for the window and return it as a QIcon + QIcon icon; + xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_unchecked(&EWMH, win); + xcb_ewmh_get_wm_icon_reply_t reply; + if(1 == xcb_ewmh_get_wm_icon_reply(&EWMH, cookie, &reply, NULL)){ + xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&reply); + //Just use the first + bool done =false; + while(!done){ + //Now convert the current data into a Qt image + // - first 2 elements are width and height (removed via XCB functions) + // - data in rows from left to right and top to bottom + QImage image(iter.width, iter.height, QImage::Format_ARGB32); //initial setup + uint* dat = iter.data; + //dat+=2; //remember the first 2 element offset + for(int i=0; i<image.byteCount()/4; ++i, ++dat){ + ((uint*)image.bits())[i] = *dat; + } + icon.addPixmap(QPixmap::fromImage(image)); //layer this pixmap onto the icon + //Now see if there are any more icons available + done = (iter.rem<1); //number of icons remaining + if(!done){ xcb_ewmh_get_wm_icon_next(&iter); } //get the next icon data + } + xcb_ewmh_get_wm_icon_reply_wipe(&reply); + } + return icon; +} + +// === WindowImage() === +QPixmap LXCB::WindowImage(WId win){ + QPixmap pix; + + //First get the size of the window + xcb_get_geometry_cookie_t cookie = xcb_get_geometry_unchecked(QX11Info::connection(), win); + xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(QX11Info::connection(), cookie, NULL); + if(reply == 0){ return pix; } //could not determine window geometry + //Now get the image + xcb_image_t *img = xcb_image_get(QX11Info::connection(), win, 0, 0, reply->width, reply->height, (uint32_t) AllPlanes, XCB_IMAGE_FORMAT_Z_PIXMAP); + if(img!=0){ + //Now convert the image into a QPixmap + pix.convertFromImage( QImage( (const uchar*) img->data, img->width, img->height, img->stride, QImage::Format_ARGB32_Premultiplied) ); + //Clean up the xcb_image structure + xcb_image_destroy(img); + } + //Return the pixmap + return pix; +} + // === SetAsSticky() === void LXCB::SetAsSticky(WId win){ //Need to send a client message event for the window so the WM picks it up @@ -1295,4 +1374,4 @@ void LXCB::MoveResizeWindow(WId win, QRect geom){ xcb_ewmh_set_workarea(&EWMH, 0, desks, dareas); //_NET_WORKAREA //Make sure to clear that reply xcb_ewmh_get_workarea_reply_wipe(&work); -}*/
\ No newline at end of file +}*/ diff --git a/libLumina/LuminaX11.h b/libLumina/LuminaX11.h index ef6fa676..62146ebe 100644 --- a/libLumina/LuminaX11.h +++ b/libLumina/LuminaX11.h @@ -91,8 +91,8 @@ public: static QString WindowVisibleName(WId); // long name (translated) static QString WindowIconName(WId); // short name (untranslated) static QString WindowVisibleIconName(WId); // short name (translated) - static QIcon WindowIcon(WId); // Icon for the window - static QPixmap WindowImage(WId win, bool useleader=true); // Image for the window + //static QIcon WindowIcon(WId); // Icon for the window + //static QPixmap WindowImage(WId win, bool useleader=true); // Image for the window static int WindowDesktop(WId); // Which virtual desktop the window is on static WINDOWSTATE GetWindowState(WId win); //State of activity static WId leaderWindow(WId); //Get the main window if this one is a redirect @@ -133,11 +133,15 @@ public: unsigned int WindowWorkspace(WId); //The workspace the window is on QRect WindowGeometry(WId, bool includeFrame = true); //the geometry of the window (frame excluded) WINDOWSTATE WindowState(WId win); //Visible state of window - QString WindowVisibleIconName(WId win); //_WM_VISIBLE_ICON_NAME - QString WindowIconName(WId win); //_WM_ICON_NAME - QString WindowVisibleName(WId win); //_WM_VISIBLE_NAME - QString WindowName(WId win); //_WM_NAME + QString WindowVisibleIconName(WId win); //_NET_WM_VISIBLE_ICON_NAME + QString WindowIconName(WId win); //_NET_WM_ICON_NAME + QString WindowVisibleName(WId win); //_NET_WM_VISIBLE_NAME + QString WindowName(WId win); //_NET_WM_NAME + QString OldWindowName(WId win); //WM_NAME (old standard) + QString OldWindowIconName(WId win); //WM_ICON_NAME (old standard) bool WindowIsMaximized(WId win); + QIcon WindowIcon(WId win); //_NET_WM_ICON + QPixmap WindowImage(WId win); //Pull the image directly from the window //Window Modification void SetAsSticky(WId); //Stick to all workspaces diff --git a/libLumina/LuminaXDG.cpp b/libLumina/LuminaXDG.cpp index 48abb40a..e4839480 100644 --- a/libLumina/LuminaXDG.cpp +++ b/libLumina/LuminaXDG.cpp @@ -20,7 +20,7 @@ XDGDesktop LXDG::loadDesktopFile(QString filePath, bool& ok){ DF.isHidden=false; DF.useTerminal=false; DF.startupNotify=false; - DF.type = XDGDesktop::BAD; + DF.type = XDGDesktop::APP; DF.filePath = filePath; DF.exec = DF.tryexec = ""; // just to make sure this is initialized //Check input file path validity @@ -83,7 +83,7 @@ XDGDesktop LXDG::loadDesktopFile(QString filePath, bool& ok){ if(val.toLower()=="application"){ DF.type = XDGDesktop::APP; } else if(val.toLower()=="link"){ DF.type = XDGDesktop::LINK; } else if(val.toLower()=="dir"){ DF.type = XDGDesktop::DIR; } - else{ DF.type = XDGDesktop::BAD; } + else{ DF.type = XDGDesktop::BAD; } //Unknown type } } //end reading file file.close(); @@ -255,10 +255,11 @@ QString LXDG::getDesktopExec(XDGDesktop app){ out = app.exec; } //Now perform any of the XDG flag substitutions as appropriate (9/2014 standards) - if(out.contains("%i")){ out.replace("%i", "--icon \'"+app.icon+"\'"); } + if(out.contains("%i") && !app.icon.isEmpty() ){ out.replace("%i", "--icon \'"+app.icon+"\'"); } if(out.contains("%c")){ - if(!app.name.isEmpty()){ out.replace("%c", ""+app.name+""); } - else if(!app.genericName.isEmpty()){ out.replace(" %c ", ""+app.genericName+""); } + if(!app.name.isEmpty()){ out.replace("%c", "\'"+app.name+"\'"); } + else if(!app.genericName.isEmpty()){ out.replace("%c", "\'"+app.genericName+"\'"); } + else{ out.replace("%c", "\'"+app.filePath.section("/",-1).section(".desktop",0,0)+"\'"); } } if(out.contains("%k")){ out.replace("%k", "\'"+app.filePath+"\'"); } return out; diff --git a/libLumina/libLumina.pro b/libLumina/libLumina.pro index 7606210b..e0963fc6 100644 --- a/libLumina/libLumina.pro +++ b/libLumina/libLumina.pro @@ -42,7 +42,7 @@ SOURCES += LuminaXDG.cpp \ INCLUDEPATH += $$PREFIX/include -LIBS += -lX11 -lXrender -lXcomposite -lxcb -lxcb-ewmh -lxcb-icccm +LIBS += -lX11 -lXrender -lXcomposite -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image include.path=$$PREFIX/include/ include.files=LuminaXDG.h \ diff --git a/lumina-config/LPlugins.cpp b/lumina-config/LPlugins.cpp index 55a5ee9e..94b61f34 100644 --- a/lumina-config/LPlugins.cpp +++ b/lumina-config/LPlugins.cpp @@ -135,13 +135,20 @@ void LPlugins::LoadPanelPlugins(){ info.ID = "homebutton"; info.icon = "go-home"; PANEL.insert(info.ID, info); - //Desktop Bar + //Start Menu info = LPI(); //clear it info.name = QObject::tr("Start Menu"); info.description = QObject::tr("This provides instant-access to application that are installed on the system."); info.ID = "appmenu"; info.icon = "Lumina-DE"; PANEL.insert(info.ID, info); + //Application Launcher + info = LPI(); //clear it + info.name = QObject::tr("Application Launcher"); + info.description = QObject::tr("Pin an application shortcut directly to the panel"); + info.ID = "applauncher"; + info.icon = "quickopen"; + PANEL.insert(info.ID, info); } void LPlugins::LoadDesktopPlugins(){ diff --git a/lumina-config/mainUI.cpp b/lumina-config/mainUI.cpp index 34b25b3d..744d4bdf 100644 --- a/lumina-config/mainUI.cpp +++ b/lumina-config/mainUI.cpp @@ -11,6 +11,7 @@ #include <QImageReader> #include <QTime> #include <QDate> +#include <QTimeZone> MainUI::MainUI() : QMainWindow(), ui(new Ui::MainUI()){ ui->setupUi(this); //load the designer file @@ -235,6 +236,7 @@ void MainUI::setupConnections(){ connect(ui->tool_help_date, SIGNAL(clicked()), this, SLOT(sessionShowDateCodes()) ); connect(ui->line_session_time, SIGNAL(textChanged(QString)), this, SLOT(sessionLoadTimeSample()) ); connect(ui->line_session_date, SIGNAL(textChanged(QString)), this, SLOT(sessionLoadDateSample()) ); + connect(ui->combo_session_timezone, SIGNAL(currentIndexChanged(int)), this, SLOT(sessionoptchanged()) ); } void MainUI::setupMenus(){ @@ -273,7 +275,28 @@ void MainUI::setupMenus(){ ui->combo_session_wtheme->addItem(fbstyles[i], fbdir.absoluteFilePath(fbstyles[i])); } - + //Available Time zones + ui->combo_session_timezone->clear(); + QList<QByteArray> TZList = QTimeZone::availableTimeZoneIds(); + QDateTime DT = QDateTime::currentDateTime(); + QStringList tzlist; //Need to create a list which can be sorted appropriately + for(int i=0; i<TZList.length(); i++){ + QTimeZone TZ(TZList[i]); + if(TZ.country()<=0){ continue; } //skip this one + QString name = QLocale::countryToString(TZ.country()); + if(name.count() > 20){ name = name.left(20)+"..."; } + name = QString(tr("%1 (%2)")).arg(name, TZ.abbreviation(DT)); + if(tzlist.filter(name).isEmpty()){ + tzlist << name+"::::"+QString::number(i); + } + } + tzlist.sort(); + for(int i=0; i<tzlist.length(); i++){ + ui->combo_session_timezone->addItem( tzlist[i].section("::::",0,0), TZList[tzlist[i].section("::::",1,1).toInt()]); + } + //ui->combo_session_timezone->sort(); + //Now set the default/system value + ui->combo_session_timezone->insertItem(0,tr("System Time")); } int MainUI::currentDesktop(){ @@ -486,11 +509,21 @@ void MainUI::loadCurrentSettings(bool screenonly){ ui->list_panel1_plugins->clear(); for(int i=0; i<plugs.length(); i++){ QString pid = plugs[i].section("---",0,0); - LPI info = PINFO->panelPluginInfo(pid); - if(!info.ID.isEmpty()){ - QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(info.icon,""), info.name ); + if(pid.startsWith("applauncher")){ + bool ok = false; + XDGDesktop desk = LXDG::loadDesktopFile(pid.section("::",1,1),ok); + if(ok){ + QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(desk.icon,""), desk.name ); + it->setWhatsThis(plugs[i]); //make sure to preserve the entire plugin ID (is the unique version) + ui->list_panel1_plugins->addItem(it); + } + }else{ + LPI info = PINFO->panelPluginInfo(pid); + if(!info.ID.isEmpty()){ + QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(info.icon,""), info.name ); it->setWhatsThis(plugs[i]); //make sure to preserve the entire plugin ID (is the unique version) - ui->list_panel1_plugins->addItem(it); + ui->list_panel1_plugins->addItem(it); + } } } QString color = settings->value(PPrefix+"color","rgba(255,255,255,160)").toString(); @@ -951,14 +984,26 @@ void MainUI::addpanel1plugin(){ dlg.exec(); if(!dlg.selected){ return; } //cancelled QString pan = dlg.plugID; //getNewPanelPlugin(); - if(pan.isEmpty()){ return; } //nothing selected - //Add the new plugin to the list - LPI info = PINFO->panelPluginInfo(pan); - QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(info.icon,""), info.name); - it->setWhatsThis(info.ID); - ui->list_panel1_plugins->addItem(it); - ui->list_panel1_plugins->setCurrentItem(it); - ui->list_panel1_plugins->scrollToItem(it); + if(pan == "applauncher"){ + //Prompt for the application to add + XDGDesktop app = getSysApp(); + if(app.filePath.isEmpty()){ return; } //cancelled + pan.append("::"+app.filePath); + QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(app.icon,""), app.name); + it->setWhatsThis(pan); + ui->list_panel1_plugins->addItem(it); + ui->list_panel1_plugins->setCurrentItem(it); + ui->list_panel1_plugins->scrollToItem(it); + }else{ + if(pan.isEmpty()){ return; } //nothing selected + //Add the new plugin to the list + LPI info = PINFO->panelPluginInfo(pan); + QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(info.icon,""), info.name); + it->setWhatsThis(info.ID); + ui->list_panel1_plugins->addItem(it); + ui->list_panel1_plugins->setCurrentItem(it); + ui->list_panel1_plugins->scrollToItem(it); + } checkpanels(); //update buttons if(!loading){ ui->push_save->setEnabled(true); modpan = true; } } @@ -969,14 +1014,26 @@ void MainUI::addpanel2plugin(){ dlg.exec(); if(!dlg.selected){ return; } //cancelled QString pan = dlg.plugID; //getNewPanelPlugin(); - if(pan.isEmpty()){ return; } //nothing selected - //Add the new plugin to the list - LPI info = PINFO->panelPluginInfo(pan); - QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(info.icon,""), info.name); - it->setWhatsThis(info.ID); - ui->list_panel2_plugins->addItem(it); - ui->list_panel2_plugins->setCurrentItem(it); - ui->list_panel2_plugins->scrollToItem(it); + if(pan == "applauncher"){ + //Prompt for the application to add + XDGDesktop app = getSysApp(); + if(app.filePath.isEmpty()){ return; } //cancelled + pan.append("::"+app.filePath); + QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(app.icon,""), app.name); + it->setWhatsThis(pan); + ui->list_panel2_plugins->addItem(it); + ui->list_panel2_plugins->setCurrentItem(it); + ui->list_panel2_plugins->scrollToItem(it); + }else{ + if(pan.isEmpty()){ return; } //nothing selected + //Add the new plugin to the list + LPI info = PINFO->panelPluginInfo(pan); + QListWidgetItem *it = new QListWidgetItem( LXDG::findIcon(info.icon,""), info.name); + it->setWhatsThis(info.ID); + ui->list_panel2_plugins->addItem(it); + ui->list_panel2_plugins->setCurrentItem(it); + ui->list_panel2_plugins->scrollToItem(it); + } checkpanels(); //update buttons if(!loading){ ui->push_save->setEnabled(true); modpan = true; } } @@ -1745,6 +1802,14 @@ void MainUI::loadSessionSettings(){ ui->push_session_setUserIcon->setIcon( LXDG::findIcon(QDir::homePath()+"/.loginIcon.png", "user-identity") ); ui->line_session_time->setText( sessionsettings->value("TimeFormat","").toString() ); ui->line_session_date->setText( sessionsettings->value("DateFormat","").toString() ); + if( !sessionsettings->value("CustomTimeZone", false).toBool() ){ + //System Time selected + ui->combo_session_timezone->setCurrentIndex(0); + }else{ + int index = ui->combo_session_timezone->findData( sessionsettings->value("TimeZoneByteCode",QByteArray()).toByteArray() ); + if(index>0){ ui->combo_session_timezone->setCurrentIndex(index); } + else{ ui->combo_session_timezone->setCurrentIndex(0); } + } //Now do the session theme options ui->combo_session_themefile->clear(); @@ -1837,6 +1902,14 @@ void MainUI::saveSessionSettings(){ sessionsettings->setValue("PlayLogoutAudio", ui->check_session_playlogoutaudio->isChecked()); sessionsettings->setValue("TimeFormat", ui->line_session_time->text()); sessionsettings->setValue("DateFormat", ui->line_session_date->text()); + if( ui->combo_session_timezone->currentIndex()==0){ + //System Time selected + sessionsettings->setValue("CustomTimeZone", false); + sessionsettings->setValue("TimeZoneByteCode", QByteArray()); //clear the value + }else{ + sessionsettings->setValue("CustomTimeZone", true); + sessionsettings->setValue("TimeZoneByteCode", ui->combo_session_timezone->currentData().toByteArray()); //clear the value + } //Now do the theme options QString themefile = ui->combo_session_themefile->itemData( ui->combo_session_themefile->currentIndex() ).toString(); @@ -1844,6 +1917,7 @@ void MainUI::saveSessionSettings(){ QString iconset = ui->combo_session_icontheme->currentText(); QString font = ui->font_session_theme->currentFont().family(); QString fontsize = QString::number(ui->spin_session_fontsize->value())+"pt"; + //qDebug() << "Saving theme options:" << themefile << colorfile << iconset << font << fontsize; LTHEME::setCurrentSettings( themefile, colorfile, iconset, font, fontsize); } diff --git a/lumina-config/mainUI.ui b/lumina-config/mainUI.ui index fcebaff7..07abacab 100644 --- a/lumina-config/mainUI.ui +++ b/lumina-config/mainUI.ui @@ -109,7 +109,7 @@ <enum>QFrame::StyledPanel</enum> </property> <property name="currentIndex"> - <number>1</number> + <number>4</number> </property> <widget class="QWidget" name="page_desktop"> <layout class="QVBoxLayout" name="verticalLayout_3"> @@ -602,8 +602,8 @@ <rect> <x>0</x> <y>0</y> - <width>263</width> - <height>178</height> + <width>233</width> + <height>150</height> </rect> </property> <attribute name="label"> @@ -799,8 +799,8 @@ <rect> <x>0</x> <y>0</y> - <width>263</width> - <height>178</height> + <width>233</width> + <height>150</height> </rect> </property> <attribute name="label"> @@ -1634,6 +1634,26 @@ </property> </widget> </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_26"> + <property name="text"> + <string>Time Zone:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="combo_session_timezone"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToMinimumContentsLength</enum> + </property> + </widget> + </item> </layout> </item> <item row="6" column="0" colspan="2"> diff --git a/lumina-desktop/LDesktop.cpp b/lumina-desktop/LDesktop.cpp index 9a045e4f..c2ef4a2a 100644 --- a/lumina-desktop/LDesktop.cpp +++ b/lumina-desktop/LDesktop.cpp @@ -287,6 +287,7 @@ void LDesktop::UpdateDesktop(){ if(DEBUG){qDebug() << " -- New Plugin:" << plugins[i];} plug = NewDP::createPlugin(plugins[i], bgDesktop); if(plug != 0){ + connect(plug, SIGNAL(OpenDesktopMenu()), this, SLOT(ShowMenu()) ); //qDebug() << " -- Show Plugin"; PLUGINS << plug; CreateDesktopPluginContainer(plug); diff --git a/lumina-desktop/LSession.cpp b/lumina-desktop/LSession.cpp index aac550c1..6437524b 100644 --- a/lumina-desktop/LSession.cpp +++ b/lumina-desktop/LSession.cpp @@ -205,6 +205,9 @@ void LSession::launchStartupApps(){ //Now play the login music 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"); } if(sessionsettings->value("EnableNumlock",true).toBool()){ @@ -254,6 +257,11 @@ void LSession::checkUserFiles(){ }*/ LUtils::LoadSystemDefaults(); } + if(oldversion <= 83){ + //Convert the old->new favorites framework + + } + //Check for the default applications file for lumina-open dset = QDir::homePath()+"/.lumina/LuminaDE/lumina-open.conf"; if(!QFile::exists(dset)){ diff --git a/lumina-desktop/LWinInfo.cpp b/lumina-desktop/LWinInfo.cpp index d46bef92..b1476c4e 100644 --- a/lumina-desktop/LWinInfo.cpp +++ b/lumina-desktop/LWinInfo.cpp @@ -15,16 +15,18 @@ QString LWinInfo::text(){ if(window==0){ return ""; } QString nm = LSession::handle()->XCB->WindowVisibleIconName(window); - if(nm.isEmpty()){ nm = LSession::handle()->XCB->WindowIconName(window); } - if(nm.isEmpty()){ nm = LSession::handle()->XCB->WindowVisibleName(window); } - if(nm.isEmpty()){ nm = LSession::handle()->XCB->WindowName(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); } return nm; } QIcon LWinInfo::icon(bool &noicon){ if(window==0){ noicon = true; return QIcon();} noicon = false; - QIcon ico = LX11::WindowIcon(window); + 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;} diff --git a/lumina-desktop/desktop-plugins/LDPlugin.h b/lumina-desktop/desktop-plugins/LDPlugin.h index 0e1f6847..9660a85c 100644 --- a/lumina-desktop/desktop-plugins/LDPlugin.h +++ b/lumina-desktop/desktop-plugins/LDPlugin.h @@ -68,6 +68,10 @@ public slots: //This needs to be re-implemented in the subclassed plugin //This is where all the visuals are set if using Theme-dependant icons. } + +signals: + void OpenDesktopMenu(); + }; #endif
\ No newline at end of file diff --git a/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp b/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp index dc0c7596..b3c6afcf 100644 --- a/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp +++ b/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp @@ -7,18 +7,19 @@ AppLauncherPlugin::AppLauncherPlugin(QWidget* parent, QString ID) : LDPlugin(par lay->setContentsMargins(0,0,0,0); button = new QToolButton(this); button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - button->setIconSize(QSize(64,64)); button->setAutoRaise(true); button->setText("..."); //Need to set something here so that initial sizing works properly + lay->addWidget(button, 0, Qt::AlignCenter); connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); - this->setInitialSize(64,66+this->fontMetrics().height()); - /*if(this->settings->allKeys().isEmpty()){ - //Brand new plugin: set initial size - this->settings->setValue("location/width",64); - this->settings->setValue("location/height",66+this->fontMetrics().height()); - this->settings->sync(); - }*/ + menu = new QMenu(this); + menu->addAction(LXDG::findIcon("zoom-in",""), tr("Increase Size"), this, SLOT(increaseIconSize())); + menu->addAction(LXDG::findIcon("zoom-out",""), tr("Decrease Size"), this, SLOT(decreaseIconSize())); + int icosize = settings->value("iconsize",64).toInt(); + button->setIconSize(QSize(icosize,icosize)); + this->setInitialSize(icosize,icosize+10+this->fontMetrics().height()); + this->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openContextMenu()) ); watcher = new QFileSystemWatcher(this); connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT( loadButton()) ); QTimer::singleShot(1,this, SLOT(loadButton()) ); @@ -62,4 +63,27 @@ void AppLauncherPlugin::buttonClicked(){ LSession::LaunchApplication("lumina-open \""+path+"\""); } -}
\ No newline at end of file +} + +void AppLauncherPlugin::openContextMenu(){ + if(button->underMouse()){ + menu->popup(QCursor::pos()); + }else{ + emit OpenDesktopMenu(); + } +} + +void AppLauncherPlugin::increaseIconSize(){ + int icosize = settings->value("iconsize",64).toInt(); + icosize += 16; + button->setIconSize(QSize(icosize,icosize)); + settings->setValue("iconsize",icosize); +} + +void AppLauncherPlugin::decreaseIconSize(){ + int icosize = settings->value("iconsize",64).toInt(); + if(icosize < 20){ return; } //cannot get smaller + icosize -= 16; + button->setIconSize(QSize(icosize,icosize)); + settings->setValue("iconsize",icosize); +} diff --git a/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h b/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h index bb21b636..2c861e4d 100644 --- a/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h +++ b/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h @@ -16,6 +16,8 @@ #include <QFile> #include <QFileSystemWatcher> #include <QTimer> +#include <QMenu> +#include <QCursor> #include "../LDPlugin.h" @@ -30,9 +32,15 @@ public: private: QToolButton *button; QFileSystemWatcher *watcher; + QMenu *menu; private slots: void loadButton(); void buttonClicked(); + void openContextMenu(); + + void increaseIconSize(); + void decreaseIconSize(); + }; #endif diff --git a/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.cpp b/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.cpp index ce20c563..0aa4a3de 100644 --- a/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.cpp +++ b/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.cpp @@ -2,6 +2,8 @@ #include <QFileInfo> #include <QDir> +#include <QClipboard> +#include <QMimeData> #include <LuminaXDG.h> #include "LSession.h" @@ -17,17 +19,31 @@ DesktopViewPlugin::DesktopViewPlugin(QWidget* parent, QString ID) : LDPlugin(par list->setBatchSize(10); //keep it snappy list->setSpacing(2); list->setSelectionBehavior(QAbstractItemView::SelectItems); - list->setSelectionMode(QAbstractItemView::NoSelection); + list->setSelectionMode(QAbstractItemView::ExtendedSelection); list->setStyleSheet( "QListWidget{ background: transparent; border: none; }" ); - list->setIconSize(QSize(64,64)); + int icosize = settings->value("IconSize",64).toInt(); + list->setIconSize(QSize(icosize,icosize)); list->setUniformItemSizes(true); + list->setContextMenuPolicy(Qt::CustomContextMenu); + + 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()) ); this->layout()->addWidget(list); this->setInitialSize(600,600); watcher = new QFileSystemWatcher(this); watcher->addPath(QDir::homePath()+"/Desktop"); connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(updateContents()) ); - connect(list, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(runItem(QListWidgetItem*)) ); + connect(list, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(runItems()) ); + connect(list, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&)) ); QTimer::singleShot(0,this, SLOT(updateContents()) ); } @@ -35,13 +51,83 @@ DesktopViewPlugin::~DesktopViewPlugin(){ } -void DesktopViewPlugin::runItem(QListWidgetItem *item){ - LSession::LaunchApplication("lumina-open \""+item->whatsThis()+"\""); +void DesktopViewPlugin::runItems(){ + QList<QListWidgetItem*> sel = list->selectedItems(); + for(int i=0; i<sel.length(); i++){ + LSession::LaunchApplication("lumina-open \""+sel[i]->whatsThis()+"\""); + } +} + +void DesktopViewPlugin::copyItems(){ + QList<QListWidgetItem*> sel = list->selectedItems(); + if(sel.isEmpty()){ return; } //nothing selected + QStringList items; + //Format the data string + for(int i=0; i<sel.length(); i++){ + items << "copy::::"+sel[i]->whatsThis(); + } + //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<QListWidgetItem*> sel = list->selectedItems(); + if(sel.isEmpty()){ return; } //nothing selected + QStringList items; + //Format the data string + for(int i=0; i<sel.length(); i++){ + items << "cut::::"+sel[i]->whatsThis(); + } + //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<QListWidgetItem*> sel = list->selectedItems(); + for(int i=0; i<sel.length(); i++){ + 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(pos); + }else{ + //Pass the context menu request on to the desktop (emit it from the plugin) + emit OpenDesktopMenu(); + } +} + +void DesktopViewPlugin::increaseIconSize(){ + int icosize = settings->value("IconSize",64).toInt(); + icosize+=16; //go in orders of 16 pixels + list->setIconSize(QSize(icosize,icosize)); + settings->setValue("IconSize",icosize); + updateContents(); +} + +void DesktopViewPlugin::decreaseIconSize(){ + int icosize = settings->value("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)); + settings->setValue("IconSize",icosize); + updateContents(); } void DesktopViewPlugin::updateContents(){ list->clear(); - list->setGridSize(QSize(80,70+this->fontMetrics().height())); + int icosize = settings->value("IconSize",64).toInt(); + list->setGridSize(QSize(icosize+8,icosize+4+this->fontMetrics().height())); 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; i<files.length(); i++){ @@ -54,15 +140,19 @@ void DesktopViewPlugin::updateContents(){ bool ok = false; XDGDesktop desk = LXDG::loadDesktopFile(files[i].absoluteFilePath(), ok); if(ok){ - it->setIcon( LXDG::findIcon(desk.icon,"") ); - it->setText( desk.name ); + it->setIcon( LXDG::findIcon(desk.icon,"unknown") ); + if(desk.name.isEmpty()){ + it->setText( files[i].fileName() ); + }else{ + it->setText( desk.name ); + } }else{ //Revert back to a standard file handling - it->setIcon( LXDG::findMimeIcon(files[i].suffix()) ); + it->setIcon( LXDG::findMimeIcon(files[i].fileName()) ); it->setText( files[i].fileName() ); } }else{ - it->setIcon( LXDG::findMimeIcon(files[i].suffix()) ); + it->setIcon( LXDG::findMimeIcon( files[i].fileName() ) ); it->setText( files[i].fileName() ); } list->addItem(it); diff --git a/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.h b/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.h index 6ec35276..61e1caf9 100644 --- a/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.h +++ b/lumina-desktop/desktop-plugins/desktopview/DesktopViewPlugin.h @@ -13,6 +13,8 @@ #include <QVBoxLayout> #include <QTimer> #include <QFileSystemWatcher> +#include <QMouseEvent> + #include "../LDPlugin.h" class DesktopViewPlugin : public LDPlugin{ @@ -24,9 +26,16 @@ public: private: QListWidget *list; QFileSystemWatcher *watcher; + QMenu *menu; private slots: - void runItem(QListWidgetItem*); + void runItems(); + void copyItems(); + void cutItems(); + void deleteItems(); + void showMenu(const QPoint&); + void increaseIconSize(); + void decreaseIconSize(); void updateContents(); @@ -37,6 +46,19 @@ public slots: void ThemeChange(){ QTimer::singleShot(0,this, SLOT(updateContents())); } + +/*protected: + void mousePressEvent(QMouseEvent *ev){ + if(ev->button()==Qt::RightButton){ + qDebug() << " - got mouse event"; + //Only show the context menu if an item is under the mouse (don't block the desktop menu) + if(list->itemAt( ev->globalPos()) !=0){ + ev->accept(); + showMenu(ev->globalPos()); + } + } + } + */ }; #endif diff --git a/lumina-desktop/desktop-plugins/notepad/NotepadPlugin.cpp b/lumina-desktop/desktop-plugins/notepad/NotepadPlugin.cpp index 6d4a2b5d..dacaca60 100644 --- a/lumina-desktop/desktop-plugins/notepad/NotepadPlugin.cpp +++ b/lumina-desktop/desktop-plugins/notepad/NotepadPlugin.cpp @@ -200,10 +200,12 @@ void NotePadPlugin::updateContents(){ 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 = settings->value("currentFile","").toString(); QStringList notes; QDir dir(QDir::homePath()+"/Notes"); diff --git a/lumina-desktop/lumina-desktop.pro b/lumina-desktop/lumina-desktop.pro index feb49f60..6c3ee0cc 100644 --- a/lumina-desktop/lumina-desktop.pro +++ b/lumina-desktop/lumina-desktop.pro @@ -1,6 +1,6 @@ QT += core gui network -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets x11extras multimedia dbus +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets x11extras multimedia TARGET = Lumina-DE isEmpty(PREFIX) { @@ -50,6 +50,7 @@ SOURCES += main.cpp \ panel-plugins/systemdashboard/SysMenuQuick.cpp \ panel-plugins/showdesktop/LHomeButton.cpp \ panel-plugins/appmenu/LAppMenuPlugin.cpp \ + panel-plugins/applauncher/AppLaunchButton.cpp \ desktop-plugins/applauncher/AppLauncherPlugin.cpp \ desktop-plugins/desktopview/DesktopViewPlugin.cpp \ desktop-plugins/notepad/NotepadPlugin.cpp \ @@ -89,6 +90,7 @@ HEADERS += Globals.h \ panel-plugins/systemdashboard/SysMenuQuick.h \ panel-plugins/showdesktop/LHomeButton.h \ panel-plugins/appmenu/LAppMenuPlugin.h \ + panel-plugins/applauncher/AppLaunchButton.h \ desktop-plugins/SamplePlugin.h \ desktop-plugins/calendar/CalendarPlugin.h \ desktop-plugins/applauncher/AppLauncherPlugin.h \ diff --git a/lumina-desktop/panel-plugins/NewPP.h b/lumina-desktop/panel-plugins/NewPP.h index 6c5c369c..3a593629 100644 --- a/lumina-desktop/panel-plugins/NewPP.h +++ b/lumina-desktop/panel-plugins/NewPP.h @@ -23,6 +23,7 @@ #include "systemdashboard/LSysDashboard.h" #include "showdesktop/LHomeButton.h" #include "appmenu/LAppMenuPlugin.h" +#include "applauncher/AppLaunchButton.h" #include "systemtray/LSysTray.h" //must be last due to X11 compile issues class NewPP{ @@ -52,6 +53,8 @@ public: plug = new LSysDashboard(parent, plugin, horizontal); }else if(plugin.startsWith("appmenu---")){ plug = new LAppMenuPlugin(parent, plugin, horizontal); + }else if(plugin.section("---",0,0).section("::",0,0)=="applauncher"){ + plug = new AppLaunchButtonPlugin(parent, plugin, horizontal); }else{ qWarning() << "Invalid Panel Plugin:"<<plugin << " -- Ignored"; } diff --git a/lumina-desktop/panel-plugins/applauncher/AppLaunchButton.cpp b/lumina-desktop/panel-plugins/applauncher/AppLaunchButton.cpp new file mode 100644 index 00000000..5bd7fa96 --- /dev/null +++ b/lumina-desktop/panel-plugins/applauncher/AppLaunchButton.cpp @@ -0,0 +1,73 @@ +//=========================================== +// 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 <LuminaXDG.h> + +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.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")){ + bool ok = false; + XDGDesktop desk = LXDG::loadDesktopFile(appfile,ok); + if(ok){ + 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<XDGDesktop> apps = LSession::handle()->applicationMenu()->currentAppHash()->value("All"); + QStringList names; + for(int i=0; i<apps.length(); i++){ names << apps[i].name; } + 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---<something>" -> "applauncher::fixed---<something>" ? + QTimer::singleShot(0,this, SLOT(updateButtonVisuals())); + }else{ + LSession::LaunchApplication("lumina-open \""+appfile+"\""); + } +} + diff --git a/lumina-desktop/panel-plugins/applauncher/AppLaunchButton.h b/lumina-desktop/panel-plugins/applauncher/AppLaunchButton.h new file mode 100644 index 00000000..3aa3c7ad --- /dev/null +++ b/lumina-desktop/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 <QToolButton> +#include <QString> +#include <QWidget> + + +// 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/lumina-desktop/panel-plugins/clock/LClock.cpp b/lumina-desktop/panel-plugins/clock/LClock.cpp index e91a3a71..79cae84b 100644 --- a/lumina-desktop/panel-plugins/clock/LClock.cpp +++ b/lumina-desktop/panel-plugins/clock/LClock.cpp @@ -35,6 +35,7 @@ LClock::~LClock(){ void LClock::updateTime(){ QDateTime CT = QDateTime::currentDateTime(); + if(useTZ){ CT = CT.toTimeZone(TZ); } //Now update the display QString label; if(deftime){ label = CT.time().toString(Qt::SystemLocaleShortDate) ; } @@ -53,5 +54,8 @@ void LClock::updateFormats(){ datefmt = LSession::handle()->sessionSettings()->value("DateFormat","").toString(); deftime = timefmt.simplified().isEmpty(); defdate = datefmt.simplified().isEmpty(); + useTZ = LSession::handle()->sessionSettings()->value("CustomTimeZone",false).toBool(); + if(useTZ){ TZ = QTimeZone( LSession::handle()->sessionSettings()->value("TimeZoneByteCode", QByteArray()).toByteArray() ); } + } diff --git a/lumina-desktop/panel-plugins/clock/LClock.h b/lumina-desktop/panel-plugins/clock/LClock.h index 1e4c8294..8156e7d8 100644 --- a/lumina-desktop/panel-plugins/clock/LClock.h +++ b/lumina-desktop/panel-plugins/clock/LClock.h @@ -13,6 +13,7 @@ #include <QWidget> #include <QString> #include <QLocale> +#include <QTimeZone> #include "../LPPlugin.h" @@ -26,7 +27,8 @@ private: QTimer *timer; QLabel *labelWidget; QString timefmt, datefmt; - bool deftime, defdate; + bool deftime, defdate, useTZ; + QTimeZone TZ; private slots: void updateTime(); diff --git a/lumina-desktop/panel-plugins/systemtray/TrayIcon.cpp b/lumina-desktop/panel-plugins/systemtray/TrayIcon.cpp index 7e0a30f6..e966f389 100644 --- a/lumina-desktop/panel-plugins/systemtray/TrayIcon.cpp +++ b/lumina-desktop/panel-plugins/systemtray/TrayIcon.cpp @@ -12,6 +12,10 @@ //#include <xcb/damage.h> //static xcb_damage_damage_t dmgID; + +#include <LSession.h> +#include <QScreen> + static int dmgID = 0; TrayIcon::TrayIcon(QWidget *parent) : QWidget(parent){ @@ -115,17 +119,23 @@ void TrayIcon::paintEvent(QPaintEvent *event){ //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 = LX11::WindowImage(AID, false); + QPixmap pix = LSession::handle()->XCB->WindowImage(AID); if(pix.isNull()){ //Try to grab the window directly with Qt qDebug() << " - - Grab window directly"; - pix = QPixmap::grabWindow(AID); + QList<QScreen*> scrnlist = QApplication::screens(); + for(int i=0; i<scrnlist.length(); i++){ + pix = scrnlist[i]->grabWindow(AID); + break; //stop here + } } //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())); qDebug() << "-- Icon size mismatch"; } painter.drawPixmap(0,0,this->width(), this->height(), pix.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation) ); + }else{ + qDebug() << " - - No Tray Icon/Image found!" << "ID:" << AID; } //qDebug() << " - Done"; } diff --git a/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp b/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp index 7c24dc3d..20607c60 100644 --- a/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp +++ b/lumina-desktop/panel-plugins/taskmanager/LTaskButton.cpp @@ -121,13 +121,14 @@ void LTaskButton::UpdateButton(){ //single window this->setPopupMode(QToolButton::DelayedPopup); this->setMenu(actMenu); - if(showText){ this->setText( this->fontMetrics().elidedText(WINLIST[0].text(), Qt::ElideRight,80) ); } - else if(noicon){ this->setText( this->fontMetrics().elidedText(cname, Qt::ElideRight ,80) ); } - else{ this->setText(""); } + if(showText){ this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); this->setText( this->fontMetrics().elidedText(WINLIST[0].text(), Qt::ElideRight,80) ); } + else if(noicon){ this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); this->setText( this->fontMetrics().elidedText(cname, Qt::ElideRight ,80) ); } + else{ this->setToolButtonStyle(Qt::ToolButtonIconOnly); this->setText(""); } }else if(WINLIST.length() > 1){ //multiple windows this->setPopupMode(QToolButton::InstantPopup); this->setMenu(winMenu); + this->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); if(noicon || showText){ this->setText( this->fontMetrics().elidedText(cname, Qt::ElideRight ,80) +" ("+QString::number(WINLIST.length())+")" ); } else{ this->setText("("+QString::number(WINLIST.length())+")"); } } diff --git a/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp b/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp index 3b8be74f..a04c6e43 100644 --- a/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp +++ b/lumina-desktop/panel-plugins/userbutton/UserItemWidget.cpp @@ -30,7 +30,7 @@ UserItemWidget::UserItemWidget(QWidget *parent, QString itemPath, bool isDir, bo } }else{ if(itemPath.endsWith("/")){ itemPath.chop(1); } - icon->setPixmap( LXDG::findMimeIcon(itemPath.section("/",-1).section(".",-1)).pixmap(32,32) ); + icon->setPixmap( LXDG::findMimeIcon(itemPath.section("/",-1)).pixmap(32,32) ); name->setText( this->fontMetrics().elidedText(itemPath.section("/",-1), Qt::ElideRight, 180) ); } linkPath = QFile::symLinkTarget(itemPath); @@ -101,7 +101,7 @@ void UserItemWidget::setupButton(bool disable){ }else if( !QFile::exists( QDir::homePath()+"/Desktop/"+icon->whatsThis().section("/",-1) ) && !QFile::exists( QDir::homePath()+"/.lumina/favorites/"+icon->whatsThis().section("/",-1) ) ){ //This file does not have a desktop shortcut yet -- allow the user to add it button->setWhatsThis("add"); - button->setIcon( LXDG::findIcon("favorites","") ); + button->setIcon( LXDG::findIcon("bookmark-toolbar","") ); button->setToolTip(tr("Create Shortcut")); connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); }else{ diff --git a/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp b/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp index 6ca3ba19..52d60714 100644 --- a/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp +++ b/lumina-desktop/panel-plugins/userbutton/UserWidget.cpp @@ -16,7 +16,7 @@ UserWidget::UserWidget(QWidget* parent) : QTabWidget(parent), ui(new Ui::UserWid sysapps = LSession::handle()->applicationMenu()->currentAppHash(); //get the raw info //Setup the Icons // - favorites tab - this->setTabIcon(0, rotateIcon(LXDG::findIcon("favorites","")) ); + this->setTabIcon(0, rotateIcon(LXDG::findIcon("folder-favorites","")) ); this->setTabText(0,""); // - apps tab this->setTabIcon(1, rotateIcon(LXDG::findIcon("system-run","")) ); @@ -194,6 +194,16 @@ void UserWidget::updateFavItems(){ connect(it, SIGNAL(RemovedShortcut()), this, SLOT(updateFavItems()) ); } static_cast<QBoxLayout*>(ui->scroll_fav->widget()->layout())->addStretch(); + + //Clean up any broken sym-links in the favorites directory + /*items = favdir.entryInfoList(QDir::System | QDir::NoDotAndDotDot, QDir::Name); + for(int i=0; i<items.length(); i++){ + if(items[i].isSymLink() && !items[i].exists()){ + //Broken sym-link - remove it + QFile::remove(items[i].absoluteFilePath()); + } + }*/ + } //Apps Tab diff --git a/lumina-fm/BackgroundWorker.cpp b/lumina-fm/BackgroundWorker.cpp index cdb0644d..1438f71e 100644 --- a/lumina-fm/BackgroundWorker.cpp +++ b/lumina-fm/BackgroundWorker.cpp @@ -83,6 +83,6 @@ void BackgroundWorker::startDirChecks(QString path){ snapDirs[i] = dir.absolutePath()+"/"+snapDirs[i].section("::::",1,50)+"/"+reldir; } if(!snapDirs.isEmpty()){ emit SnapshotsAvailable(baseSnapDir, snapDirs); } - qDebug() << "Found snapshots:" << snapDirs; + qDebug() << "Found snapshots"; } }
\ No newline at end of file diff --git a/lumina-fm/MainUI.cpp b/lumina-fm/MainUI.cpp index fe3240dc..fa892dfd 100644 --- a/lumina-fm/MainUI.cpp +++ b/lumina-fm/MainUI.cpp @@ -173,7 +173,7 @@ void MainUI::setupIcons(){ ui->tool_act_paste->setIcon( LXDG::findIcon("edit-paste","") ); ui->tool_act_rename->setIcon( LXDG::findIcon("edit-rename","") ); ui->tool_act_rm->setIcon( LXDG::findIcon("edit-delete","") ); - ui->tool_act_fav->setIcon( LXDG::findIcon("quickopen","") ); + ui->tool_act_fav->setIcon( LXDG::findIcon("bookmark-toolbar","") ); //Multimedia Player page ui->tool_player_next->setIcon( LXDG::findIcon("media-skip-forward","") ); @@ -938,6 +938,7 @@ void MainUI::OpenContextMenu(const QPoint &pt){ contextMenu->addAction(LXDG::findIcon("run-build-configure",""), tr("Open With..."), this, SLOT(OpenItemWith()) ); } contextMenu->addAction(LXDG::findIcon("edit-rename",""), tr("Rename"), this, SLOT(RenameItem()) )->setEnabled(info.isWritable()); + contextMenu->addAction(LXDG::findIcon("document-encrypted",""), tr("View Checksums"), this, SLOT(ChecksumItems()) ); contextMenu->addSeparator(); } bool hasSelection = !getSelectedItems().isEmpty(); @@ -972,7 +973,17 @@ void MainUI::ItemSelectionChanged(){ } QString itname; if(sel.length()==1){ itname = sel[0].fileName(); } - ui->tool_act_fav->setEnabled(!itname.isEmpty() && !QFile::exists(favdir+itname) ); + bool ok = !itname.isEmpty() && (getCurrentDir()!=QDir::homePath()+"/Desktop"); + if(ok){ + if(QFile::exists(favdir+itname)){ + //Make sure this favorite does not already point to the current file + QFileInfo info(favdir+itname); + if(info.isSymLink() && info.exists()){ + ok = false; //still an active favorite - do not allow replacement + } + } + } + ui->tool_act_fav->setEnabled(ok); } //------------------------------- @@ -1026,6 +1037,10 @@ void MainUI::removePicture(){ QString file = getCurrentDir(); if(!file.endsWith("/")){ file.append("/"); } file.append(ui->combo_image_name->currentText()); + //Verify permanent removal of file/dir + if(QMessageBox::Yes != QMessageBox::question(this, tr("Verify Removal"), tr("WARNING: This will permanently delete the file from the system!")+"\n"+tr("Are you sure you want to continue?")+"\n\n"+file, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){ + return; //cancelled + } if( QFile::remove(file) ){ int index = ui->combo_image_name->currentIndex(); ui->combo_image_name->removeItem( index ); @@ -1286,17 +1301,17 @@ void MainUI::RemoveItem(){ if(!checkUserPerms()){ return; } //Get the selected items QStringList paths, names; - if(CItem.isEmpty()){ + //if(CItem.isEmpty()){ QFileInfoList sel = getSelectedItems(); for(int i=0; i<sel.length(); i++){ paths << sel[i].absoluteFilePath(); names << sel[i].fileName(); } if(sel.isEmpty()){ return; } //nothing selected - }else{ + /*}else{ paths << CItem; names << CItem.section("/",-1); - } + }*/ //Verify permanent removal of file/dir if(QMessageBox::Yes != QMessageBox::question(this, tr("Verify Removal"), tr("WARNING: This will permanently delete the file(s) from the system!")+"\n"+tr("Are you sure you want to continue?")+"\n\n"+names.join("\n"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){ return; //cancelled @@ -1364,6 +1379,7 @@ void MainUI::FavoriteItem(){ QString fname = CItem; QString fullpath = fname; fname = fname.section("/",-1); //turn this into just the file name + if(QFile::exists(favdir+fname)){ QFile::remove(favdir+fname); } //remove the stale link QFile::link(fullpath, favdir+fname); CItem.clear(); ItemSelectionChanged(); @@ -1463,8 +1479,30 @@ void MainUI::PasteItems(){ QApplication::clipboard()->setMimeData(dat); } } - ItemSelectionChanged(); - + ItemSelectionChanged(); +} + +void MainUI::ChecksumItems(){ + if(ui->stackedWidget->currentWidget()!=ui->page_browser){ return; } + QFileInfoList sel = getSelectedItems(); + if(sel.isEmpty()){ return; } + QStringList info, files; + for(int i=0; i<sel.length(); i++){ + files << sel[i].absoluteFilePath(); + } + qDebug() << "Run Checksums:" << files; + info = LOS::Checksums(files); + qDebug() << " - Info:" << info; + if(info.isEmpty() || (info.length() != files.length()) ){ return; } + for(int i=0; i<info.length(); i++){ + info[i] = QString("%2 \t(%1)").arg(files[i].section("/",-1), info[i]); + } + /*QMessageBox dlg(this); + dlg.setWindowFlags( Qt::Dialog ); + dlg.setWindowTitle( tr("File Checksums") ); + dlg.setInformativeText(info.join("\n")); + dlg.exec();*/ + QMessageBox::information(this, tr("File Checksums"), info.join("\n") ); } void MainUI::resizeEvent(QResizeEvent *event){ diff --git a/lumina-fm/MainUI.h b/lumina-fm/MainUI.h index 40ef25ff..97cadf86 100644 --- a/lumina-fm/MainUI.h +++ b/lumina-fm/MainUI.h @@ -89,7 +89,6 @@ private: //Phonon Widgets for the multimedia player QMediaPlayer *mediaObj; QVideoWidget *videoDisplay; - //Phonon::AudioOutput *audioOut; QSlider *playerSlider; QString playerTTime; //total time - to prevent recalculation every tick @@ -219,6 +218,7 @@ private slots: void CutItems(); void CopyItems(); void PasteItems(); + void ChecksumItems(); signals: void DirChanged(QString path); diff --git a/lumina-fm/MimeIconProvider.h b/lumina-fm/MimeIconProvider.h index 344d6801..0c9ba98e 100644 --- a/lumina-fm/MimeIconProvider.h +++ b/lumina-fm/MimeIconProvider.h @@ -34,8 +34,16 @@ public: if(showthumbnails && (info.suffix().toLower()=="png" || info.suffix().toLower()=="jpg") ){ //make sure to only load small versions of the files into memory: could have hundreds of them... return QIcon( QPixmap(info.absoluteFilePath()).scaledToHeight(64) ); + }else if(info.fileName().endsWith(".desktop") ){ + bool ok = false; + XDGDesktop desk = LXDG::loadDesktopFile(info.absoluteFilePath(), ok); + if(ok){ + return LXDG::findIcon(desk.icon, "unknown"); + }else{ + return LXDG::findMimeIcon(info.fileName()); + } }else{ - return LXDG::findMimeIcon(info.suffix()); + return LXDG::findMimeIcon(info.fileName()); } }else{ return LXDG::findIcon("unknown",""); diff --git a/lumina-open/LFileDialog.cpp b/lumina-open/LFileDialog.cpp index d648925b..361cd99f 100644 --- a/lumina-open/LFileDialog.cpp +++ b/lumina-open/LFileDialog.cpp @@ -239,7 +239,7 @@ void LFileDialog::on_tool_ok_clicked(){ bool ok = false; XDGDesktop app = LXDG::loadDesktopFile(PREFAPPS[ui->combo_rec->currentIndex()], ok); //Set the output variables - appExec = app.exec; + appExec = LXDG::getDesktopExec(app); appPath = app.path; appFile = app.filePath; setPreferredApplication(app.filePath); //bump this to the top of the preferred list for next time @@ -248,7 +248,7 @@ void LFileDialog::on_tool_ok_clicked(){ bool ok = false; XDGDesktop app = LXDG::loadDesktopFile(ui->tree_apps->currentItem()->whatsThis(0), ok); //Set the output variables - appExec = app.exec; + appExec = LXDG::getDesktopExec(app); appPath = app.path; appFile = app.filePath; setPreferredApplication(app.filePath); //save this app to this extension as a recommendation diff --git a/lumina-open/main.cpp b/lumina-open/main.cpp index 4dab7ed0..8b79682d 100644 --- a/lumina-open/main.cpp +++ b/lumina-open/main.cpp @@ -43,6 +43,16 @@ void printUsageInfo(){ exit(1); } +void ShowErrorDialog(int argc, char **argv, QString message){ + //Setup the application + QApplication App(argc, argv); + LuminaThemeEngine theme(&App); + LUtils::LoadTranslation(&App,"lumina-open"); + QMessageBox dlg(QMessageBox::Critical, QObject::tr("File Error"), message ); + dlg.exec(); + exit(1); +} + void showOSD(int argc, char **argv, QString message){ //Setup the application QApplication App(argc, argv); @@ -187,7 +197,7 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat if(QFile::exists(inFile)){ isFile=true; } else if(QFile::exists(QDir::currentPath()+"/"+inFile)){isFile=true; inFile = QDir::currentPath()+"/"+inFile;} //account for relative paths else if(QUrl(inFile).isValid() && !inFile.startsWith("/") ){ isUrl=true; } - if( !isFile && !isUrl ){ qDebug() << "Error: Invalid file or URL"; return;} + if( !isFile && !isUrl ){ ShowErrorDialog( argc, argv, QString(QObject::tr("Invalid file or URL: %1")).arg(inFile) ); } //Determing the type of file (extension) QString extension; //qDebug() << "File Type:" << isFile << isUrl; @@ -209,8 +219,7 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat bool ok = false; XDGDesktop DF = LXDG::loadDesktopFile(inFile, ok); if(!ok){ - qDebug() << "[ERROR] Input *.desktop file could not be read:" << inFile; - exit(1); + ShowErrorDialog( argc, argv, QString(QObject::tr("File could not be opened: %1")).arg(inFile) ); } switch(DF.type){ case XDGDesktop::APP: @@ -218,8 +227,7 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat cmd = LXDG::getDesktopExec(DF); if(!DF.path.isEmpty()){ path = DF.path; } }else{ - qDebug() << "[ERROR] Input *.desktop application file is missing the Exec line:" << inFile; - exit(1); + ShowErrorDialog( argc, argv, QString(QObject::tr("Application shortcut is missing the launching information (malformed shortcut): %1")).arg(inFile) ); } break; case XDGDesktop::LINK: @@ -229,8 +237,7 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat cmd.clear(); extension = inFile.section(":",0,0); }else{ - qDebug() << "[ERROR] Input *.desktop link file is missing the URL line:" << inFile; - exit(1); + ShowErrorDialog( argc, argv, QString(QObject::tr("URL shortcut is missing the URL: %1")).arg(inFile) ); } break; case XDGDesktop::DIR: @@ -240,13 +247,11 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat cmd.clear(); extension = "directory"; }else{ - qDebug() << "[ERROR] Input *.desktop directory file is missing the Path line:" << inFile; - exit(1); + ShowErrorDialog( argc, argv, QString(QObject::tr("Directory shortcut is missing the path to the directory: %1")).arg(inFile) ); } break; default: - qDebug() << "[ERROR] Unknown *.desktop file type:" << inFile; - exit(1); + ShowErrorDialog( argc, argv, QString(QObject::tr("Unknown type of shortcut : %1")).arg(inFile) ); } } if(cmd.isEmpty()){ |