From 33681a12eb754af6f057e8a6984db4af18dc010b Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Thu, 2 Mar 2017 09:43:19 -0500 Subject: Clean up some of the applauncher context menu functionality: 1) Add the ability for plugins to set their own high-priority context menu, and put the plugin modification menu into that as needed. 2) For the applauncher plugin, generate a custom context menu specifically for the file in question. This may include the various "actions" in .desktop files as appropriate, and also adds shortcuts for launch, open, open-with, file properties, and delete file. --- src-qt5/core/libLumina/LuminaXDG.cpp | 4 +- src-qt5/core/libLumina/RootSubWindow.cpp | 6 +-- .../lumina-desktop/desktop-plugins/LDPlugin.cpp | 32 ++++++++------ .../core/lumina-desktop/desktop-plugins/LDPlugin.h | 11 ++--- .../applauncher/AppLauncherPlugin.cpp | 51 +++++++++++++++++++++- .../applauncher/AppLauncherPlugin.h | 9 +++- src-qt5/core/lumina-open/main.cpp | 6 ++- 7 files changed, 91 insertions(+), 28 deletions(-) diff --git a/src-qt5/core/libLumina/LuminaXDG.cpp b/src-qt5/core/libLumina/LuminaXDG.cpp index cbc90f25..f34bc0e8 100644 --- a/src-qt5/core/libLumina/LuminaXDG.cpp +++ b/src-qt5/core/libLumina/LuminaXDG.cpp @@ -59,7 +59,7 @@ void XDGDesktop::sync(){ if(!CDA.ID.isEmpty()){ actions << CDA; CDA = XDGDesktopAction(); } }else if(line.startsWith("[")){ insection=false; inaction = false; } //Now check if this is the beginning of a section - if(line=="[Desktop Entry]"){ insection=true; continue; } + if(line=="[Desktop Entry]"){ insection=true; continue; } else if(line.startsWith("[Desktop Action ")){ //Grab the ID of the action out of the label CDA.ID = line.section("]",0,0).section("Desktop Action",1,1).simplified(); @@ -131,6 +131,8 @@ void XDGDesktop::sync(){ else{ type = XDGDesktop::BAD; } //Unknown type } } //end reading file + if(!CDA.ID.isEmpty()){ actions << CDA; CDA = XDGDesktopAction(); } //if an action was still being read, add that to the list now + file.clear(); //done with contents of file //If there are OnlyShowIn desktops listed, add them to the name if( !showInList.isEmpty() && !showInList.contains("Lumina", Qt::CaseInsensitive) ){ diff --git a/src-qt5/core/libLumina/RootSubWindow.cpp b/src-qt5/core/libLumina/RootSubWindow.cpp index 7be89f48..e4c3c4ee 100644 --- a/src-qt5/core/libLumina/RootSubWindow.cpp +++ b/src-qt5/core/libLumina/RootSubWindow.cpp @@ -15,7 +15,7 @@ RootSubWindow::RootSubWindow(QMdiArea *root, NativeWindow *win) : QMdiSubWindow( closing = false; WinWidget = QWidget::createWindowContainer( WIN->window(), this); this->setWidget(WinWidget); - LoadProperties( QList< NativeWindow::Property>() << NativeWindow::WindowFlags << NativeWindow::Title << NativeWindow::Icon \ + LoadProperties( QList< NativeWindow::Property>() << NativeWindow::Title << NativeWindow::Icon \ << NativeWindow::MinSize << NativeWindow::MaxSize << NativeWindow::Size ); //Hookup the signals/slots connect(this, SIGNAL(aboutToActivate()), this, SLOT(aboutToActivate()) ); @@ -89,9 +89,9 @@ void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ case NativeWindow::Active: if(val.toBool()){ this->mdiArea()->setActiveSubWindow(this); } break; - case NativeWindow::WindowFlags: + /*case NativeWindow::WindowFlags: this->setWindowFlags( val.value< Qt::WindowFlags >() ); - break; + break;*/ default: qDebug() << "Window Property Unused:" << prop << val; } diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.cpp b/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.cpp index 545ba430..efae7b24 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.cpp +++ b/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.cpp @@ -16,6 +16,7 @@ LDPlugin::LDPlugin(QWidget *parent, QString id) : QFrame(parent){ settings = LSession::handle()->DesktopPluginSettings(); //Setup the plugin system control menu menu = new QMenu(this); + contextM = 0; setupMenu(); //Setup the internal timer for when to start/stop drag events dragTimer = new QTimer(this); @@ -33,11 +34,13 @@ LDPlugin::LDPlugin(QWidget *parent, QString id) : QFrame(parent){ void LDPlugin::setupMenu(){ menu->clear(); + menu->setTitle(tr("Modify Item")); + menu->setIcon(LXDG::findIcon("preferences-desktop-icons","") ); //SPECIAL CONTEXT MENU OPTIONS FOR PARTICULAR PLUGIN TYPES - if(PLUGID.startsWith("applauncher::")){ + /*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()) ); @@ -48,16 +51,17 @@ void LDPlugin::setupMenu(){ 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(); +void LDPlugin::showPluginMenu(){ + emit CloseDesktopMenu(); + //Double check which menu should be shown + if(this->contextMenu()!=0){ + //Got a special context menu for this plugin - need to layer them together + if(!this->contextMenu()->actions().contains(menu->menuAction())){ + this->contextMenu()->addSeparator(); + this->contextMenu()->addMenu(menu); } - //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()); -}*/ + this->contextMenu()->popup( QCursor::pos() ); + }else{ + menu->popup( QCursor::pos() ); + } +} diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.h b/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.h index 820880ed..a200ab90 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.h +++ b/src-qt5/core/lumina-desktop/desktop-plugins/LDPlugin.h @@ -33,7 +33,7 @@ class LDPlugin : public QFrame{ private: QString PLUGID, prefix; QSettings *settings; - QMenu *menu; + QMenu *menu, *contextM; QTimer *dragTimer; void setupMenu(); @@ -46,6 +46,10 @@ public: QString ID(){ return PLUGID; } + + void setContextMenu(QMenu *cmen){ contextM = cmen; } + + QMenu* contextMenu(){ return contextM; } virtual QSize defaultPluginSize(){ //This needs to be re-implemented in the subclassed plugin @@ -97,10 +101,7 @@ public slots: //This is where all the visuals are set if using Theme-dependant icons. setupMenu(); } - void showPluginMenu(){ - emit CloseDesktopMenu(); - menu->popup( QCursor::pos() ); - } + void showPluginMenu(); signals: void OpenDesktopMenu(); diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp b/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp index 14599c5d..8d8106f7 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp +++ b/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.cpp @@ -20,6 +20,9 @@ AppLauncherPlugin::AppLauncherPlugin(QWidget* parent, QString ID) : LDPlugin(par 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. + this->setContextMenu( new QMenu(this) ); + connect(this->contextMenu(), SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*)) ); + loadButton(); //QTimer::singleShot(0,this, SLOT(loadButton()) ); } @@ -32,6 +35,7 @@ void AppLauncherPlugin::Cleanup(){ 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 + this->contextMenu()->clear(); //qDebug() << "Default Application Launcher:" << def << path; bool ok = QFile::exists(path); if(!ok){ emit RemovePlugin(this->ID()); return;} @@ -53,6 +57,16 @@ void AppLauncherPlugin::loadButton(){ button->setIcon( QIcon(LXDG::findIcon(file.icon,"system-run").pixmap(QSize(icosize,icosize)).scaledToHeight(icosize, Qt::SmoothTransformation) ) ); if(!file.comment.isEmpty()){button->setToolTip(file.comment); } txt = file.name; + //Put the simple Open action first (no open-with for .desktop files) + this->contextMenu()->addAction(button->icon(), QString(tr("Launch %1")).arg(file.name), this, SLOT(buttonClicked()) ); + //See if there are any "actions" listed for this file, and put them in the context menu as needed. + if(!file.actions.isEmpty()){ + for(int i=0; icontextMenu()->addAction( file.actions[i].name ); + tmp->setIcon( LXDG::findIcon(file.actions[i].icon,"quickopen-file") ); + tmp->setWhatsThis( file.actions[i].ID ); + } + } if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); } watcher->addPath(file.filePath); //make sure to update this shortcut if the file changes } @@ -82,6 +96,16 @@ void AppLauncherPlugin::loadButton(){ button->setText( tr("Click to Set") ); if(!watcher->files().isEmpty()){ watcher->removePaths(watcher->files()); } } + //Now adjust the context menu for the button as needed + if(this->contextMenu()->isEmpty()){ + this->contextMenu()->addAction(LXDG::findIcon("document-open",""), tr("Open"), this, SLOT(buttonClicked()) ); + this->contextMenu()->addAction(LXDG::findIcon("document-preview",""), tr("Open With"), this, SLOT(openWith()) ); + } + this->contextMenu()->addAction(LXDG::findIcon("document-properties",""), tr("Properties"), this, SLOT(fileProperties()) ); + if(QFileInfo(path).isWritable()){ + this->contextMenu()->addSeparator(); + this->contextMenu()->addAction(LXDG::findIcon("document-close",""), tr("Delete File"), this, SLOT(fileDelete()) ); + } //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(); @@ -132,7 +156,7 @@ void AppLauncherPlugin::loadButton(){ QTimer::singleShot(100, this, SLOT(update()) ); //Make sure to re-draw the image in a moment } -void AppLauncherPlugin::buttonClicked(){ +void AppLauncherPlugin::buttonClicked(bool openwith){ QString path = button->whatsThis(); if(path.isEmpty() || !QFile::exists(path) ){ //prompt for the user to select an application @@ -144,8 +168,33 @@ void AppLauncherPlugin::buttonClicked(){ if(!ok || names.indexOf(app)<0){ return; } //cancelled this->saveSetting("applicationpath", apps[ names.indexOf(app) ]->filePath); QTimer::singleShot(0,this, SLOT(loadButton())); + }else if(openwith){ + LSession::LaunchApplication("lumina-open -select \""+path+"\""); }else{ LSession::LaunchApplication("lumina-open \""+path+"\""); } } + +void AppLauncherPlugin::actionTriggered(QAction *act){ + if(act->whatsThis().isEmpty()){ return; } + QString path = button->whatsThis(); + if(path.isEmpty() || !QFile::exists(path)){ return; } //invalid file + LSession::LaunchApplication("lumina-open -action \""+act->whatsThis()+"\" \""+path+"\""); +} + +void AppLauncherPlugin::openWith(){ + buttonClicked(true); +} + +void AppLauncherPlugin::fileProperties(){ + QString path = button->whatsThis(); + if(path.isEmpty() || !QFile::exists(path)){ return; } //invalid file + LSession::LaunchApplication("lumina-fileinfo \""+path+"\""); +} + +void AppLauncherPlugin::fileDelete(){ + QString path = button->whatsThis(); + if(path.isEmpty() || !QFile::exists(path)){ return; } //invalid file + QFile::remove(path); +} diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h b/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h index a0f6a7cd..95fc9284 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h +++ b/src-qt5/core/lumina-desktop/desktop-plugins/applauncher/AppLauncherPlugin.h @@ -1,6 +1,6 @@ //=========================================== // Lumina-DE source code -// Copyright (c) 2014, Ken Moore +// Copyright (c) 2014-2017, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== @@ -38,13 +38,18 @@ private: private slots: void loadButton(); - void buttonClicked(); + void buttonClicked(bool openwith = false); //void openContextMenu(); //void increaseIconSize(); //void decreaseIconSize(); //void deleteFile(); + void actionTriggered(QAction *act); + void openWith(); + void fileProperties(); + void fileDelete(); + public slots: void LocaleChange(){ loadButton(); //force reload diff --git a/src-qt5/core/lumina-open/main.cpp b/src-qt5/core/lumina-open/main.cpp index 59cb05d9..7434b6c2 100644 --- a/src-qt5/core/lumina-open/main.cpp +++ b/src-qt5/core/lumina-open/main.cpp @@ -32,7 +32,7 @@ void printUsageInfo(){ qDebug() << "lumina-open: Application launcher for the Lumina Desktop Environment"; qDebug() << "Description: Given a file (with absolute path) or URL, this utility will try to find the appropriate application with which to open the file. If the file is a *.desktop application shortcut, it will just start the application appropriately. It can also perform a few specific system operations if given special flags."; - qDebug() << "Usage: lumina-open [-select] [-action ]"; + qDebug() << "Usage: lumina-open [-select] [-action ] "; qDebug() << " lumina-open [-volumeup, -volumedown, -brightnessup, -brightnessdown]"; qDebug() << " [-select] (optional) flag to bypass any default application settings and show the application selector window"; qDebug() << " [-action ] (optional) Flag to run one of the alternate Actions listed in a .desktop registration file rather than the main command."; @@ -266,8 +266,10 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat } switch(DF.type){ case XDGDesktop::APP: + qDebug() << "Found .desktop application:" << ActionID; if(!DF.exec.isEmpty()){ cmd = DF.getDesktopExec(ActionID); + qDebug() << "Got command:" << cmd; if(!DF.path.isEmpty()){ path = DF.path; } watch = DF.startupNotify || !DF.filePath.contains("/xdg/autostart/"); }else{ @@ -340,7 +342,7 @@ void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& pat cmd.append(" \""+inFile+"\""); } } - //qDebug() << "Found Command:" << cmd << "Extension:" << extension; + qDebug() << "Found Command:" << cmd << "Extension:" << extension; //Clean up any leftover "Exec" field codes (should have already been replaced earlier) if(cmd.contains("%")){cmd = cmd.remove("%U").remove("%u").remove("%F").remove("%f").remove("%i").remove("%c").remove("%k").simplified(); } binary = cmd; //pass this string to the calling function -- cgit