From fca13b7dd02f5445e0523cb7736abcd91c7b2864 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Tue, 29 Aug 2017 13:08:26 -0400 Subject: Start getting the Root context menu all moved over from Lumina 1 to 2. Not quite finished yet. --- src-qt5/core/libLumina/LuminaXDG.cpp | 159 ++++++++++++++++----- src-qt5/core/libLumina/LuminaXDG.h | 11 +- src-qt5/core/lumina-desktop-unified/LSession.cpp | 2 +- .../src-desktop/ContextMenu.cpp | 53 +++++-- .../src-desktop/ContextMenu.h | 7 +- 5 files changed, 177 insertions(+), 55 deletions(-) diff --git a/src-qt5/core/libLumina/LuminaXDG.cpp b/src-qt5/core/libLumina/LuminaXDG.cpp index 01b3305e..dea1938b 100644 --- a/src-qt5/core/libLumina/LuminaXDG.cpp +++ b/src-qt5/core/libLumina/LuminaXDG.cpp @@ -44,7 +44,7 @@ void XDGDesktop::sync(){ //Get the current localization code type = XDGDesktop::APP; //assume this initially if we read the file properly QString lang = QLocale::system().name(); //lang code - QString slang = lang.section("_",0,0); //short lang code + QString slang = lang.section("_",0,0); //short lang code //Now start looping over the information XDGDesktopAction CDA; //current desktop action bool insection=false; @@ -53,14 +53,14 @@ void XDGDesktop::sync(){ QString line = file[i]; //if(filePath.contains("pcbsd")){ qDebug() << " - Check Line:" << line << inaction << insection; } //Check if this is the end of a section - if(line.startsWith("[") && inaction){ + if(line.startsWith("[") && inaction){ insection=false; inaction=false; //Add the current Action structure to the main desktop structure if appropriate 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; } - else if(line.startsWith("[Desktop Action ")){ + 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(); inaction = true; @@ -73,7 +73,7 @@ void XDGDesktop::sync(){ var = var.section("[",0,0).simplified(); //remove the localization QString val = line.section("=",1,50).simplified(); //------------------- - if(var=="Name"){ + if(var=="Name"){ if(insection){ if(name.isEmpty() && loc.isEmpty()){ name = val; } else if(name.isEmpty() && loc==slang){ name = val; } //short locale code @@ -81,18 +81,18 @@ void XDGDesktop::sync(){ }else if(inaction){ if(CDA.name.isEmpty() && loc.isEmpty()){ CDA.name = val; } else if(CDA.name.isEmpty() && loc==slang){ CDA.name = val; } //short locale code - else if(loc == lang){ CDA.name = val; } + else if(loc == lang){ CDA.name = val; } } //hasName = true; - }else if(var=="GenericName" && insection){ + }else if(var=="GenericName" && insection){ if(genericName.isEmpty() && loc.isEmpty()){ genericName = val; } else if(genericName.isEmpty() && loc==slang){ genericName = val; } //short locale code else if(loc == lang){ genericName = val; } - }else if(var=="Comment" && insection){ + }else if(var=="Comment" && insection){ if(comment.isEmpty() && loc.isEmpty()){ comment = val; } else if(comment.isEmpty() && loc==slang){ comment = val; } //short locale code else if(loc == lang){ comment = val; } - }else if(var=="Icon"){ + }else if(var=="Icon"){ if(insection){ if(icon.isEmpty() && loc.isEmpty()){ icon = val; } else if(icon.isEmpty() && loc==slang){ icon = val; } //short locale code @@ -107,7 +107,7 @@ void XDGDesktop::sync(){ else if(var=="Exec"){ if(insection && exec.isEmpty() ){ exec = val; } else if(inaction && CDA.exec.isEmpty() ){ CDA.exec = val; } - } + } else if( (var=="Path") && (path.isEmpty() ) && insection){ path = val; } else if(var=="NoDisplay" && !isHidden && insection){ isHidden = (val.toLower()=="true"); } else if(var=="Hidden" && !isHidden && insection){ isHidden = (val.toLower()=="true"); } @@ -117,7 +117,7 @@ void XDGDesktop::sync(){ else if(var=="Terminal" && insection){ useTerminal= (val.toLower()=="true"); } else if(var=="Actions" && insection){ actionList = val.split(";",QString::SkipEmptyParts); } else if(var=="MimeType" && insection){ mimeList = val.split(";",QString::SkipEmptyParts); } - else if(var=="Keywords" && insection){ + else if(var=="Keywords" && insection){ if(keyList.isEmpty() && loc.isEmpty()){ keyList = val.split(";",QString::SkipEmptyParts); } else if(loc == lang){ keyList = val.split(";",QString::SkipEmptyParts); } } @@ -136,7 +136,7 @@ void XDGDesktop::sync(){ 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) ){ - name.append(" ("+showInList.join(", ")+")"); + name.append(" ("+showInList.join(", ")+")"); } //Quick fix for showing "wine" applications (which quite often don't list a category, or have other differences) if(catList.isEmpty() && filePath.contains("/wine/")){ @@ -164,7 +164,7 @@ bool XDGDesktop::isValid(bool showAll){ //if(DEBUG){ qDebug() << "[LXDG] Check File validity:" << dFile.name << dFile.filePath; } switch (type){ case XDGDesktop::BAD: - ok=false; + ok=false; //if(DEBUG){ qDebug() << " - Bad file type"; } break; case XDGDesktop::APP: @@ -182,7 +182,7 @@ bool XDGDesktop::isValid(bool showAll){ break; default: ok=false; - //if(DEBUG){ qDebug() << " - Unknown file type"; } + //if(DEBUG){ qDebug() << " - Unknown file type"; } } if(!showAll){ QString cdesk = getenv("XDG_CURRENT_DESKTOP"); @@ -206,7 +206,7 @@ QString XDGDesktop::getDesktopExec(QString ActionID){ } } } - + if(out.isEmpty()){ return ""; } else if(useTerminal){ //Get the currently default terminal @@ -222,7 +222,7 @@ QString XDGDesktop::getDesktopExec(QString ActionID){ } //Now perform any of the XDG flag substitutions as appropriate (9/2014 standards) if(out.contains("%i") && !icon.isEmpty() ){ out.replace("%i", "--icon \""+icon+"\""); } - if(out.contains("%c")){ + if(out.contains("%c")){ if(!name.isEmpty()){ out.replace("%c", "\""+name+"\""); } else if(!genericName.isEmpty()){ out.replace("%c", "\""+genericName+"\""); } else{ out.replace("%c", "\""+filePath.section("/",-1).section(".desktop",0,0)+"\""); } @@ -250,17 +250,17 @@ QString XDGDesktop::generateExec(QStringList inputfiles, QString ActionID){ } } //Now to the exec replacements as needed - if(exec.contains("%f")){ + if(exec.contains("%f")){ if(inputfiles.isEmpty()){ exec.replace("%f",""); } else{ exec.replace("%f", "\""+inputfiles.first()+"\""); } //Note: can only take one input - }else if(exec.contains("%F")){ + }else if(exec.contains("%F")){ if(inputfiles.isEmpty()){ exec.replace("%F",""); } else{ exec.replace("%F", "\""+inputfiles.join("\" \"")+"\""); } } - if(exec.contains("%u")){ + if(exec.contains("%u")){ if(inputfiles.isEmpty()){ exec.replace("%u",""); } else{ exec.replace("%u", "\""+inputfiles.first()+"\""); } //Note: can only take one input - }else if(exec.contains("%U")){ + }else if(exec.contains("%U")){ if(inputfiles.isEmpty()){ exec.replace("%U",""); } else{ exec.replace("%U", "\""+inputfiles.join("\" \"")+"\""); } } @@ -281,7 +281,7 @@ bool XDGDesktop::saveDesktopFile(bool merge){ info = LUtils::readFile(filePath); //set a couple flags based on the contents before we start iterating through // - determine if a translated field was changed (need to remove all the now-invalid translations) - bool clearName, clearComment, clearGName; + bool clearName, clearComment, clearGName; QString tmp = ""; if(!info.filter("Name=").isEmpty()){ tmp = info.filter("Name=").first().section("=",1,50); } clearName=(tmp!=name); @@ -294,13 +294,13 @@ bool XDGDesktop::saveDesktopFile(bool merge){ //Now start iterating through the file and changing fields as necessary bool insection = false; for(int i=0; iname, topmenu); + act->setIcon(LXDG::findIcon(this->icon, "")); + act->setToolTip(this->comment); + act->setWhatsThis(this->filePath); + topmenu->addAction(act); + }else{ + //This app has additional actions - make this a sub menu + // - first the main menu/action + QMenu *submenu = new QMenu(this->name, topmenu); + submenu->setIcon( LXDG::findIcon(this->icon,"") ); + //This is the normal behavior - not a special sub-action (although it needs to be at the top of the new menu) + QAction *act = new QAction(this->name, submenu); + act->setIcon(LXDG::findIcon(this->icon, "")); + act->setToolTip(this->comment); + act->setWhatsThis(this->filePath); + submenu->addAction(act); + //Now add entries for every sub-action listed + for(int sa=0; saactions.length(); sa++){ + QAction *sact = new QAction( this->actions[sa].name, this); + sact->setIcon(LXDG::findIcon( this->actions[sa].icon, this->icon)); + sact->setToolTip(this->comment); + sact->setWhatsThis("-action \""+this->actions[sa].ID+"\" \""+this->filePath+"\""); + submenu->addAction(sact); + } + topmenu->addMenu(submenu); + } +} + + //====XDGDesktopList Functions ==== XDGDesktopList::XDGDesktopList(QObject *parent, bool watchdirs) : QObject(parent){ synctimer = new QTimer(this); //interval set automatically based on changes/interactions @@ -462,6 +494,14 @@ XDGDesktopList::~XDGDesktopList(){ //nothing special to do here } +XDGDesktopList* XDGDesktopList::instance(){ + static XDGDesktopList *APPLIST = 0; + if(APPLIST==0){ + APPLIST = new XDGDesktopList(0, true); + } + return APPLIST; +} + void XDGDesktopList::watcherChanged(){ if(synctimer->isActive()){ synctimer->stop(); } synctimer->setInterval(1000); //1 second delay before check kicks off @@ -484,7 +524,7 @@ void XDGDesktopList::updateList(){ apps = dir.entryList(QStringList() << "*.desktop",QDir::Files, QDir::Name); for(int a=0; alastRead>QFileInfo(path).lastModified()) ){ + if(files.contains(path) && (files.value(path)->lastRead>QFileInfo(path).lastModified()) ){ //Re-use previous data for this file (nothing changed) found << files[path]->name; //keep track of which files were already found }else{ @@ -503,7 +543,7 @@ void XDGDesktopList::updateList(){ } //end loop over apps } //end loop over appDirs //Save the extra info to the internal lists - if(!firstrun){ + if(!firstrun){ removedApps = oldkeys;//files which were removed newApps = newfiles; //files which were added } @@ -539,6 +579,51 @@ QList XDGDesktopList::apps(bool showAll, bool showHidden){ return out; } +XDGDesktop* XDGDesktopList::findAppFile(QString filename){ + QStringList keys = files.keys().filter(filename); + QString chk = filename.section("/",-1); + for(int i=0; iclear(); + if(byCategory){ + QHash > APPS = LXDG::sortDesktopCats( this->apps(false,false) ); + QStringList cats = APPS.keys(); + cats.sort(); //make sure they are alphabetical + for(int i=0; isetIcon(LXDG::findIcon(icon,"")); + QList appL = APPS.value(cats[i]); + for( int a=0; aaddToMenu(menu); } + } //end loop over cats + }else{ + QList APPS = this->apps(false, false); + for(int i=0; iaddToMenu(topmenu); } + } +} + //==== LFileInfo Functions ==== //Need some extra information not usually available by a QFileInfo void LFileInfo::loadExtraInfo(){ diff --git a/src-qt5/core/libLumina/LuminaXDG.h b/src-qt5/core/libLumina/LuminaXDG.h index cc250c7e..d0f3426f 100644 --- a/src-qt5/core/libLumina/LuminaXDG.h +++ b/src-qt5/core/libLumina/LuminaXDG.h @@ -28,7 +28,8 @@ #include #include #include - +#include +#include // ====================== // FreeDesktop Desktop Actions Framework (data structure) @@ -82,6 +83,9 @@ public: bool saveDesktopFile(bool merge = true); //This will use the "filePath" variable for where to save the file bool setAutoStarted(bool autostart = true); + + //Create a menu entry for this application + void addToMenu(QMenu*); }; // ======================== @@ -93,8 +97,13 @@ public: //Functions XDGDesktopList(QObject *parent = 0, bool watchdirs = false); ~XDGDesktopList(); + + static XDGDesktopList* instance(); + //Main Interface functions QList apps(bool showAll, bool showHidden); //showAll: include invalid files, showHidden: include NoShow/Hidden files + XDGDesktop* findAppFile(QString filename); + void populateMenu(QMenu *, bool byCategory = true); //Administration variables (not typically used directly) QDateTime lastCheck; diff --git a/src-qt5/core/lumina-desktop-unified/LSession.cpp b/src-qt5/core/lumina-desktop-unified/LSession.cpp index ef1e2828..b7ea2fe1 100644 --- a/src-qt5/core/lumina-desktop-unified/LSession.cpp +++ b/src-qt5/core/lumina-desktop-unified/LSession.cpp @@ -54,7 +54,7 @@ LSession::LSession(int &argc, char ** argv) : LSingleApplication(argc, argv, "lu Lumina::NWS->moveToThread(Lumina::EVThread); Lumina::EVThread->start(); Lumina::ROOTWIN = new RootWindow(); - Lumina::APPLIST = new XDGDesktopList(0, true); //keep this list up to date + Lumina::APPLIST = XDGDesktopList::instance(); Lumina::SHORTCUTS = new LShortcutEvents(); //this can be moved to it's own thread eventually as well setupGlobalConnections(); diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp index c3e9a1db..ec470d48 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.cpp @@ -8,36 +8,39 @@ #include void DesktopContextMenu::SettingsChanged(DesktopSettings::File file){ - if(file == DesktopSettings::ContextMenu){ UpdateMenu(); } + if(file == DesktopSettings::ContextMenu){ UpdateMenu(false); } } -void DesktopContextMenu::UpdateMenu(){ +void DesktopContextMenu::UpdateMenu(bool fast){ //Put a label at the top unsigned int num = Lumina::NWS->currentWorkspace(); workspaceLabel->setText( ""+QString(tr("Workspace %1")).arg(QString::number(num+1))+""); + if(fast && usewinmenu){ updateWinMenu(); } + if(fast){ return; } //already done this->clear(); //clear it for refresh this->addAction(wkspaceact); this->addSeparator(); //Now load the user's menu setup and fill the menu - QStringList items = DesktopSettings::instance()->value(DesktopSettings::ContextMenu, "itemlist", QStringList()<< "terminal" << "filemanager" << "line" << "settings" <<"lockdesktop").toStringList(); - //usewinmenu=false; + QStringList items = DesktopSettings::instance()->value(DesktopSettings::ContextMenu, "itemlist", QStringList()<< "terminal" << "filemanager" << "line" << "applications" << "windowlist" << "settings" << "lockdesktop").toStringList(); + usewinmenu=false; for(int i=0; iaddAction(LXDG::findIcon("utilities-terminal",""), tr("Terminal"))->setWhatsThis("lumina-open -terminal"); } else if(items[i]=="lockdesktop"){ this->addAction(LXDG::findIcon("system-lock-screen",""), tr("Lock Session"), this, SIGNAL(LockSession()) ); } else if(items[i]=="filemanager"){ this->addAction( LXDG::findIcon("user-home",""), tr("Browse Files"))->setWhatsThis("lumina-open \""+QDir::homePath()+"\""); } - //else if(items[i]=="applications"){ this->addMenu( LSession::handle()->applicationMenu() ); } + else if(items[i]=="applications"){ this->addMenu( appMenu ); } else if(items[i]=="line"){ this->addSeparator(); } //else if(items[i]=="settings"){ this->addMenu( LSession::handle()->settingsMenu() ); } - //else if(items[i]=="windowlist"){ this->addMenu( winMenu); usewinmenu=true;} + else if(items[i]=="windowlist"){ updateWinMenu(); this->addMenu( winMenu); usewinmenu=true; } else if(items[i].startsWith("app::::") && items[i].endsWith(".desktop")){ //Custom *.desktop application QString file = items[i].section("::::",1,1).simplified(); - XDGDesktop xdgf(file);// = LXDG::loadDesktopFile(file, ok); - if(xdgf.type!=XDGDesktop::BAD){ - this->addAction( LXDG::findIcon(xdgf.icon,""), xdgf.name)->setWhatsThis("lumina-open \""+file+"\""); - }else{ - qDebug() << "Could not load application file:" << file; - } + //Try to use the pre-loaded app entry for this + XDGDesktop *xdg = XDGDesktopList::instance()->findAppFile(file); + if(xdg!=0){ xdg->addToMenu(this); } + else{ + XDGDesktop xdgf(file);// = LXDG::loadDesktopFile(file, ok); + if(xdgf.type!=XDGDesktop::BAD){ xdgf.addToMenu(this); } + } }/*else if(items[i].startsWith("jsonmenu::::")){ //Custom JSON menu system (populated on demand via external scripts/tools QStringList info = items[i].split("::::"); //FORMAT:[ "jsonmenu",exec,name, icon(optional)] @@ -62,11 +65,15 @@ DesktopContextMenu::DesktopContextMenu(QWidget *parent) : QMenu(parent){ parent->setContextMenuPolicy(Qt::CustomContextMenu); connect(parent, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&)) ); } + appMenu = 0; + winMenu = 0; workspaceLabel = new QLabel(0); wkspaceact = new QWidgetAction(0); wkspaceact->setDefaultWidget(workspaceLabel); connect(this, SIGNAL(triggered(QAction*)), this, SLOT(LaunchAction(QAction*)) ); - + //Connect to a couple global objects + connect(XDGDesktopList::instance(), SIGNAL(appsUpdated()), this, SLOT(updateAppMenu()) ); + connect(this, SIGNAL(aboutToShow()), this, SLOT(UpdateMenu()) ); //this will do a "fast" update } DesktopContextMenu::~DesktopContextMenu(){ @@ -78,9 +85,8 @@ DesktopContextMenu::~DesktopContextMenu(){ void DesktopContextMenu::start(){ connect(DesktopSettings::instance(), SIGNAL(FileModified(DesktopSettings::File)), this, SLOT(SettingsChanged(DesktopSettings::File)) ); connect(this, SIGNAL(LockSession()), Lumina::SS, SLOT(LockScreenNow()) ); - + UpdateMenu(false); //Still need to connect to some "workspaceChanged(int)" signal - QTimer::singleShot(0, this, SLOT(UpdateMenu()) ); //initial update } // === PRIVATE SLOTS === @@ -95,3 +101,20 @@ void DesktopContextMenu::LaunchAction(QAction *act){ void DesktopContextMenu::showMenu(const QPoint &pt){ this->popup(pt); } + +void DesktopContextMenu::updateAppMenu(){ + if(appMenu==0){ + appMenu = new QMenu(this); + appMenu->setTitle( tr("Applications")); + appMenu->setIcon( LXDG::findIcon("system-run","") ); + } + XDGDesktopList::instance()->populateMenu(appMenu); +} + +void DesktopContextMenu::updateWinMenu(){ + if(winMenu==0){ + winMenu = new QMenu(this); + winMenu->setTitle( tr("Task Manager") ); + } +} + diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h b/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h index 7fd21967..31daa0a8 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/ContextMenu.h @@ -13,11 +13,13 @@ class DesktopContextMenu : public QMenu{ Q_OBJECT public slots: void SettingsChanged(DesktopSettings::File); - void UpdateMenu(); //re-create the menu + void UpdateMenu(bool fast = true); //re-create the menu private: QLabel *workspaceLabel; QWidgetAction *wkspaceact; + QMenu *appMenu, *winMenu; + bool usewinmenu; public: DesktopContextMenu(QWidget *parent = 0); @@ -29,6 +31,9 @@ private slots: void LaunchAction(QAction *act); void showMenu(const QPoint&); + void updateAppMenu(); + void updateWinMenu(); + signals: void LockSession(); void showLeaveDialog(); -- cgit