aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/core/lumina-open/main.cpp
diff options
context:
space:
mode:
authorKen Moore <moorekou@gmail.com>2016-04-25 13:08:12 -0400
committerKen Moore <moorekou@gmail.com>2016-04-25 13:08:12 -0400
commited5ecf7ea7a482b4649e66ecb35fbc60af680684 (patch)
treeacc0fa17d228259e847f55c678db9fb0a9b50f0c /src-qt5/core/lumina-open/main.cpp
parentMerge branch 'master' of github.com:pcbsd/lumina (diff)
downloadlumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.tar.gz
lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.tar.bz2
lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.zip
Rearrange the Lumina source tree quite a bit:
Now the utilites are arranged by category (core, core-utils, desktop-utils), so all the -utils may be excluded by a package system (or turned into separate packages) as needed.
Diffstat (limited to 'src-qt5/core/lumina-open/main.cpp')
-rw-r--r--src-qt5/core/lumina-open/main.cpp400
1 files changed, 400 insertions, 0 deletions
diff --git a/src-qt5/core/lumina-open/main.cpp b/src-qt5/core/lumina-open/main.cpp
new file mode 100644
index 00000000..a323e075
--- /dev/null
+++ b/src-qt5/core/lumina-open/main.cpp
@@ -0,0 +1,400 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2012, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+
+#include <QApplication>
+#include <QX11Info>
+#include <QProcess>
+#include <QProcessEnvironment>
+#include <QFile>
+#include <QFileInfo>
+#include <QString>
+#include <QUrl>
+#include <QDebug>
+#include <QTranslator>
+#include <QMessageBox>
+#include <QLabel>
+#include <QDateTime>
+#include <QPixmap>
+#include <QColor>
+#include <QDesktopWidget>
+
+#include "LFileDialog.h"
+
+#include <LuminaXDG.h>
+#include <LuminaUtils.h>
+#include <LuminaOS.h>
+#include <LuminaThemes.h>
+
+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] <absolute file path or URL>";
+ qDebug() << " lumina-open [-volumeup, -volumedown, -brightnessup, -brightnessdown]";
+ qDebug() << " [-select] (optional) flag to bypass any default application settings and show the application selector window";
+ qDebug() << "Special Flags:";
+ qDebug() << " \"-volume[up/down]\" Flag to increase/decrease audio volume by 5%";
+ qDebug() << " \"-brightness[up/down]\" Flag to increase/decrease screen brightness by 5%";
+ qDebug() << " \"-autostart-apps\" Flag to launch all the various apps which are registered with XDG autostart specification";
+ 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);
+ LUtils::LoadTranslation(&App,"lumina-open");
+
+ //Display the OSD
+ QPixmap pix(":/icons/OSD.png");
+ QLabel splash(0, Qt::Window | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
+ splash.setWindowTitle("");
+ splash.setStyleSheet("QLabel{background: black; color: white; font-weight: bold; font-size: 13pt; margin: 1ex;}");
+ splash.setAlignment(Qt::AlignCenter);
+
+
+ qDebug() << "Display OSD";
+ splash.setText(message);
+ //Make sure it is centered on the current screen
+ QPoint center = App.desktop()->screenGeometry(QCursor::pos()).center();
+ splash.move(center.x()-(splash.sizeHint().width()/2), center.y()-(splash.sizeHint().height()/2));
+ splash.show();
+ //qDebug() << " - show message";
+ //qDebug() << " - loop";
+ QDateTime end = QDateTime::currentDateTime().addMSecs(800);
+ while(QDateTime::currentDateTime() < end){ App.processEvents(); }
+ splash.hide();
+}
+
+void LaunchAutoStart(){
+ QList<XDGDesktop> xdgapps = LXDG::findAutoStartFiles();
+ for(int i=0; i<xdgapps.length(); i++){
+ //Generate command and clean up any stray "Exec" field codes (should not be any here)
+ QString cmd = LXDG::getDesktopExec(xdgapps[i]);
+ if(cmd.contains("%")){cmd = cmd.remove("%U").remove("%u").remove("%F").remove("%f").remove("%i").remove("%c").remove("%k").simplified(); }
+ //Now run the command
+ if(!cmd.isEmpty()){
+ qDebug() << " - Auto-Starting File:" << xdgapps[i].filePath;
+ QProcess::startDetached(cmd);
+ }
+ }
+}
+
+QString cmdFromUser(int argc, char **argv, QString inFile, QString extension, QString& path, bool showDLG=false){
+ //First check to see if there is a default for this extension
+ QString defApp;
+ if(extension=="mimetype"){
+ //qDebug() << "inFile:" << inFile;
+ QStringList matches = LXDG::findAppMimeForFile(inFile, true).split("::::"); //allow multiple matches
+ //qDebug() << "Matches:" << matches;
+ for(int i=0; i<matches.length(); i++){
+ defApp = LXDG::findDefaultAppForMime(matches[i]);
+ if(!defApp.isEmpty()){ extension = matches[i]; break; }
+ else if(i+1==matches.length()){ extension = matches[0]; }
+ }
+ }else{ defApp = LFileDialog::getDefaultApp(extension); }
+ //qDebug() << "extension:" << extension << "defApp:" << defApp;
+ if( !defApp.isEmpty() && !showDLG ){
+ bool ok = false;
+ if(defApp.endsWith(".desktop")){
+ XDGDesktop DF = LXDG::loadDesktopFile(defApp, ok);
+ if(ok){
+ QString exec = LXDG::getDesktopExec(DF);
+ if(!exec.isEmpty()){
+ qDebug() << "[lumina-open] Using default application:" << DF.name << "File:" << inFile;
+ if(!DF.path.isEmpty()){ path = DF.path; }
+ return exec;
+ }
+ }
+ }else{
+ //Only binary given
+ if(LUtils::isValidBinary(defApp)){
+ qDebug() << "[lumina-open] Using default application:" << defApp << "File:" << inFile;
+ return defApp; //just use the binary
+ }
+ }
+ //invalid default - reset it and continue on
+ LFileDialog::setDefaultApp(extension, "");
+ }
+ //Final catch: directory given - no valid default found - use lumina-fm
+ if(extension=="inode/directory" && !showDLG){ return "lumina-fm"; }
+ //No default set -- Start up the application selection dialog
+ LTHEME::LoadCustomEnvSettings();
+ QApplication App(argc, argv);
+ LuminaThemeEngine theme(&App);
+ LUtils::LoadTranslation(&App,"lumina-open");
+
+ LFileDialog w;
+ if(extension=="email" || extension.startsWith("x-scheme-handler/")){
+ //URL
+ w.setFileInfo(inFile, extension, false);
+ }else{
+ //File
+ if(inFile.endsWith("/")){ inFile.chop(1); }
+ w.setFileInfo(inFile.section("/",-1), extension, true);
+ }
+
+ w.show();
+ App.exec();
+ if(!w.appSelected){ return ""; }
+ //Return the run path if appropriate
+ if(!w.appPath.isEmpty()){ path = w.appPath; }
+ //Just do the default application registration here for now
+ // might move it to the runtime phase later after seeing that the app has successfully started
+ if(w.setDefault){
+ if(!w.appFile.isEmpty()){ LFileDialog::setDefaultApp(extension, w.appFile); }
+ else{ LFileDialog::setDefaultApp(extension, w.appExec); }
+ }else{ LFileDialog::setDefaultApp(extension, ""); }
+ //Now return the resulting application command
+ return w.appExec;
+}
+
+void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& path, bool& watch){
+ //Get the input file
+ //Make sure to load the proper system encoding first
+ LUtils::LoadTranslation(0,""); //bypass application modification
+ QString inFile, ActionID;
+ bool showDLG = false; //flag to bypass any default application setting
+ if(argc > 1){
+ for(int i=1; i<argc; i++){
+ if(QString(argv[i]).simplified() == "-select"){
+ showDLG = true;
+ }else if(QString(argv[i]).simplified() == "-testcrash"){
+ //Test the crash handler
+ binary = "internalcrashtest"; watch=true;
+ return;
+ }else if(QString(argv[i]).simplified() == "-autostart-apps"){
+ LaunchAutoStart();
+ return;
+ }else if(QString(argv[i]).simplified() == "-volumeup"){
+ int vol = LOS::audioVolume()+5; //increase 5%
+ if(vol>100){ vol=100; }
+ LOS::setAudioVolume(vol);
+ showOSD(argc,argv, QString(QObject::tr("Audio Volume %1%")).arg(QString::number(vol)) );
+ return;
+ }else if(QString(argv[i]).simplified() == "-volumedown"){
+ int vol = LOS::audioVolume()-5; //decrease 5%
+ if(vol<0){ vol=0; }
+ LOS::setAudioVolume(vol);
+ showOSD(argc,argv, QString(QObject::tr("Audio Volume %1%")).arg(QString::number(vol)) );
+ return;
+ }else if(QString(argv[i]).simplified() == "-brightnessup"){
+ int bright = LOS::ScreenBrightness();
+ if(bright > 0){ //brightness control available
+ bright = bright+5; //increase 5%
+ if(bright>100){ bright = 100; }
+ LOS::setScreenBrightness(bright);
+ showOSD(argc,argv, QString(QObject::tr("Screen Brightness %1%")).arg(QString::number(bright)) );
+ }
+ return;
+ }else if(QString(argv[i]).simplified() == "-brightnessdown"){
+ int bright = LOS::ScreenBrightness();
+ if(bright > 0){ //brightness control available
+ bright = bright-5; //decrease 5%
+ if(bright<0){ bright = 0; }
+ LOS::setScreenBrightness(bright);
+ showOSD(argc,argv, QString(QObject::tr("Screen Brightness %1%")).arg(QString::number(bright)) );
+ }
+ return;
+ }else if( (QString(argv[i]).simplified() =="-action") && (argc>(i+1)) ){
+ ActionID = QString(argv[i+1]);
+ i++; //skip the next input
+ }else{
+ inFile = QString::fromLocal8Bit(argv[i]);
+ break;
+ }
+ }
+ }else{
+ printUsageInfo();
+ }
+ //Make sure that it is a valid file/URL
+ bool isFile=false; bool isUrl=false;
+ //Quick check/replacement for the URL syntax of a file
+ if(inFile.startsWith("file://")){ inFile.remove(0,7); }
+ //Now check what type of file this is
+ 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 ){ 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;
+ if(isFile){
+ QFileInfo info(inFile);
+ extension=info.suffix();
+ //qDebug() << " - Extension:" << extension;
+ if(info.isDir()){ extension="inode/directory"; }
+ else if(info.isExecutable() && extension.isEmpty()){ extension="binary"; }
+ else if(extension!="desktop"){ extension="mimetype"; } //flag to check for mimetype default based on file
+ }
+ else if(isUrl && inFile.startsWith("mailto:")){ extension = "email"; }
+ else if(isUrl && inFile.contains("://") ){ extension = "x-scheme-handler/"+inFile.section("://",0,0); }
+ else if(isUrl && inFile.startsWith("www.")){ extension = "x-scheme-handler/http"; inFile.prepend("http://"); } //this catches partial (but still valid) URL's ("www.<something>" for instance)
+ //qDebug() << "Input:" << inFile << isFile << isUrl << extension;
+ //if not an application - find the right application to open the file
+ QString cmd;
+ bool useInputFile = false;
+ if(extension=="desktop" && !showDLG){
+ bool ok = false;
+ XDGDesktop DF = LXDG::loadDesktopFile(inFile, ok);
+ if(!ok){
+ ShowErrorDialog( argc, argv, QString(QObject::tr("File could not be opened: %1")).arg(inFile) );
+ }
+ switch(DF.type){
+ case XDGDesktop::APP:
+ if(!DF.exec.isEmpty()){
+ cmd = LXDG::getDesktopExec(DF,ActionID);
+ if(!DF.path.isEmpty()){ path = DF.path; }
+ watch = DF.startupNotify || !DF.filePath.contains("/xdg/autostart/");
+ }else{
+ ShowErrorDialog( argc, argv, QString(QObject::tr("Application shortcut is missing the launching information (malformed shortcut): %1")).arg(inFile) );
+ }
+ break;
+ case XDGDesktop::LINK:
+ if(!DF.url.isEmpty()){
+ //This is a URL - so adjust the input variables appropriately
+ inFile = DF.url;
+ cmd.clear();
+ extension = inFile.section(":",0,0);
+ watch = DF.startupNotify || !DF.filePath.contains("/xdg/autostart/");
+ }else{
+ ShowErrorDialog( argc, argv, QString(QObject::tr("URL shortcut is missing the URL: %1")).arg(inFile) );
+ }
+ break;
+ case XDGDesktop::DIR:
+ if(!DF.path.isEmpty()){
+ //This is a directory link - adjust inputs
+ inFile = DF.path;
+ cmd.clear();
+ extension = "directory";
+ watch = DF.startupNotify || !DF.filePath.contains("/xdg/autostart/");
+ }else{
+ ShowErrorDialog( argc, argv, QString(QObject::tr("Directory shortcut is missing the path to the directory: %1")).arg(inFile) );
+ }
+ break;
+ default:
+ qDebug() << DF.type << DF.name << DF.icon << DF.exec;
+ ShowErrorDialog( argc, argv, QString(QObject::tr("Unknown type of shortcut : %1")).arg(inFile) );
+ }
+ }
+ if(cmd.isEmpty()){
+ if(extension=="binary" && !showDLG){ cmd = inFile; }
+ else{
+ //Find out the proper application to use this file/directory
+ useInputFile=true;
+ cmd = cmdFromUser(argc, argv, inFile, extension, path, showDLG);
+ if(cmd.isEmpty()){ return; }
+ }
+ }
+ //Now assemble the exec string (replace file/url field codes as necessary)
+ if(useInputFile){
+ args = inFile; //just to keep them distinct internally
+ // NOTE: lumina-open is only designed for a single input file,
+ // so no need to distinguish between the list codes (uppercase)
+ // and the single-file codes (lowercase)
+ //Special "inFile" format replacements for input codes
+ if( (cmd.contains("%f") || cmd.contains("%F") ) ){
+ //Apply any special field replacements for the desired format
+ inFile.replace("%20"," ");
+ if(inFile.startsWith("file://")){ inFile.remove(0,7); } //chop that URL prefix off the front (should have happened earlier - just make sure)
+ //Now replace the field codes
+ cmd.replace("%f","\""+inFile+"\"");
+ cmd.replace("%F","\""+inFile+"\"");
+ }else if( (cmd.contains("%U") || cmd.contains("%u")) ){
+ //Apply any special field replacements for the desired format
+ if(!inFile.contains("://")){ inFile.prepend("file://"); } //local file - add the extra flag
+ inFile.replace(" ", "%20");
+ //Now replace the field codes
+ cmd.replace("%u","\""+inFile+"\"");
+ cmd.replace("%U","\""+inFile+"\"");
+ }else{
+ //No field codes (or improper field codes given in the file - which is quite common)
+ // - Just tack the input file on the end and let the app handle it as necessary
+ cmd.append(" \""+inFile+"\"");
+ }
+ }
+ //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
+
+}
+
+int main(int argc, char **argv){
+ //Run all the actual code in a separate function to have as little memory usage
+ // as possible aside from the main application when running
+
+ //Make sure the XDG environment variables exist first
+ LXDG::setEnvironmentVars();
+ //now get the command
+ QString cmd, args, path;
+ bool watch = true; //enable the crash handler by default (only disabled for some *.desktop inputs)
+ getCMD(argc, argv, cmd, args, path, watch);
+ //qDebug() << "Run CMD:" << cmd << args;
+ //Now run the command (move to execvp() later?)
+ if(cmd.isEmpty()){ return 0; } //no command to run (handled internally)
+ qDebug() << "[lumina-open] Running Cmd:" << cmd;
+ int retcode = 0;
+
+ if(!watch && path.isEmpty()){
+ //Nothing special about this one - just start it detached (less overhead)
+ QProcess::startDetached(cmd);
+ }else{
+ //Keep an eye on this process for errors and notify the user if it crashes
+ QString log;
+ if(cmd.contains("\\\\")){
+ //Special case (generally for Wine applications)
+ cmd = cmd.replace("\\\\","\\");
+ retcode = system(cmd.toLocal8Bit()); //need to run it through the "system" instead of QProcess
+ }else if(cmd=="internalcrashtest"){
+ log = "This is a sample crash log";
+ retcode = 2;
+ }else{
+ QProcess *p = new QProcess();
+ p->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
+ if(!path.isEmpty() && QFile::exists(path)){
+ //qDebug() << " - Setting working path:" << path;
+ p->setWorkingDirectory(path);
+ }
+ p->start(cmd);
+
+ //Now check up on it once every minute until it is finished
+ while(!p->waitForFinished(60000)){
+ //qDebug() << "[lumina-open] process check:" << p->state();
+ if(p->state() != QProcess::Running){ break; } //somehow missed the finished signal
+ }
+ retcode = p->exitCode();
+ if( (p->exitStatus()==QProcess::CrashExit) && retcode ==0){ retcode=1; } //so we catch it later
+ log = QString(p->readAllStandardError());
+ if(log.isEmpty()){ log = QString(p->readAllStandardOutput()); }
+ }
+ //qDebug() << "[lumina-open] Finished Cmd:" << cmd << retcode << p->exitStatus();
+ if( QFile::exists("/tmp/.luminastopping") ){ watch = false; } //closing down session - ignore "crashes" (app could have been killed during cleanup)
+ if( (retcode > 0) && watch && !(retcode==1 && cmd.startsWith("pc-su ")) ){ //pc-su returns 1 if the user cancelles the operation
+
+ qDebug() << "[lumina-open] Application Error:" << retcode;
+ //Setup the application
+ QApplication App(argc, argv);
+ LuminaThemeEngine theme(&App);
+ LUtils::LoadTranslation(&App,"lumina-open");
+ //App.setApplicationName("LuminaOpen");
+ QMessageBox dlg(QMessageBox::Critical, QObject::tr("Application Error"), QObject::tr("The following application experienced an error and needed to close:")+"\n\n"+cmd );
+ dlg.setWindowFlags(Qt::Window);
+ if(!log.isEmpty()){ dlg.setDetailedText(log); }
+ dlg.exec();
+ }
+ }
+ return retcode;
+}
bgstack15