aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/core/libLumina
diff options
context:
space:
mode:
Diffstat (limited to 'src-qt5/core/libLumina')
-rw-r--r--src-qt5/core/libLumina/DesktopSettings.cpp57
-rw-r--r--src-qt5/core/libLumina/DesktopSettings.h8
-rw-r--r--src-qt5/core/libLumina/LDesktopUtils.cpp36
-rw-r--r--src-qt5/core/libLumina/LDesktopUtils.h4
-rw-r--r--src-qt5/core/libLumina/LIconCache.cpp23
-rw-r--r--src-qt5/core/libLumina/LIconCache.h4
-rw-r--r--src-qt5/core/libLumina/LUtils.cpp27
-rw-r--r--src-qt5/core/libLumina/LUtils.h9
-rw-r--r--src-qt5/core/libLumina/LuminaSingleApplication.cpp12
-rw-r--r--src-qt5/core/libLumina/LuminaThemes.cpp75
-rw-r--r--src-qt5/core/libLumina/LuminaXDG.cpp176
-rw-r--r--src-qt5/core/libLumina/LuminaXDG.h12
-rw-r--r--src-qt5/core/libLumina/NativeEmbedWidget.cpp365
-rw-r--r--src-qt5/core/libLumina/NativeEmbedWidget.h17
-rw-r--r--src-qt5/core/libLumina/NativeEventFilter.cpp11
-rw-r--r--src-qt5/core/libLumina/NativeEventFilter.h1
-rw-r--r--src-qt5/core/libLumina/NativeWindow.cpp4
-rw-r--r--src-qt5/core/libLumina/NativeWindow.h1
-rw-r--r--src-qt5/core/libLumina/NativeWindow.pri1
-rw-r--r--src-qt5/core/libLumina/NativeWindowSystem.cpp108
-rw-r--r--src-qt5/core/libLumina/NativeWindowSystem.h1
-rw-r--r--src-qt5/core/libLumina/RootSubWindow-animations.cpp27
-rw-r--r--src-qt5/core/libLumina/RootSubWindow.cpp162
-rw-r--r--src-qt5/core/libLumina/RootSubWindow.h14
-rw-r--r--src-qt5/core/libLumina/RootWindow-mgmt.cpp58
-rw-r--r--src-qt5/core/libLumina/RootWindow.cpp64
-rw-r--r--src-qt5/core/libLumina/RootWindow.h14
-rw-r--r--src-qt5/core/libLumina/XDGMime.cpp3
28 files changed, 988 insertions, 306 deletions
diff --git a/src-qt5/core/libLumina/DesktopSettings.cpp b/src-qt5/core/libLumina/DesktopSettings.cpp
index 8bda1ac5..f1c74bc5 100644
--- a/src-qt5/core/libLumina/DesktopSettings.cpp
+++ b/src-qt5/core/libLumina/DesktopSettings.cpp
@@ -56,11 +56,19 @@ void DesktopSettings::stop(){
}
//Main Read/Write functions
+QList< DesktopSettings::File > DesktopSettings::writableFiles(){
+ QList< DesktopSettings::File > tmp;
+ if(runmode!=DesktopSettings::SystemFull){ tmp = filesForRunMode(runmode); }
+ return tmp;
+}
+
QVariant DesktopSettings::value(DesktopSettings::File file, QString variable, QVariant defaultvalue){
if(!files.contains(file)){ return defaultvalue; }
for(int i=0; i<files[file].length(); i++){
+ //qDebug() << "Look for Settings value:" << variable << files[file];
if( settings.contains(files[file][i])){ //make sure this file is in the settings hash
if(settings[files[file][i]]->contains(variable)){ //if this file does not have the variable - go to the next one
+ //qDebug() << " - Found Setting in File:" << files[file][i];
return settings[files[file][i]]->value(variable, defaultvalue);
}
}
@@ -118,7 +126,7 @@ void DesktopSettings::parseSystemSettings(){
QString defMode = settings[path]->value("default_mode","fulluser").toString().toLower();
if(defMode=="fullsystem"){ runmode= DesktopSettings::SystemFull; }
else if(defMode=="staticinterface"){ runmode = DesktopSettings::SystemInterface; }
-
+ else{ runmode = DesktopSettings::UserFull; }
//Now determine the runmode for this user
struct passwd *pw = getpwuid(getuid());
if(pw!=0){
@@ -178,25 +186,29 @@ void DesktopSettings::locateFiles(){
systemdirs << QString(getenv("XDG_CONFIG_DIRS")).split(":") << QString(getenv("XDG_DATA_DIRS")).split(":");
//Load all the user-level files for this run mode
QList< DesktopSettings::File > tmp;
- if(runmode == DesktopSettings::UserFull){ tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Keys << DesktopSettings::Theme; }
- else if(runmode == DesktopSettings::SystemInterface){ tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Keys << DesktopSettings::Theme; }
- for(int i=0; i<tmp.length(); i++){
- QString path = userdir+rel_path(tmp[i]);
- touchFile(path);
- files.insert(tmp[i], QStringList() << path);
- settings.insert(path, new QSettings(path, QSettings::IniFormat, this) );
- watcher->addPath(path);
+ if(runmode!=DesktopSettings::SystemFull){
+ //Load the user-level files
+ tmp= filesForRunMode(runmode);
+ for(int i=0; i<tmp.length(); i++){
+ QString path = userdir+rel_path(tmp[i]);
+ touchFile(path);
+ files.insert(tmp[i], QStringList() << path);
+ settings.insert(path, new QSettings(path, QSettings::IniFormat, this) );
+ watcher->addPath(path);
+ }
}
//Now load all the system-level files
- tmp.clear();
- tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Keys << DesktopSettings::Theme;
+ tmp = filesForRunMode(DesktopSettings::SystemFull);
for(int i=0; i<systemdirs.length(); i++){
if(systemdirs[i].endsWith("/xdg")){ systemdirs[i] = systemdirs[i].section("/",0,-2); }
if( !QFile::exists(systemdirs[i]+"/lumina-desktop") ){ continue; }
for(int j=0; j<tmp.length(); j++){
QString path = systemdirs[i]+rel_path(tmp[j]);
if(QFile::exists(path)){
- files.insert(tmp[j], QStringList() << path);
+ QStringList filepaths;
+ if(files.contains(tmp[j])){ filepaths = files[tmp[j]]; }
+ filepaths << path; //add this file to the end of the list for this type of settings file
+ files.insert(tmp[j], filepaths);
settings.insert(path, new QSettings(path, QSettings::IniFormat, this) );
watcher->addPath(path);
}
@@ -219,6 +231,17 @@ void DesktopSettings::touchFile(QString path){
}
}
+QList< DesktopSettings::File > DesktopSettings::filesForRunMode(RunMode mode){
+ // Note that the "System" file is always ignored here - that is specially loaded
+ QList< DesktopSettings::File > tmp;
+ if(mode == DesktopSettings::UserFull || mode == DesktopSettings::SystemFull){
+ tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session << DesktopSettings::Desktop << DesktopSettings::Panels << DesktopSettings::Plugins << DesktopSettings::Keys << DesktopSettings::ContextMenu << DesktopSettings::Animation << DesktopSettings::ScreenSaver << DesktopSettings::WM;
+ }else if(runmode == DesktopSettings::SystemInterface){
+ tmp << DesktopSettings::Favorites << DesktopSettings::Environment << DesktopSettings::Session;
+ }
+ return tmp;
+}
+
QString DesktopSettings::rel_path(DesktopSettings::File file){
QString name;
switch(file){
@@ -236,10 +259,16 @@ QString DesktopSettings::rel_path(DesktopSettings::File file){
name="contextmenu"; break;
case DesktopSettings::Keys:
name="keys"; break;
- case DesktopSettings::Theme:
- name="theme"; break;
case DesktopSettings::Animation:
name="animations"; break;
+ case DesktopSettings::Panels:
+ name="panels"; break;
+ case DesktopSettings::Plugins:
+ name="plugins"; break;
+ case DesktopSettings::ScreenSaver:
+ name="screensaver"; break;
+ case DesktopSettings::WM:
+ name="windows"; break;
}
return FILEPREFIX+name+".conf";
}
diff --git a/src-qt5/core/libLumina/DesktopSettings.h b/src-qt5/core/libLumina/DesktopSettings.h
index dcb10bb6..efb3776e 100644
--- a/src-qt5/core/libLumina/DesktopSettings.h
+++ b/src-qt5/core/libLumina/DesktopSettings.h
@@ -25,7 +25,8 @@
class DesktopSettings : public QObject{
Q_OBJECT
public:
- enum File{ System, Favorites, Environment, Session, Desktop, ContextMenu, Keys, Theme, Animation };
+ enum File{ System, Favorites, Environment, Session, Desktop, Panels, Plugins, ContextMenu, Keys, Animation, ScreenSaver, WM};
+ //Changes to this enum need to be added to the "filesForRunMode()" and "rel_path()" functions as well
DesktopSettings(QObject *parent = 0);
~DesktopSettings();
@@ -37,6 +38,7 @@ public:
void stop();
//Main Read/Write functions
+ QList< DesktopSettings::File > writableFiles(); //return the list of all writable files
QVariant value(DesktopSettings::File, QString variable, QVariant defaultvalue);
bool setValue(DesktopSettings::File, QString variable, QVariant value);
QStringList keys(DesktopSettings::File); //return a list of all variables which are available in this file
@@ -51,9 +53,13 @@ private:
QHash< DesktopSettings::File, QStringList > files; //location hash for where files are actually located on disk
QHash< QString, QSettings*> settings; //location hash for the settings files themselves
+ //Functions
void parseSystemSettings(); //run at start - determine the RunMode for this user/session
void locateFiles(); //run at start - finds the locations of the various files (based on RunMode)
void touchFile(QString path); //used to create an empty file so it can be watched for changes later
+
+ //The two functions which define the public "File" enumeration (both need updates when the enum changes)
+ QList< DesktopSettings::File > filesForRunMode(RunMode mode);
QString rel_path(DesktopSettings::File); //return the relative file path (starting with "/")
private slots:
diff --git a/src-qt5/core/libLumina/LDesktopUtils.cpp b/src-qt5/core/libLumina/LDesktopUtils.cpp
index b79f777d..54e660e6 100644
--- a/src-qt5/core/libLumina/LDesktopUtils.cpp
+++ b/src-qt5/core/libLumina/LDesktopUtils.cpp
@@ -16,7 +16,7 @@
static QStringList fav;
QString LDesktopUtils::LuminaDesktopVersion(){
- QString ver = "1.3.2";
+ QString ver = "1.3.3";
#ifdef GIT_VERSION
ver.append( QString(" (Git Revision: %1)").arg(GIT_VERSION) );
#endif
@@ -143,6 +143,8 @@ void LDesktopUtils::upgradeFavorites(int){ //fromoldversionnumber
void LDesktopUtils::LoadSystemDefaults(bool skipOS){
//Will create the Lumina configuration files based on the current system template (if any)
qDebug() << "Loading System Defaults";
+ bool skipmime = QFile::exists( QString(getenv("XDG_CONFIG_HOME"))+"/lumina-mimapps.list" );
+ qDebug() << " - Skipping mimetype default apps" << skipmime;
QStringList sysDefaults;
if(!skipOS){ sysDefaults = LUtils::readFile(LOS::AppPrefix()+"etc/luminaDesktop.conf"); }
if(sysDefaults.isEmpty() && !skipOS){ sysDefaults = LUtils::readFile(LOS::AppPrefix()+"etc/luminaDesktop.conf.dist"); }
@@ -193,18 +195,18 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){
if(var=="session_enablenumlock"){ sset = "EnableNumlock="+ istrue; }
else if(var=="session_playloginaudio"){ sset = "PlayStartupAudio="+istrue; }
else if(var=="session_playlogoutaudio"){ sset = "PlayLogoutAudio="+istrue; }
- else if(var=="session_default_terminal"){
+ else if(var=="session_default_terminal" && !skipmime){
LXDG::setDefaultAppForMime("application/terminal", val);
//sset = "default-terminal="+val;
- }else if(var=="session_default_filemanager"){
+ }else if(var=="session_default_filemanager" && !skipmime){
LXDG::setDefaultAppForMime("inode/directory", val);
//sset = "default-filemanager="+val;
//loset = "directory="+val;
- }else if(var=="session_default_webbrowser"){
+ }else if(var=="session_default_webbrowser" && !skipmime){
//loset = "webbrowser="+val;
LXDG::setDefaultAppForMime("x-scheme-handler/http", val);
LXDG::setDefaultAppForMime("x-scheme-handler/https", val);
- }else if(var=="session_default_email"){
+ }else if(var=="session_default_email" && !skipmime){
LXDG::setDefaultAppForMime("application/email",val);
//loset = "email="+val;
}
@@ -225,7 +227,7 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){
// -- MIMETYPE DEFAULTS --
tmp = sysDefaults.filter("mime_default_");
- for(int i=0; i<tmp.length(); i++){
+ for(int i=0; i<tmp.length() && !skipmime; i++){
if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
QString var = tmp[i].section("=",0,0).toLower().simplified();
QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
@@ -337,6 +339,16 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){
else if(var=="favorites_remove"){ qDebug() << " - Removing:"; LDesktopUtils::removeFavorite(val); }
}
+ tmp = sysDefaults.filter("desktoplinks_");
+ QString desktopFolder = QDir::homePath()+"/Desktop/"; //need to make this translatable and dynamic later
+ for(int i=0; i<tmp.length(); i++){
+ if(tmp[i].startsWith("#") || !tmp[i].contains("=") ){ continue; }
+ QString var = tmp[i].section("=",0,0).toLower().simplified();
+ QString val = tmp[i].section("=",1,1).section("#",0,0).simplified();
+ val = LUtils::AppToAbsolute(val); //turn any relative files into absolute
+ if(var=="desktoplinks_add" && QFile::exists(val) && !QFile::exists(desktopFolder+val.section("/",-1)) ){ QFile::link(val, desktopFolder+val.section("/",-1)); }
+ }
+
// -- QUICKLAUNCH --
tmp = sysDefaults.filter("quicklaunch_");
if(tmp.isEmpty()){ tmp = sysDefaults.filter("quicklaunch."); }
@@ -409,6 +421,7 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){
}
}
}
+
}
//qDebug() << " - Final Theme Color:" << themesettings[1];
@@ -419,7 +432,16 @@ void LDesktopUtils::LoadSystemDefaults(bool skipOS){
dir.mkpath(setdir);
}
//Now save the settings files
- if(setTheme){ LTHEME::setCurrentSettings( themesettings[0], themesettings[1], themesettings[2], themesettings[3], themesettings[4]); }
+ if(setTheme){
+ LTHEME::setCurrentSettings( themesettings[0], themesettings[1], themesettings[2], themesettings[3], themesettings[4]);
+ QSettings themeset("lthemeengine","lthemeengine");
+ themeset.setValue("Appearance/icon_theme",themesettings[2]);
+ //Quick hack for a "dark" theme/color to be uniform across the desktop/applications
+ if(themesettings[0].contains("DarkGlass") || themesettings[1].contains("Black")){
+ themeset.setValue("Appearance/custom_palette", true);
+ themeset.setValue("Appearance/color_scheme_path", LOS::LuminaShare().section("/",0,-3)+"/lthemeengine/colors/darker.conf");
+ }
+ }
LUtils::writeFile(setdir+"/sessionsettings.conf", sesset, true);
LUtils::writeFile(setdir+"/desktopsettings.conf", deskset, true);
diff --git a/src-qt5/core/libLumina/LDesktopUtils.h b/src-qt5/core/libLumina/LDesktopUtils.h
index b21bc78a..a9b44c67 100644
--- a/src-qt5/core/libLumina/LDesktopUtils.h
+++ b/src-qt5/core/libLumina/LDesktopUtils.h
@@ -30,7 +30,7 @@ public:
static QString findQuickPluginFile(QString ID);
static QStringList listQuickPlugins(); //List of valid ID's
static QStringList infoQuickPlugin(QString ID); //Returns: [Name, Description, Icon]
-
+
//Various functions for the favorites sub-system
// Formatting Note: "<name>::::[dir/app/<mimetype>]::::<path>"
// the <name> field might not be used for "app" flagged entries
@@ -40,7 +40,7 @@ public:
static bool addFavorite(QString path, QString name = "");
static void removeFavorite(QString path);
static void upgradeFavorites(int fromoldversionnumber);
-
+
//Load the default setup for the system
static void LoadSystemDefaults(bool skipOS = false);
static bool checkUserFiles(QString lastversion); //returns true if something changed
diff --git a/src-qt5/core/libLumina/LIconCache.cpp b/src-qt5/core/libLumina/LIconCache.cpp
index 70c360fb..84428546 100644
--- a/src-qt5/core/libLumina/LIconCache.cpp
+++ b/src-qt5/core/libLumina/LIconCache.cpp
@@ -184,6 +184,27 @@ void LIconCache::loadIcon(QLabel *label, QString icon, bool noThumb){
if(needload){ startReadFile(icon, idata.fullpath); }
}
+void LIconCache::loadIcon(QMenu *action, QString icon, bool noThumb){
+ if(icon.isEmpty()){ return; }
+ if(isThemeIcon(icon)){
+ action->setIcon( iconFromTheme(icon));
+ return ;
+ }
+ //See if the icon has already been loaded into the HASH
+ bool needload = !HASH.contains(icon);
+ if(!needload){
+ if(!noThumb && !HASH[icon].thumbnail.isNull()){ action->setIcon( HASH[icon].thumbnail ); return; }
+ else if(!HASH[icon].icon.isNull()){ action->setIcon( HASH[icon].icon ); return; }
+ }
+ //Need to load the icon
+ icon_data idata;
+ if(HASH.contains(icon)){ idata = HASH.value(icon); }
+ else { idata = createData(icon); }
+ idata.pendingMenus << QPointer<QMenu>(action); //save this button for later
+ HASH.insert(icon, idata);
+ if(needload){ startReadFile(icon, idata.fullpath); }
+}
+
void LIconCache::clearIconTheme(){
//use when the icon theme changes to refresh all requested icons
QStringList keys = HASH.keys();
@@ -282,6 +303,8 @@ void LIconCache::startReadFile(QString id, QString path){
idat.pendingLabels.clear();
for(int i=0; i<idat.pendingActions.length(); i++){ if(!idat.pendingActions[i].isNull()){ idat.pendingActions[i]->setIcon(idat.icon); } }
idat.pendingActions.clear();
+ for(int i=0; i<idat.pendingMenus.length(); i++){ if(!idat.pendingMenus[i].isNull()){ idat.pendingMenus[i]->setIcon(idat.icon); } }
+ idat.pendingMenus.clear();
//Now update the hash and let the world know it is available now
HASH.insert(id, idat);
this->emit IconAvailable(id);
diff --git a/src-qt5/core/libLumina/LIconCache.h b/src-qt5/core/libLumina/LIconCache.h
index 428ffcab..691d328c 100644
--- a/src-qt5/core/libLumina/LIconCache.h
+++ b/src-qt5/core/libLumina/LIconCache.h
@@ -4,7 +4,7 @@
// Available under the 3-clause BSD license
// See the LICENSE file for full details
//===========================================
-// This is a simple class for loading/serving icon files
+// This is a simple class for loading/serving icon files
// from the icon theme or local filesystem
//===========================================
#include <QHash>
@@ -26,6 +26,7 @@ struct icon_data{
QList<QPointer<QLabel> > pendingLabels;
QList<QPointer<QAbstractButton> > pendingButtons;
QList<QPointer<QAction> > pendingActions;
+ QList<QPointer<QMenu> > pendingMenus;
QIcon icon;
QIcon thumbnail;
};
@@ -50,6 +51,7 @@ public:
void loadIcon(QAbstractButton *button, QString icon, bool noThumb = false);
void loadIcon(QLabel *label, QString icon, bool noThumb = false);
void loadIcon(QAction *action, QString icon, bool noThumb = false);
+ void loadIcon(QMenu *action, QString icon, bool noThumb = false);
QIcon loadIcon(QString icon, bool noThumb = false); //generic loading routine - does not background the loading of icons when not in the cache
diff --git a/src-qt5/core/libLumina/LUtils.cpp b/src-qt5/core/libLumina/LUtils.cpp
index fa0173dc..491778ca 100644
--- a/src-qt5/core/libLumina/LUtils.cpp
+++ b/src-qt5/core/libLumina/LUtils.cpp
@@ -26,7 +26,7 @@ inline QStringList ProcessRun(QString cmd, QStringList args){
if(args.isEmpty()){
proc.start(cmd, QIODevice::ReadOnly);
}else{
- proc.start(cmd,args ,QIODevice::ReadOnly);
+ proc.start(cmd,args ,QIODevice::ReadOnly);
}
QString info;
while(!proc.waitForFinished(1000)){
@@ -37,7 +37,7 @@ inline QStringList ProcessRun(QString cmd, QStringList args){
}
out[0] = QString::number(proc.exitCode());
out[1] = info+QString(proc.readAllStandardOutput());
- return out;
+ return out;
}
//=============
// LUtils Functions
@@ -59,7 +59,6 @@ int LUtils::runCmd(QString cmd, QStringList args){
return ret;*/
QFuture<QStringList> future = QtConcurrent::run(ProcessRun, cmd, args);
return future.result()[0].toInt(); //turn it back into an integer return code
-
}
QStringList LUtils::getCmdOutput(QString cmd, QStringList args){
@@ -72,7 +71,7 @@ QStringList LUtils::getCmdOutput(QString cmd, QStringList args){
if(args.isEmpty()){
proc.start(cmd);
}else{
- proc.start(cmd,args);
+ proc.start(cmd,args);
}
//if(!proc.waitForStarted(30000)){ return QStringList(); } //process never started - max wait of 30 seconds
while(!proc.waitForFinished(300)){
@@ -118,7 +117,7 @@ bool LUtils::isValidBinary(QString& bin){
//Relative path: search for it on the current "PATH" settings
QStringList paths = QString(qgetenv("PATH")).split(":");
for(int i=0; i<paths.length(); i++){
- if(QFile::exists(paths[i]+"/"+bin)){ bin = paths[i]+"/"+bin; break;}
+ if(QFile::exists(paths[i]+"/"+bin)){ bin = paths[i]+"/"+bin; break;}
}
}
//bin should be the full path by now
@@ -150,7 +149,7 @@ QSettings* LUtils::openSettings(QString org, QString name, QObject *parent){
}else{
return (new QSettings(filepath, QSettings::IniFormat, parent));
}
-
+
}
QStringList LUtils::systemApplicationDirs(){
@@ -164,7 +163,7 @@ QStringList LUtils::systemApplicationDirs(){
for(int i=0; i<appDirs.length(); i++){
if( QFile::exists(appDirs[i]+"/applications") ){
out << appDirs[i]+"/applications";
- //Also check any subdirs within this directory
+ //Also check any subdirs within this directory
// (looking at you KDE - stick to the standards!!)
out << LUtils::listSubDirectories(appDirs[i]+"/applications");
}
@@ -197,7 +196,7 @@ QString LUtils::GenerateOpenTerminalExec(QString term, QString dirpath){
}else if(term=="konsole" || term == "qterminal"){
exec = term+" --workdir \""+dirpath+"\"";
}else{
- //-e is the parameter for most of the terminal appliction to execute an external command.
+ //-e is the parameter for most of the terminal appliction to execute an external command.
//In this case we start a shell in the selected directory
//Need the user's shell first
QString shell = QString(getenv("SHELL"));
@@ -253,12 +252,18 @@ QString LUtils::AppToAbsolute(QString path){
return path;
}
+QStringList LUtils::videoExtensions() {
+ static QStringList vidExtensions;
+ vidExtensions << "avi" << "mkv" << "mp4" << "mov" << "webm" << "wmv";
+ return vidExtensions;
+}
+
QStringList LUtils::imageExtensions(bool wildcards){
//Note that all the image extensions are lowercase!!
static QStringList imgExtensions;
if(imgExtensions.isEmpty()){
QList<QByteArray> fmt = QImageReader::supportedImageFormats();
- for(int i=0; i<fmt.length(); i++){
+ for(int i=0; i<fmt.length(); i++){
if(wildcards){ imgExtensions << "*."+QString::fromLocal8Bit(fmt[i]); }
else{ imgExtensions << QString::fromLocal8Bit(fmt[i]); }
}
@@ -308,7 +313,7 @@ QStringList LUtils::imageExtensions(bool wildcards){
qDebug() << "Loading System Encoding:" << langEnc;
}
//Load current encoding for this locale
- QTextCodec::setCodecForLocale( QTextCodec::codecForName(langEnc.toUtf8()) );
+ QTextCodec::setCodecForLocale( QTextCodec::codecForName(langEnc.toUtf8()) );
return cTrans;
}
@@ -379,7 +384,7 @@ void LUtils::setLocaleEnv(QString lang, QString msg, QString time, QString num,Q
else{
if(!ctype.contains(".")){ ctype.append(".UTF-8"); }
setenv("LC_CTYPE",ctype.toUtf8(),1);
- }
+ }
}
QString LUtils::currentLocale(){
diff --git a/src-qt5/core/libLumina/LUtils.h b/src-qt5/core/libLumina/LUtils.h
index 4ad05ca1..a494d4da 100644
--- a/src-qt5/core/libLumina/LUtils.h
+++ b/src-qt5/core/libLumina/LUtils.h
@@ -55,7 +55,7 @@ public:
//Create the exec string to open a terminal in a particular directory
static QString GenerateOpenTerminalExec(QString term, QString dirpath);
-
+
//List all the sub-directories of a parent dir (recursive)
static QStringList listSubDirectories(QString dir, bool recursive = true);
@@ -65,18 +65,19 @@ public:
//Get the list of all file extensions which Qt can read (lowercase)
static QStringList imageExtensions(bool wildcards = false);
-
+ static QStringList videoExtensions();
+
//Load a translation file for a Lumina Project
static QTranslator* LoadTranslation(QApplication *app, QString appname, QString locale = "", QTranslator *cTrans = 0);
//Other localization shortcuts
static QStringList knownLocales(); //Note: This only lists locales known to Lumina (so the i18n files need to be installed)
static void setLocaleEnv(QString lang, QString msg="", QString time="", QString num="" ,QString money="",QString collate="", QString ctype="");
static QString currentLocale();
-
+
//Number format conversions
static double DisplaySizeToBytes(QString num); //Turn a display size (like 50M or 50KB) into a double for calculations (bytes)
static QString BytesToDisplaySize(qint64 bytes); //convert into a readable size (like 50M or 50KB)
-
+
static QString SecondsToDisplay(int secs); //convert into a readable time
};
#endif
diff --git a/src-qt5/core/libLumina/LuminaSingleApplication.cpp b/src-qt5/core/libLumina/LuminaSingleApplication.cpp
index 86248666..6811d147 100644
--- a/src-qt5/core/libLumina/LuminaSingleApplication.cpp
+++ b/src-qt5/core/libLumina/LuminaSingleApplication.cpp
@@ -107,17 +107,17 @@ void LSingleApplication::PerformLockChecks(){
QLocalSocket socket(this);
socket.connectToServer(cfile);
socket.waitForConnected();
- if(!socket.isValid() || socket.state()!=QLocalSocket::ConnectedState){
+ if(!socket.isValid() || socket.state()!=QLocalSocket::ConnectedState){
//error - could not forward info for some reason
qDebug() << " - Could not connect to locking process: exiting...";
- exit(1);
- }
-
- qDebug() << " - Forwarding inputs to locking process and closing down this instance...";
+ exit(1);
+ }
+
+ qDebug() << " - Forwarding inputs to locking process and closing down this instance...";
socket.write( inputlist.join("::::").toLocal8Bit() );
socket.waitForDisconnected(500); //max out at 1/2 second (only hits this if no inputs)
}
-
+
}
//New messages detected
diff --git a/src-qt5/core/libLumina/LuminaThemes.cpp b/src-qt5/core/libLumina/LuminaThemes.cpp
index 85d43925..ddbc7b37 100644
--- a/src-qt5/core/libLumina/LuminaThemes.cpp
+++ b/src-qt5/core/libLumina/LuminaThemes.cpp
@@ -23,7 +23,7 @@
//#include "qxcbcursor.h" //needed to prod Qt to refresh the mouse cursor theme
//#include <QCursor>
-QStringList LTHEME::availableSystemThemes(){
+QStringList LTHEME::availableSystemThemes(){
//returns: [name::::path] for each item
QDir dir(LOS::LuminaShare()+"themes");
QStringList list = dir.entryList(QStringList() <<"*.qss.template", QDir::Files, QDir::Name);
@@ -31,7 +31,7 @@ QStringList LTHEME::availableSystemThemes(){
//Format the output entry [<name>::::<fullpath>]
list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
}
- return list;
+ return list;
}
QStringList LTHEME::availableLocalThemes(){ //returns: [name::::path] for each item
@@ -52,7 +52,7 @@ QStringList LTHEME::availableSystemColors(){ //returns: [name::::path] for each
//Format the output entry [<name>::::<fullpath>]
list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
}
- return list;
+ return list;
}
QStringList LTHEME::availableLocalColors(){ //returns: [name::::path] for each item
@@ -62,7 +62,7 @@ QStringList LTHEME::availableLocalColors(){ //returns: [name::::path] for each
//Format the output entry [<name>::::<fullpath>]
list[i] = list[i].section(".qss.",0,0)+"::::"+dir.absoluteFilePath(list[i]);
}
- return list;
+ return list;
}
QStringList LTHEME::availableSystemIcons(){ //returns: [name] for each item
@@ -72,7 +72,7 @@ QStringList LTHEME::availableSystemIcons(){ //returns: [name] for each item
xdd << QString(getenv("XDG_DATA_DIRS")).split(":");
for(int i=0; i<xdd.length(); i++){
if(QFile::exists(xdd[i]+"/icons")){
- paths << xdd[i]+"/icons";
+ paths << xdd[i]+"/icons";
}
}
//Now get all the icon themes in these directories
@@ -83,8 +83,8 @@ QStringList LTHEME::availableSystemIcons(){ //returns: [name] for each item
tmpthemes = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
for(int j=0; j<tmpthemes.length(); j++){
if(tmpthemes[j].startsWith("default")){ continue; }
- if(QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/index.theme")) ||
- QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/index.desktop")) ){ themes << tmpthemes[j]; }
+ if( (QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/index.theme")) ||
+ QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/index.desktop")) ) ){ themes << tmpthemes[j]; }
}
}
}
@@ -92,21 +92,32 @@ QStringList LTHEME::availableSystemIcons(){ //returns: [name] for each item
themes.sort();
return themes;
}
-
+
QStringList LTHEME::availableSystemCursors(){ //returns: [name] for each item
- QStringList paths; paths << LOS::SysPrefix()+"lib/X11/icons/" << LOS::AppPrefix()+"lib/X11/icons/";
- QStringList out;
+ QStringList paths;
+ paths << QDir::homePath()+"/.icons";
+ QStringList xdd = QString(getenv("XDG_DATA_HOME")).split(":");
+ xdd << QString(getenv("XDG_DATA_DIRS")).split(":");
+ for(int i=0; i<xdd.length(); i++){
+ if(QFile::exists(xdd[i]+"/icons")){
+ paths << xdd[i]+"/icons";
+ }
+ }
+ //Now get all the icon themes in these directories
+ QStringList themes, tmpthemes;
+ QDir dir;
for(int i=0; i<paths.length(); i++){
- if( !QFile::exists(paths[i]) ){ continue; }
- QDir dir(paths[i]);
- QStringList tmp = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
- for(int j=0; j<tmp.length(); j++){
- if(QFile::exists(paths[i]+tmp[j]+"/cursors")){
- out << tmp[j]; //good theme - save it to the output list
- }
+ if(dir.cd(paths[i])){
+ tmpthemes = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ for(int j=0; j<tmpthemes.length(); j++){
+ if(tmpthemes[j].startsWith("default")){ continue; }
+ if( QFile::exists(dir.absoluteFilePath(tmpthemes[j]+"/cursors")) ){ themes << tmpthemes[j]; }
+ }
}
}
- return out;
+ themes.removeDuplicates();
+ themes.sort();
+ return themes;
}
//Save a new theme/color file
@@ -119,7 +130,7 @@ bool LTHEME::saveLocalTheme(QString name, QStringList contents){
bool LTHEME::saveLocalColors(QString name, QStringList contents){
QString localdir = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-desktop/colors/";
if(!QFile::exists(localdir)){ QDir dir; dir.mkpath(localdir); }
- return LUtils::writeFile(localdir+name+".qss.colors", contents, true);
+ return LUtils::writeFile(localdir+name+".qss.colors", contents, true);
}
//Return the currently selected Theme/Colors/Icons
@@ -129,21 +140,23 @@ QStringList LTHEME::currentSettings(){ //returns [theme path, colorspath, iconsn
for(int i=0; i<settings.length(); i++){
if(settings[i].startsWith("THEMEFILE=")){ out[0] = settings[i].section("=",1,1).simplified(); }
else if(settings[i].startsWith("COLORFILE=")){ out[1] = settings[i].section("=",1,1).simplified(); }
- else if(settings[i].startsWith("ICONTHEME=")){ out[2] = settings[i].section("=",1,1).simplified(); }
+ //else if(settings[i].startsWith("ICONTHEME=")){ out[2] = settings[i].section("=",1,1).simplified(); }
else if(settings[i].startsWith("FONTFAMILY=")){ out[3] = settings[i].section("=",1,1).simplified(); }
else if(settings[i].startsWith("FONTSIZE=")){ out[4] = settings[i].section("=",1,1).simplified(); }
}
+ QSettings engineset("lthemeengine","lthemeengine");
+ out[2]=engineset.value("Appearance/icon_theme", "material-design-light").toString();
bool nofile = settings.isEmpty();
if(out[0].isEmpty() || !QFile::exists(out[0]) ){ out[0] = LOS::LuminaShare()+"themes/Lumina-default.qss.template"; }
if(out[1].isEmpty() || !QFile::exists(out[1]) ){ out[1] = LOS::LuminaShare()+"colors/Lumina-Glass.qss.colors"; }
if(out[3].isEmpty()){ out[3] = QFont().defaultFamily(); }
- if(out[4].isEmpty()){
+ if(out[4].isEmpty()){
int num = QFont().pointSize(); out[4] = QString::number(num)+"pt"; //Check point size first
if(num<0){ num = QFont().pixelSize(); out[4] = QString::number(num)+"px";} //Now check pixel size
if(num<0){ out[4] = "9pt"; } //Now hard-code a fallback (just in case)
}
if(nofile){ setCurrentSettings(out[0], out[1], out[2], out[3], out[4]); }
-
+
return out;
}
@@ -167,8 +180,14 @@ QString LTHEME::currentCursor(){
//Change the current Theme/Colors/Icons
bool LTHEME::setCurrentSettings(QString themepath, QString colorpath, QString iconname, QString font, QString fontsize){
- QIcon::setThemeName(iconname);
- //Now save the theme settings file
+ //QIcon::setThemeName(iconname);
+ QSettings engineset("lthemeengine","lthemeengine");
+ engineset.setValue("Appearance/icon_theme", iconname);
+ //engineset.setValue("Appearance/color_scheme_path", colorpath); //re-enable this once the color scheme has been synced with lthemeengine
+ //Need to add theme path saving here too later
+
+
+ //Now save the theme settings file
QStringList contents;
contents << "THEMEFILE="+themepath;
contents << "COLORFILE="+colorpath;
@@ -195,13 +214,13 @@ bool LTHEME::setCursorTheme(QString cursorname){
bool changed = false;
QString newval = "Inherits="+cursorname;
for(int i=0; i<info.length() && !changed; i++){
- if(info[i]=="[Icon Theme]"){
+ if(info[i]=="[Icon Theme]"){
insection = true;
- }else if( info[i].startsWith("[") && insection){
+ }else if( info[i].startsWith("[") && insection){
//Section does not have the setting - add it
- info.insert(i, newval);
+ info.insert(i, newval);
changed =true;
- }else if( info[i].startsWith("[") ){
+ }else if( info[i].startsWith("[") ){
insection = false;
}else if(insection && info[i].startsWith("Inherits=")){
info[i] = newval; //replace the current setting
diff --git a/src-qt5/core/libLumina/LuminaXDG.cpp b/src-qt5/core/libLumina/LuminaXDG.cpp
index 01b3305e..ab1000ab 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,26 +73,26 @@ 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
- else if(loc == lang){ name = val; }
+ if(loc==slang){ name = val;} //short locale code
+ else if(loc==lang){ name = val;}
+ else if(name.isEmpty() && loc.isEmpty()){ /*qDebug() << "Empty" << val;*/ name = val; }
}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; i<info.length(); i++){
- if(info[i]=="[Desktop Entry]"){
- insection = true;
+ if(info[i]=="[Desktop Entry]"){
+ insection = true;
continue;
- }else if(info[i].startsWith("[")){
+ }else if(info[i].startsWith("[")){
if(insection){ insertloc = i; } //save this location for later insertions
- insection = false;
- continue;
+ insection = false;
+ continue;
}
if(!insection || info[i].isEmpty() || info[i].section("#",0,0).simplified().isEmpty()){ continue; }
QString var = info[i].section("=",0,0);
@@ -331,25 +331,25 @@ bool XDGDesktop::saveDesktopFile(bool merge){
else if(var=="OnlyShowIn"){ info[i] = var+"="+showInList.join(";"); showInList.clear(); }
else if(var=="NotShowIn"){ info[i] = var+"="+notShowInList.join(";"); notShowInList.clear(); }
else if(var=="URL"){ info[i] = var+"="+url; url.clear(); }
-
+
// --BOOLIAN VALUES--
- else if(var=="Hidden"){
+ else if(var=="Hidden"){
if(!autofile){ info.removeAt(i); i--; continue; }
else{ info[i] = var+"="+(isHidden ? "true": "false"); isHidden=false;}
- }else if(var=="NoDisplay"){
+ }else if(var=="NoDisplay"){
if(autofile){ info.removeAt(i); i--; continue; }
else{ info[i] = var+"="+(isHidden ? "true": "false"); isHidden=false;}
- }else if(var=="Terminal"){
+ }else if(var=="Terminal"){
info[i] = var+"="+(useTerminal ? "true": "false"); useTerminal=false;
- }else if(var=="StartupNotify"){
+ }else if(var=="StartupNotify"){
info[i] = var+"="+(startupNotify ? "true": "false"); startupNotify=false;
}
// Remove any lines that have been un-set or removed from the file
if(info[i].section("=",1,50).simplified().isEmpty()){ info.removeAt(i); i--; }
}
-
+
}else{
- //Just write a new file and overwrite any old one
+ //Just write a new file and overwrite any old one
// (pre-set some values here which are always required)
info << "[Desktop Entry]";
info << "Version=1.0";
@@ -357,7 +357,7 @@ bool XDGDesktop::saveDesktopFile(bool merge){
else if(type==XDGDesktop::LINK){ info << "Type=Link"; }
else if(type==XDGDesktop::DIR){ info << "Type=Dir"; }
}
-
+
if(insertloc<0){ insertloc = info.size(); }//put it at the end
//Now add in any items that did not exist in the original file
if( !exec.isEmpty() ){ info.insert(insertloc,"Exec="+exec); }
@@ -379,7 +379,7 @@ bool XDGDesktop::saveDesktopFile(bool merge){
else if(isHidden){ info.insert(insertloc,"NoDisplay=true"); }
if( useTerminal){ info.insert(insertloc,"Terminal=true"); }
if( startupNotify ){ info.insert(insertloc,"StartupNotify=true"); }
-
+
//Now save the file
return LUtils::writeFile(filePath, info, true);
}
@@ -409,7 +409,7 @@ bool XDGDesktop::setAutoStarted(bool autostart){
}
}
//Make sure the user-autostart dir is specified, and clean the app structure as necessary
- if( !filePath.startsWith(upath) && autostart){
+ if( !filePath.startsWith(upath) && autostart){
//Some other non-override autostart file - set it up to open with lumina-open
if(!filePath.endsWith(".desktop")){
exec = "lumina-open \""+filePath+"\"";
@@ -444,6 +444,39 @@ bool XDGDesktop::setAutoStarted(bool autostart){
return saved;
}
+void XDGDesktop::addToMenu(QMenu *topmenu){
+ if(!this->isValid()){ return; }
+ if(actions.isEmpty()){
+ //Just a single entry point - no extra actions
+ QAction *act = new QAction(this->name, 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; sa<this->actions.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 +495,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 +525,7 @@ void XDGDesktopList::updateList(){
apps = dir.entryList(QStringList() << "*.desktop",QDir::Files, QDir::Name);
for(int a=0; a<apps.length(); a++){
path = dir.absoluteFilePath(apps[a]);
- if(files.contains(path) && (files.value(path)->lastRead>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 +544,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 +580,52 @@ QList<XDGDesktop*> 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; i<keys.length(); i++){
+ if(keys[i] == filename || keys[i].endsWith("/"+chk)){ return files[keys[i]]; }
+ }
+ //No matches
+ return 0;
+}
+
+void XDGDesktopList::populateMenu(QMenu *topmenu, bool byCategory){
+ topmenu->clear();
+ if(byCategory){
+ QHash<QString, QList<XDGDesktop*> > APPS = LXDG::sortDesktopCats( this->apps(false,false) );
+ QStringList cats = APPS.keys();
+ cats.sort(); //make sure they are alphabetical
+ for(int i=0; i<cats.length(); i++){
+ //Make sure they are translated and have the right icons
+ QString name, icon;
+ if(cats[i]=="All"){continue; } //skip this listing for the menu
+ else if(cats[i] == "Multimedia"){ name = tr("Multimedia"); icon = "applications-multimedia"; }
+ else if(cats[i] == "Development"){ name = tr("Development"); icon = "applications-development"; }
+ else if(cats[i] == "Education"){ name = tr("Education"); icon = "applications-education"; }
+ else if(cats[i] == "Game"){ name = tr("Games"); icon = "applications-games"; }
+ else if(cats[i] == "Graphics"){ name = tr("Graphics"); icon = "applications-graphics"; }
+ else if(cats[i] == "Network"){ name = tr("Network"); icon = "applications-internet"; }
+ else if(cats[i] == "Office"){ name = tr("Office"); icon = "applications-office"; }
+ else if(cats[i] == "Science"){ name = tr("Science"); icon = "applications-science"; }
+ else if(cats[i] == "Settings"){ name = tr("Settings"); icon = "preferences-system"; }
+ else if(cats[i] == "System"){ name = tr("System"); icon = "applications-system"; }
+ else if(cats[i] == "Utility"){ name = tr("Utility"); icon = "applications-utilities"; }
+ else if(cats[i] == "Wine"){ name = tr("Wine"); icon = "wine"; }
+ else{ name = tr("Unsorted"); icon = "applications-other"; }
+
+ QMenu *menu = new QMenu(name, topmenu);
+ menu->setIcon(LXDG::findIcon(icon,""));
+ QList<XDGDesktop*> appL = APPS.value(cats[i]);
+ for( int a=0; a<appL.length(); a++){ appL[a]->addToMenu(menu); }
+ topmenu->addMenu(menu);
+ } //end loop over cats
+ }else{
+ QList<XDGDesktop*> APPS = this->apps(false, false);
+ for(int i=0; i<APPS.length(); i++){ APPS[i]->addToMenu(topmenu); }
+ }
+}
+
//==== LFileInfo Functions ====
//Need some extra information not usually available by a QFileInfo
void LFileInfo::loadExtraInfo(){
@@ -617,7 +704,14 @@ XDGDesktop* LFileInfo::XDG(){
return desk;
}
-// -- Check if this is a readable image file (for thumbnail support)
+// -- Check if this is a readable video file (for thumbnail support)
+bool LFileInfo::isVideo(){
+ if(!mime.startsWith("video/")){ return false; }
+ //Check the hardcoded list of known supported video formats to see if the thumbnail can be generated
+ return ( !LUtils::videoExtensions().filter(this->suffix().toLower()).isEmpty() );
+}
+
+// -- Check if this is a readable image file
bool LFileInfo::isImage(){
if(!mime.startsWith("image/")){ return false; } //quick return for non-image files
//Check the Qt subsystems to see if this image file can be read
diff --git a/src-qt5/core/libLumina/LuminaXDG.h b/src-qt5/core/libLumina/LuminaXDG.h
index cc250c7e..0f7e7c48 100644
--- a/src-qt5/core/libLumina/LuminaXDG.h
+++ b/src-qt5/core/libLumina/LuminaXDG.h
@@ -28,7 +28,8 @@
#include <QTextStream>
#include <QDateTime>
#include <QDebug>
-
+#include <QMenu>
+#include <QAction>
// ======================
// 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<XDGDesktop*> 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;
@@ -150,6 +159,7 @@ public:
//Other file type identification routines
bool isImage(); //Is a readable image file (for thumbnail support)
+ bool isVideo(); //Is a readable video file (for thumbnail support)
bool isAVFile(); //Is an audio/video file
};
typedef QList<LFileInfo> LFileInfoList;
diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.cpp b/src-qt5/core/libLumina/NativeEmbedWidget.cpp
index 487d1040..ff0f4734 100644
--- a/src-qt5/core/libLumina/NativeEmbedWidget.cpp
+++ b/src-qt5/core/libLumina/NativeEmbedWidget.cpp
@@ -8,31 +8,75 @@
#include <QPainter>
#include <QX11Info>
+#include <QApplication>
+#include <QScreen>
#include <QDebug>
#include <xcb/xproto.h>
#include <xcb/xcb_aux.h>
+#include <xcb/xcb_event.h>
#include <xcb/xcb_image.h>
+//#include <xcb/render.h>
+//#include <xcb/xcb_renderutil.h>
#include <xcb/composite.h>
#include <X11/extensions/Xdamage.h>
#define DISABLE_COMPOSITING true
-#define NORMAL_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
- XCB_EVENT_MASK_BUTTON_RELEASE | \
- XCB_EVENT_MASK_POINTER_MOTION | \
- XCB_EVENT_MASK_BUTTON_MOTION | \
- XCB_EVENT_MASK_EXPOSURE | \
- XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
- XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
- XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
- XCB_EVENT_MASK_ENTER_WINDOW| \
- XCB_EVENT_MASK_PROPERTY_CHANGE)
+/*inline xcb_render_pictformat_t get_pictformat(){
+ static xcb_render_pictformat_t format = 0;
+ if(format==0){
+ xcb_render_query_pict_formats_reply_t *reply = xcb_render_query_pict_formats_reply( QX11Info::connection(), xcb_render_query_pict_formats(QX11Info::connection()), NULL);
+ format = xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32)->id;
+ free(reply);
+ }
+ return format;
+}
+
+inline void renderWindowToWidget(WId id, QWidget *widget, bool hastransparency = true){
+ //window and widget are assumed to be the same size
+ //Pull the XCB pixmap out of the compositing layer
+ xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection());
+ xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix);
+ if(pix==0){ qDebug() << "Got blank pixmap!"; return; }
-inline void registerClientEvents(WId id){
- uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK};
- xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list);
+ xcb_render_picture_t pic_id = xcb_generate_id(QX11Info::connection());
+ xcb_render_create_picture_aux(QX11Info::connection(), pic_id, pix, get_pictformat() , 0, NULL);
+ //
+ xcb_render_composite(QX11Info::connection(), hastransparency ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC, pic_id, XCB_RENDER_PICTURE_NONE, widget->x11RenderHandle(),
+ 0, 0, 0, 0, 0, 0, (uint16_t) widget->width(), (uint16_t) widget->height() );
+}*/
+
+#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_FOCUS_CHANGE | \
+ XCB_EVENT_MASK_POINTER_MOTION)
+
+#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+inline void registerClientEvents(WId id, bool client = true){
+ uint32_t values[] = {XCB_NONE};
+ values[0] = client ? CLIENT_EVENT_MASK : FRAME_EVENT_MASK ;
+ /*{ (XCB_EVENT_MASK_PROPERTY_CHANGE
+ | XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_POINTER_MOTION
+ | XCB_EVENT_MASK_BUTTON_MOTION
+ | XCB_EVENT_MASK_EXPOSURE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
+ | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_ENTER_WINDOW)
+ };*/
+ xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, values);
}
// ============
@@ -40,12 +84,14 @@ inline void registerClientEvents(WId id){
// ============
//Simplification functions for the XCB/XLib interactions
void NativeEmbedWidget::syncWinSize(QSize sz){
- if(WIN==0 ){ return; }
+ if(WIN==0){ return; }
else if(!sz.isValid()){ sz = this->size(); } //use the current widget size
//qDebug() << "Sync Window Size:" << sz;
- if(sz == winSize){ return; } //no change
- const uint32_t valList[2] = {(uint32_t) sz.width(), (uint32_t) sz.height()};
- const uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+ //if(sz == winSize){ return; } //no change
+ QPoint pt(0,0);
+ if(!DISABLE_COMPOSITING){ pt = this->mapToGlobal(QPoint(0,0)); }
+ const uint32_t valList[4] = {(uint32_t) pt.x(), (uint32_t) pt.y(), (uint32_t) sz.width(), (uint32_t) sz.height()};
+ const uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
xcb_configure_window(QX11Info::connection(), WIN->id(), mask, valList);
winSize = sz; //save this for checking later
}
@@ -56,47 +102,74 @@ void NativeEmbedWidget::syncWidgetSize(QSize sz){
}
void NativeEmbedWidget::hideWindow(){
+ //qDebug() << "Hide Embed Window";
xcb_unmap_window(QX11Info::connection(), WIN->id());
}
void NativeEmbedWidget::showWindow(){
+ //qDebug() << "Show Embed Window";
xcb_map_window(QX11Info::connection(), WIN->id());
- QTimer::singleShot(0,this, SLOT(repaintWindow()));
+ reregisterEvents();
+ if(!DISABLE_COMPOSITING){
+ QTimer::singleShot(0,this, SLOT(repaintWindow()));
+ }
}
QImage NativeEmbedWidget::windowImage(QRect geom){
- //Pull the XCB pixmap out of the compositing layer
- xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection());
- xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix);
- if(pix==0){ qDebug() << "Got blank pixmap!"; return QImage(); }
-
- //Convert this pixmap into a QImage
- xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, geom.x(), geom.y(), geom.width(), geom.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
- if(ximg == 0){ qDebug() << "Got blank image!"; return QImage(); }
- QImage img(ximg->data, ximg->width, ximg->height, ximg->stride, QImage::Format_ARGB32_Premultiplied);
- img = img.copy(); //detach this image from the XCB data structures
- xcb_image_destroy(ximg);
+ //if(DISABLE_COMPOSITING){
+ if(!this->isVisible()){ return QImage(); } //nothing to grab yet
+ QList<QScreen*> screens = static_cast<QApplication*>( QApplication::instance() )->screens();
+ //for(int i=0; i<screens.length(); i++){
+ //if(screens[i]->contains(this)){
+ if(!screens.isEmpty()){
+ return screens[0]->grabWindow(WIN->id(), geom.x(), geom.y(), geom.width(), geom.height()).toImage();
+ }
+ //}
+ //}
+ return QImage();
+ /*}else{
+ //Pull the XCB pixmap out of the compositing layer
+ xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection());
+ xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix);
+ if(pix==0){ qDebug() << "Got blank pixmap!"; return QImage(); }
- //Cleanup the XCB data structures
- xcb_free_pixmap(QX11Info::connection(), pix);
+ //Convert this pixmap into a QImage
+ //xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, 0, 0, this->width(), this->height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, geom.x(), geom.y(), geom.width(), geom.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ if(ximg == 0){ qDebug() << "Got blank image!"; return QImage(); }
+ QImage img(ximg->data, ximg->width, ximg->height, ximg->stride, QImage::Format_ARGB32_Premultiplied);
+ img = img.copy(); //detach this image from the XCB data structures before we clean them up, otherwise the QImage will try to clean it up a second time on window close and crash
+ xcb_image_destroy(ximg);
- return img;
+ //Cleanup the XCB data structures
+ xcb_free_pixmap(QX11Info::connection(), pix);
+ return img;
+ }*/
+}
+void NativeEmbedWidget::setWinUnpaused(){
+ paused = false;
+ winImage = QImage();
+ if(!DISABLE_COMPOSITING){
+ repaintWindow(); //update the cached image right away
+ }else if(this->isVisible()){
+ showWindow();
+ }
+ resyncWindow(); //make sure the window knows about the new location
}
-
// ============
// PUBLIC
// ============
NativeEmbedWidget::NativeEmbedWidget(QWidget *parent) : QWidget(parent){
WIN = 0; //nothing embedded yet
paused = false;
+ this->setMouseTracking(true);
//this->setSizeIncrement(2,2);
}
bool NativeEmbedWidget::embedWindow(NativeWindow *window){
WIN = window;
- //PIXBACK = xcb_generate_id(QX11Info::connection());
- xcb_reparent_window(QX11Info::connection(), WIN->id(), this->winId(), 0, 0);
+
//Now send the embed event to the app
//qDebug() << " - send _XEMBED event";
/*xcb_client_message_event_t event;
@@ -115,7 +188,7 @@ bool NativeEmbedWidget::embedWindow(NativeWindow *window){
//Now setup any redirects and return
if(!DISABLE_COMPOSITING){
xcb_composite_redirect_window(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
- xcb_composite_redirect_subwindows(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
+ xcb_composite_redirect_subwindows(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //AUTOMATIC); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
//Now create/register the damage handler
// -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore)
@@ -126,19 +199,28 @@ bool NativeEmbedWidget::embedWindow(NativeWindow *window){
Damage dmgID = XDamageCreate(QX11Info::display(), WIN->id(), XDamageReportRawRectangles);
WIN->addDamageID( (uint) dmgID); //save this for later
+ connect(WIN, SIGNAL(VisualChanged()), this, SLOT(repaintWindow()) ); //make sure we repaint the widget on visual change
+ }else{
+ xcb_reparent_window(QX11Info::connection(), WIN->id(), this->winId(), 0, 0);
+ registerClientEvents(this->winId()); //child events get forwarded through the frame - watch this for changes too
+ //Also use a partial-composite here - make sure the window pixmap is available even when the window is obscured
+ xcb_composite_redirect_window(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_AUTOMATIC);
+ //xcb_composite_redirect_subwindows(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL);
+ //Also alert us when the window visual changes
+ Damage dmgID = XDamageCreate(QX11Info::display(), WIN->id(), XDamageReportRawRectangles);
+
+ WIN->addDamageID( (uint) dmgID); //save this for later
+ connect(WIN, SIGNAL(VisualChanged()), this, SLOT(repaintWindow()) ); //make sure we repaint the widget on visual change
}
WIN->addFrameWinID(this->winId());
- connect(WIN, SIGNAL(VisualChanged()), this, SLOT(repaintWindow()) ); //make sure we repaint the widget on visual change
-
registerClientEvents(WIN->id());
- registerClientEvents(this->winId());
- qDebug() << "Events Registered:" << WIN->id() << this->winId();
+ //qDebug() << "Events Registered:" << WIN->id() << this->winId();
return true;
}
bool NativeEmbedWidget::detachWindow(){
xcb_reparent_window(QX11Info::connection(), WIN->id(), QX11Info::appRootWindow(), -1, -1);
- WIN = 0;
+ //WIN = 0;
return true;
}
@@ -146,69 +228,94 @@ bool NativeEmbedWidget::isEmbedded(){
return (WIN!=0);
}
+void NativeEmbedWidget::raiseWindow(){
+ if(DISABLE_COMPOSITING){ return; }
+ uint32_t val = XCB_STACK_MODE_ABOVE;
+ xcb_configure_window(QX11Info::connection(), WIN->id(), XCB_CONFIG_WINDOW_STACK_MODE, &val);
+}
+
+void NativeEmbedWidget::lowerWindow(){
+ if(DISABLE_COMPOSITING){ return; }
+ uint32_t val = XCB_STACK_MODE_BELOW;
+ xcb_configure_window(QX11Info::connection(), WIN->id(), XCB_CONFIG_WINDOW_STACK_MODE, &val);
+}
+
// ==============
// PUBLIC SLOTS
// ==============
//Pause/resume
void NativeEmbedWidget::pause(){
- if(winImage.isNull()){ repaintWindow(); } //make sure we have one image already cached first
+ if(DISABLE_COMPOSITING){
+ winImage = windowImage(QRect(QPoint(0,0), this->size()));
+ hideWindow();
+ }else{
+ if(winImage.isNull()){ repaintWindow(); } //make sure we have one image already cached first
+ }
paused = true;
}
void NativeEmbedWidget::resume(){
- paused = false;
- //syncWinSize();
- //showWindow();
- repaintWindow(); //update the cached image right away
+ //paused = false;
+ syncWinSize();
+ if(DISABLE_COMPOSITING){
+ //showWindow();
+ }else{
+ repaintWindow(); //update the cached image right away
+ }
+ QTimer::singleShot(10, this, SLOT(setWinUnpaused()) );
}
void NativeEmbedWidget::resyncWindow(){
if(WIN==0){ return; }
- /*return; //skip the stuff below (not working)
- QRect geom = WIN->geometry();
- //Send an artificial configureNotify event to the window with the global position/size included
- xcb_configure_notify_event_t event;
- event.x = geom.x() + this->pos().x();
- event.y = geom.y() + this->pos().y();
- event.width = this->width();
- event.height = this->height();
- event.border_width = 0;
- event.above_sibling = XCB_NONE;
- event.override_redirect = false;
- event.window = WIN->id();
- event.event = WIN->id();
- event.response_type = XCB_CONFIGURE_NOTIFY;
- xcb_send_event(QX11Info::connection(), false, WIN->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (const char *) &event);
- */
- //Just jitter the window size by 1 pixel really quick so the window knows to update it's geometry
- QSize sz = this->size();
- uint32_t valList[2] = {(uint32_t) sz.width()-1, (uint32_t) sz.height()};
- uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
- xcb_configure_window(QX11Info::connection(), WIN->id(), mask, valList);
- xcb_flush(QX11Info::connection());
- valList[0] = (uint32_t) sz.width();
- xcb_configure_window(QX11Info::connection(), WIN->id(), mask, valList);
+ //syncWinSize();
+ //if(DISABLE_COMPOSITING){
+ // Specs say to send an artificial configure event to the window if the window was reparented into the frame
+ QPoint loc = this->mapToGlobal( QPoint(0,0) );
+ //Send an artificial configureNotify event to the window with the global position/size included
+ xcb_configure_notify_event_t *event = (xcb_configure_notify_event_t*) calloc(32,1); //always 32-byes long, even if we don't need all of it
+ event->x = loc.x();
+ event->y = loc.y();
+ event->width = this->width();
+ event->height = this->height();
+ event->border_width = 0;
+ event->above_sibling = XCB_NONE;
+ event->override_redirect = false;
+ event->window = WIN->id();
+ event->event = WIN->id();
+ event->response_type = XCB_CONFIGURE_NOTIFY;
+ xcb_send_event(QX11Info::connection(), false, WIN->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (char *) event);
xcb_flush(QX11Info::connection());
- syncWinSize();
- QTimer::singleShot(10, this, SLOT(repaintWindow()) );
+ free(event);
+ /*}else{
+ //Window is floating invisibly - make sure it is in the right place
+ //Make sure the window size is syncronized and visual up to date
+ //syncWinSize();
+ QTimer::singleShot(10, this, SLOT(repaintWindow()) );
+ }*/
+
}
void NativeEmbedWidget::repaintWindow(){
- if(DISABLE_COMPOSITING){ return; }
+ //if(DISABLE_COMPOSITING){ return; }
//qDebug() << "Update Window Image:" << !paused;
if(paused){ return; }
- QImage tmp = windowImage( QRect(QPoint(0,0), this->size()) );
+ /*QImage tmp = windowImage( QRect(QPoint(0,0), this->size()) );
if(!tmp.isNull()){
winImage = tmp;
- }else{ qDebug() << "Got Null Image!!"; }
- this->parentWidget()->update();
+ }else{ qDebug() << "Got Null Image!!"; }*/
+ this->parentWidget()->update(); //visual changed - need to update the image on the widget
+}
+
+void NativeEmbedWidget::reregisterEvents(){
+ if(WIN!=0){ registerClientEvents(WIN->id()); }
}
+
// ==============
// PROTECTED
// ==============
void NativeEmbedWidget::resizeEvent(QResizeEvent *ev){
QWidget::resizeEvent(ev);
- if(WIN!=0){
+ if(WIN!=0 && !paused){
syncWinSize(ev->size());
} //syncronize the window with the new widget size
}
@@ -224,30 +331,106 @@ void NativeEmbedWidget::hideEvent(QHideEvent *ev){
}
void NativeEmbedWidget::paintEvent(QPaintEvent *ev){
- if(WIN==0 || DISABLE_COMPOSITING){ QWidget::paintEvent(ev); return; }
- else if( winImage.isNull() ){ /*QTimer::singleShot(0, this, SLOT(repaintWindow()) );*/ return; }
- else if(paused){ return; }
- //else if(this->size()!=winSize){ QTimer::singleShot(0,this, SLOT(syncWinSize())); return; } //do not paint here - waiting to re-sync the sizes
- //else if(this->size() != winImage.size()){ QTimer::singleShot(0, this, SLOT(repaintWindow()) ); return; }
- //Need to paint the image from the window onto the widget as an overlay
+ if(WIN==0){ return; }
QRect geom = ev->rect(); //atomic updates
- geom.adjust(-10,-10,10,10); //add an additional few pixels in each direction to be painted
- geom = geom.intersected(QRect(0,0,this->width(), this->height())); //ensure intersection with actual window
- if( !QRect(QPoint(0,0),winImage.size()).contains(geom) ){ QTimer::singleShot(0,this, SLOT(repaintWindow()) );return; }
+ //qDebug() << "Paint Rect:" << geom;
+ //geom.adjust(-10,-10,10,10); //add an additional few pixels in each direction to be painted
+ //geom = geom.intersected(QRect(0,0,this->width(), this->height())); //ensure intersection with actual window
+ QImage img;
+ if(!paused){ img = windowImage(geom); }
+ else if(!winImage.isNull()){
+ if(winImage.size() == this->size()){ img = winImage.copy(geom); }
+ else{ img = winImage.scaled(geom.size()); } //this is a fast transformation - might be slightly distorted
+ }
+ //Need to paint the image from the window onto the widget as an overlay
+
QPainter P(this);
P.setClipping(true);
P.setClipRect(0,0,this->width(), this->height());
+ //if(DISABLE_COMPOSITING){ P.fillRect(geom, Qt::black); } //get weird effects when partial-compositing is enabled if you layer transparent window frames above other windows
//qDebug() << "Paint Embed Window:" << geom << winImage.size();
- if(winImage.size() == this->size()){
- P.drawImage( geom , winImage, geom, Qt::NoOpaqueDetection); //1-to-1 mapping
+ //if(winImage.size() == this->size()){
+ P.drawImage( geom , img, QRect(QPoint(0,0), img.size()), Qt::NoOpaqueDetection); //1-to-1 mapping
//Note: Qt::NoOpaqueDetection Speeds up the paint by bypassing the checks to see if there are [semi-]transparent pixels
// Since this is an embedded image - we fully expect there to be transparency all/most of the time.
- }else{
- P.drawImage( geom , winImage);
- }
+ // }else{
+ //P.drawImage( geom , winImage); //auto-scale it to fit (transforming a static image while paused?)
+ // }
//else{ QImage scaled = winImage.scaled(geom.size()); P.drawImage(geom, scaled); }
//P.drawImage( geom , winImage, geom, Qt::NoOpaqueDetection); //1-to-1 mapping
//Note: Qt::NoOpaqueDetection Speeds up the paint by bypassing the checks to see if there are [semi-]transparent pixels
// Since this is an embedded image - we fully expect there to be transparency all/most of the time.
}
+
+void NativeEmbedWidget::enterEvent(QEvent *ev){
+ QWidget::enterEvent(ev);
+ //qDebug() << "Enter Embed Widget";
+ //raiseWindow(); //this->grabMouse();
+}
+
+void NativeEmbedWidget::leaveEvent(QEvent *ev){
+ QWidget::leaveEvent(ev);
+ /*qDebug() << "Leave Embed Widget";
+ QPoint pt = QCursor::pos();
+ QPoint relpt = this->parentWidget()->mapFromGlobal(pt);
+ qDebug() << " - Geom:" << this->geometry() << "Global pt:" << pt << "Relative pt:" << relpt;
+ if(!this->geometry().contains(relpt) ){ lowerWindow(); }*/
+}
+
+void NativeEmbedWidget::mouseMoveEvent(QMouseEvent *ev){
+ QWidget::mouseMoveEvent(ev);
+ //Forward this event on to the window
+}
+
+void NativeEmbedWidget::mousePressEvent(QMouseEvent *ev){
+ QWidget::mousePressEvent(ev);
+ //Forward this event on to the window
+}
+
+void NativeEmbedWidget::mouseReleaseEvent(QMouseEvent *ev){
+ QWidget::mouseReleaseEvent(ev);
+ //Forward this event on to the window
+}
+
+/*bool NativeEmbedWidget::nativeEvent(const QByteArray &eventType, void *message, long *result){
+ if(eventType=="xcb_generic_event_t" && WIN!=0){
+ //Convert to known event type (for X11 systems)
+ xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
+ //qDebug() << "Got Embed Window Event:" << xcb_event_get_label(ev->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) << xcb_event_get_request_label(ev->response_type);
+ uint32_t mask = 0;
+ switch( ev->response_type & XCB_EVENT_RESPONSE_TYPE_MASK){
+ case XCB_BUTTON_PRESS:
+ //This is a mouse button press
+ mask = XCB_EVENT_MASK_BUTTON_PRESS;
+ break;
+ case XCB_BUTTON_RELEASE:
+ //This is a mouse button release
+ //qDebug() << "Button Release Event";
+ mask = XCB_EVENT_MASK_BUTTON_RELEASE;
+ break;
+ case XCB_MOTION_NOTIFY:
+ //This is a mouse movement event
+ mask = XCB_EVENT_MASK_POINTER_MOTION;
+ break;
+ case XCB_ENTER_NOTIFY:
+ //This is a mouse movement event when mouse goes over a new window
+ mask = XCB_EVENT_MASK_ENTER_WINDOW;
+ break;
+ case XCB_LEAVE_NOTIFY:
+ //This is a mouse movement event when mouse goes leaves a window
+ mask = XCB_EVENT_MASK_LEAVE_WINDOW;
+ break;
+ default:
+ mask = 0;
+ }
+
+ //Now forward this event on to the embedded window
+ if(mask!=0){
+ qDebug() << " - Got a mouse event";
+ xcb_send_event(QX11Info::connection(), true, WIN->id(),mask, (char*) ev);
+ return true;
+ }
+ }
+ return false;
+}*/
diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.h b/src-qt5/core/libLumina/NativeEmbedWidget.h
index 65e03c51..16bb46dc 100644
--- a/src-qt5/core/libLumina/NativeEmbedWidget.h
+++ b/src-qt5/core/libLumina/NativeEmbedWidget.h
@@ -18,6 +18,7 @@
#include <QShowEvent>
#include <QHideEvent>
#include <QPaintEvent>
+#include <QMouseEvent>
class NativeEmbedWidget : public QWidget{
Q_OBJECT
@@ -25,7 +26,7 @@ private:
NativeWindow *WIN;
QSize winSize;
QImage winImage;
- bool paused;
+ bool paused, hasAlphaChannel;
private slots:
//Simplification functions
@@ -35,6 +36,7 @@ private slots:
void showWindow();
QImage windowImage(QRect geom);
+ void setWinUnpaused();
public:
NativeEmbedWidget(QWidget *parent);
@@ -42,22 +44,31 @@ public:
bool embedWindow(NativeWindow *window);
bool detachWindow();
bool isEmbedded(); //status of the embed
-
-
+ bool isPaused(){ return paused; }
public slots:
+ void raiseWindow();
+ void lowerWindow();
+
//Pause/resume
void pause();
void resume();
void resyncWindow();
void repaintWindow();
+ void reregisterEvents();
protected:
void resizeEvent(QResizeEvent *ev);
void showEvent(QShowEvent *ev);
void hideEvent(QHideEvent *ev);
void paintEvent(QPaintEvent *ev);
+ void enterEvent(QEvent *ev);
+ void leaveEvent(QEvent *ev);
+ void mouseMoveEvent(QMouseEvent *ev);
+ void mousePressEvent(QMouseEvent *ev);
+ void mouseReleaseEvent(QMouseEvent *ev);
+ //bool nativeEvent(const QByteArray &eventType, void *message, long *result);
};
#endif
diff --git a/src-qt5/core/libLumina/NativeEventFilter.cpp b/src-qt5/core/libLumina/NativeEventFilter.cpp
index 354dbe76..c13c1fc8 100644
--- a/src-qt5/core/libLumina/NativeEventFilter.cpp
+++ b/src-qt5/core/libLumina/NativeEventFilter.cpp
@@ -65,7 +65,7 @@ static xcb_ewmh_connection_t EWMH;
static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE = 0;
inline void ParsePropertyEvent(xcb_property_notify_event_t *ev, NativeEventFilter *obj){
- qDebug() << "Got Property Event:" << ev->window << ev->atom;
+ //qDebug() << "Got Property Event:" << ev->window << ev->atom;
NativeWindow::Property prop = NativeWindow::None;
//Now determine which properties are getting changed, and update the native window as appropriate
if(ev->atom == EWMH._NET_WM_NAME){ prop = NativeWindow::Title; }
@@ -76,11 +76,14 @@ inline void ParsePropertyEvent(xcb_property_notify_event_t *ev, NativeEventFilte
else if( ev->atom == EWMH._NET_WM_STATE){ prop = NativeWindow::States; }
//Send out the signal if necessary
if(prop!=NativeWindow::None){
- //if(DEBUG){
- qDebug() << "Detected Property Change:" << ev->window << prop;
+ //if(DEBUG){
+ //qDebug() << "Detected Property Change:" << ev->window << prop;
//}
obj->emit WindowPropertyChanged(ev->window, prop);
}else{
+ //Quick re-check of the simple properties (nothing like the icon or other graphics)
+ obj->emit WindowPropertiesChanged(ev->window, QList<NativeWindow::Property>() << NativeWindow::Title
+ << NativeWindow::ShortTitle << NativeWindow::Workspace );
//qDebug() << "Unknown Property Change:" << ev->window << ev->atom;
}
}
@@ -98,7 +101,7 @@ inline void ParseClientMessageEvent(xcb_client_message_event_t *ev, NativeEventF
else if(ev->type==EWMH._NET_WM_STATE){ prop = NativeWindow::States; }
if(prop!=NativeWindow::None){
- //if(DEBUG){
+ //if(DEBUG){
qDebug() << "Detected Property Change Request:" << ev->window << prop; //}
if(val.isNull()){ obj->emit WindowPropertyChanged(ev->window, prop); }
else{ obj->emit RequestWindowPropertyChange(ev->window, prop, val); }
diff --git a/src-qt5/core/libLumina/NativeEventFilter.h b/src-qt5/core/libLumina/NativeEventFilter.h
index 2b184f99..a3be3ef1 100644
--- a/src-qt5/core/libLumina/NativeEventFilter.h
+++ b/src-qt5/core/libLumina/NativeEventFilter.h
@@ -34,6 +34,7 @@ signals:
void WindowCreated(WId);
void WindowDestroyed(WId);
void WindowPropertyChanged(WId, NativeWindow::Property);
+ void WindowPropertiesChanged(WId, QList<NativeWindow::Property>);
void WindowPropertyChanged(WId, NativeWindow::Property, QVariant);
void WindowPropertiesChanged(WId, QList<NativeWindow::Property>, QList<QVariant>);
void RequestWindowPropertyChange(WId, NativeWindow::Property, QVariant);
diff --git a/src-qt5/core/libLumina/NativeWindow.cpp b/src-qt5/core/libLumina/NativeWindow.cpp
index 3c76ed00..02cc001e 100644
--- a/src-qt5/core/libLumina/NativeWindow.cpp
+++ b/src-qt5/core/libLumina/NativeWindow.cpp
@@ -106,6 +106,10 @@ QRect NativeWindow::geometry(){
return geom;
}
// ==== PUBLIC SLOTS ===
+void NativeWindow::toggleVisibility(){
+ setProperty(NativeWindow::Visible, !property(NativeWindow::Visible).toBool() );
+}
+
void NativeWindow::requestClose(){
emit RequestClose(winid);
}
diff --git a/src-qt5/core/libLumina/NativeWindow.h b/src-qt5/core/libLumina/NativeWindow.h
index d04815ce..67436259 100644
--- a/src-qt5/core/libLumina/NativeWindow.h
+++ b/src-qt5/core/libLumina/NativeWindow.h
@@ -78,6 +78,7 @@ public:
QRect geometry(); //this returns the "full" geometry of the window (window + frame)
public slots:
+ void toggleVisibility();
void requestClose(); //ask the app to close the window (may/not depending on activity)
void requestKill(); //ask the WM to kill the app associated with this window (harsh - only use if not responding)
void requestPing(); //ask the app if it is still active (a WindowNotResponding signal will get sent out if there is no reply);
diff --git a/src-qt5/core/libLumina/NativeWindow.pri b/src-qt5/core/libLumina/NativeWindow.pri
index c2ac0137..c906d6fd 100644
--- a/src-qt5/core/libLumina/NativeWindow.pri
+++ b/src-qt5/core/libLumina/NativeWindow.pri
@@ -2,6 +2,7 @@
# Files
QT *= x11extras
LIBS *= -lc -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image -lxcb-composite -lxcb-damage -lxcb-util -lxcb-keysyms -lXdamage
+#QT *= -lxcb-render -lxcb-render-util
SOURCES *= $${PWD}/NativeWindow.cpp \
$${PWD}/NativeWindowSystem.cpp \
diff --git a/src-qt5/core/libLumina/NativeWindowSystem.cpp b/src-qt5/core/libLumina/NativeWindowSystem.cpp
index 71e95a0e..0ee65929 100644
--- a/src-qt5/core/libLumina/NativeWindowSystem.cpp
+++ b/src-qt5/core/libLumina/NativeWindowSystem.cpp
@@ -45,7 +45,6 @@
#define ROOT_WIN_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
XCB_EVENT_MASK_BUTTON_PRESS | \
XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
- XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
XCB_EVENT_MASK_POINTER_MOTION | \
XCB_EVENT_MASK_PROPERTY_CHANGE | \
@@ -60,13 +59,45 @@
XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
- XCB_EVENT_MASK_ENTER_WINDOW| \
- XCB_EVENT_MASK_PROPERTY_CHANGE)
+ XCB_EVENT_MASK_ENTER_WINDOW | \
+ XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_FOCUS_CHANGE)
+
+#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_FOCUS_CHANGE | \
+ XCB_EVENT_MASK_POINTER_MOTION)
+
+#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
-inline void registerClientEvents(WId id){
+inline void registerClientEvents(WId id, bool client = true){
+ uint32_t values[] = {XCB_NONE};
+ values[0] = client ? CLIENT_EVENT_MASK : FRAME_EVENT_MASK ;
+ /*{ (XCB_EVENT_MASK_PROPERTY_CHANGE
+ | XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_POINTER_MOTION
+ | XCB_EVENT_MASK_BUTTON_MOTION
+ | XCB_EVENT_MASK_EXPOSURE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
+ | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_ENTER_WINDOW)
+ };*/
+ xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, values);
+}
+
+/*inline void registerClientEvents(WId id){
uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK};
xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list);
-}
+}*/
//Internal XCB private objects class
class NativeWindowSystem::p_objects{
@@ -255,7 +286,7 @@ NativeWindow* NativeWindowSystem::findWindow(WId id, bool checkRelated){
//qDebug() << "Find Window:" << id;
for(int i=0; i<NWindows.length(); i++){
if(id==NWindows[i]->id() ){ return NWindows[i]; }
- else if(id==NWindows[i]->frameId() ){ qDebug() << "Matched Frame:" << id; return NWindows[i]; }
+ else if(id==NWindows[i]->frameId() ){ return NWindows[i]; }
//if(checkRelated && NWindows[i]->isRelatedTo(id)){ return NWindows[i]; }
//else if(!checkRelated && id==NWindows[i]->id()){ return NWindows[i]; }
}
@@ -479,27 +510,27 @@ void NativeWindowSystem::ChangeWindowProperties(NativeWindow* win, QList< Native
}
if(props.contains(NativeWindow::Size) || props.contains(NativeWindow::GlobalPos) ){
- xcb_configure_window_value_list_t valList;
- valList.x = 0; //Note that this is the relative position - should always be 0,0 relative to the embed widget
- valList.y = 0;
+ /*xcb_configure_window_value_list_t valList;
+ //valList.x = 0; //Note that this is the relative position - should always be 0,0 relative to the embed widget
+ //valList.y = 0;
QSize sz = win->property(NativeWindow::Size).toSize();
if(props.contains(NativeWindow::Size)){
sz = vals[ props.indexOf(NativeWindow::Size) ] .toSize();
}
valList.width = sz.width();
valList.height = sz.height();
- /*if(props.contains(NativeWindow::GlobalPos)){
+ if(props.contains(NativeWindow::GlobalPos)){
QPoint pt = vals[ props.indexOf(NativeWindow::GlobalPos) ] .toPoint();
valList.x = pt.x();
valList.y = pt.y();
}else{
valList.x = win->property(NativeWindow::GlobalPos).toPoint().x();
valList.y = win->property(NativeWindow::GlobalPos).toPoint().y();
- }*/
+ }
uint16_t mask = 0;
- mask = mask | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;// | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
- qDebug() << "Configure window Geometry:" << sz;
- xcb_configure_window_aux(QX11Info::connection(), win->id(), mask, &valList);
+ mask = mask | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
+ //qDebug() << "Configure window Geometry:" << sz;
+ xcb_configure_window_aux(QX11Info::connection(), win->id(), mask, &valList);*/
}
if(props.contains(NativeWindow::Name)){
@@ -524,10 +555,20 @@ void NativeWindowSystem::ChangeWindowProperties(NativeWindow* win, QList< Native
if(props.contains(NativeWindow::Active)){
//Only one window can be "Active" at a time - so only do anything if this window wants to be active
if(vals[props.indexOf(NativeWindow::Active)].toBool() ){
- xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), (win->frameId()==0 ?win->id() : win->frameId()));
+ //Lower the currently active window (invisible window) to the bottom of the stack
+ xcb_window_t cactive;
+ if( 1 == xcb_ewmh_get_active_window_reply( &obj->EWMH,
+ xcb_ewmh_get_active_window_unchecked(&obj->EWMH, QX11Info::appScreen()),
+ &cactive, NULL) ){
+ uint32_t val = XCB_STACK_MODE_BELOW;
+ xcb_configure_window(QX11Info::connection(), cactive, XCB_CONFIG_WINDOW_STACK_MODE, &val);
+ }
+
+ xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), win->id() );
//Also send the active window a message to take input focus
+ xcb_set_input_focus(QX11Info::connection(), XCB_INPUT_FOCUS_PARENT, win->id(), XCB_CURRENT_TIME);
//Send the window a WM_TAKE_FOCUS message
- xcb_client_message_event_t event;
+/* xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.window = win->id();
@@ -540,6 +581,7 @@ void NativeWindowSystem::ChangeWindowProperties(NativeWindow* win, QList< Native
xcb_send_event(QX11Info::connection(), 0, win->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
xcb_flush(QX11Info::connection());
+*/
}
}
@@ -553,6 +595,9 @@ void NativeWindowSystem::RegisterVirtualRoot(WId id){
array[0] = id;
//Set the property
xcb_ewmh_set_virtual_roots(&obj->EWMH, QX11Info::appScreen(), 1, array);
+ //Now also enable automatic compositing for children of this window
+ //xcb_composite_redirect_window(QX11Info::connection(), id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
+ //xcb_composite_redirect_subwindows(QX11Info::connection(), id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
}
void NativeWindowSystem::setRoot_supportedActions(){
@@ -561,7 +606,7 @@ void NativeWindowSystem::setRoot_supportedActions(){
obj->EWMH._NET_WM_ICON,
obj->EWMH._NET_WM_ICON_NAME,
obj->EWMH._NET_WM_DESKTOP,
- obj->ATOMS["_NET_WM_WINDOW_OPACITY"],
+ /*obj->ATOMS["_NET_WM_WINDOW_OPACITY"],*/
/*_NET_WINDOW_TYPE (and all the various types - 15 in total*/
obj->EWMH._NET_WM_WINDOW_TYPE, obj->EWMH._NET_WM_WINDOW_TYPE_DESKTOP, obj->EWMH._NET_WM_WINDOW_TYPE_DOCK,
obj->EWMH._NET_WM_WINDOW_TYPE_TOOLBAR, obj->EWMH._NET_WM_WINDOW_TYPE_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_UTILITY,
@@ -625,7 +670,7 @@ void NativeWindowSystem::setRoot_desktopWorkarea(QList<QRect> list){
}
void NativeWindowSystem::setRoot_activeWindow(WId win){
- xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), win);
+ /*xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), win);
//Also send the active window a message to take input focus
//Send the window a WM_TAKE_FOCUS message
xcb_client_message_event_t event;
@@ -640,7 +685,7 @@ void NativeWindowSystem::setRoot_activeWindow(WId win){
event.data.data32[4] = 0;
xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
- xcb_flush(QX11Info::connection());
+ xcb_flush(QX11Info::connection());*/
}
int NativeWindowSystem::currentWorkspace(){
@@ -656,7 +701,7 @@ int NativeWindowSystem::currentWorkspace(){
//NativeWindowEventFilter interactions
void NativeWindowSystem::NewWindowDetected(WId id){
//Make sure this can be managed first
- if(findWindow(id, false) != 0){ qDebug() << "Window Already Managed!!!!"; findWindow(id,false)->setProperty(NativeWindow::Visible, true, true); return; } //already managed
+ if(findWindow(id, false) != 0){ findWindow(id,false)->setProperty(NativeWindow::Visible, true, true); return; } //already managed
xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), id);
xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL);
if(attr == 0){ return; } //could not get attributes of window
@@ -668,7 +713,7 @@ void NativeWindowSystem::NewWindowDetected(WId id){
registerClientEvents(win->id());
NWindows << win;
UpdateWindowProperties(win, NativeWindow::allProperties());
- qDebug() << "New Window [& associated ID's]:" << win->id() << win->frameId() << win->property(NativeWindow::RelatedWindows);
+ qDebug() << "New Window [& associated ID's]:" << win->id() << win->property(NativeWindow::Name).toString();
//Now setup the connections with this window
connect(win, SIGNAL(RequestClose(WId)), this, SLOT(RequestClose(WId)) );
connect(win, SIGNAL(RequestKill(WId)), this, SLOT(RequestKill(WId)) );
@@ -733,6 +778,27 @@ void NativeWindowSystem::WindowPropertyChanged(WId id, NativeWindow::Property pr
if(win==0){ win = findTrayWindow(id); }
if(win!=0){
UpdateWindowProperties(win, QList<NativeWindow::Property>() << prop);
+ }else if(prop != 0){
+ //Could not find the window for a specific property with an undefined value
+ // - update this property for all the windows just in case
+ for(int i=0; i<NWindows.length(); i++){
+ UpdateWindowProperties( NWindows[i], QList<NativeWindow::Property>() << prop);
+ }
+ }
+}
+
+void NativeWindowSystem::WindowPropertiesChanged(WId id, QList<NativeWindow::Property> props){
+ //NOTE: This is triggered by the NativeEventFilter - not by changes to the NativeWindow objects themselves
+ NativeWindow *win = findWindow(id);
+ if(win==0){ win = findTrayWindow(id); }
+ if(win!=0){
+ UpdateWindowProperties(win, props);
+ }else{
+ //Could not find the window for a specific property with an undefined value
+ // - update this property for all the windows just in case
+ for(int i=0; i<NWindows.length(); i++){
+ UpdateWindowProperties( NWindows[i], props);
+ }
}
}
diff --git a/src-qt5/core/libLumina/NativeWindowSystem.h b/src-qt5/core/libLumina/NativeWindowSystem.h
index 97208c2f..b67ecc94 100644
--- a/src-qt5/core/libLumina/NativeWindowSystem.h
+++ b/src-qt5/core/libLumina/NativeWindowSystem.h
@@ -105,6 +105,7 @@ public slots:
void NewTrayWindowDetected(WId); //will automatically create the new NativeWindow object
void WindowCloseDetected(WId); //will update the lists and make changes if needed
void WindowPropertyChanged(WId, NativeWindow::Property); //will rescan the window and update the object as needed
+ void WindowPropertiesChanged(WId, QList<NativeWindow::Property>);
void WindowPropertyChanged(WId, NativeWindow::Property, QVariant); //will save that property/value to the right object
void WindowPropertiesChanged(WId, QList<NativeWindow::Property>, QList<QVariant>);
void RequestPropertyChange(WId, NativeWindow::Property, QVariant);
diff --git a/src-qt5/core/libLumina/RootSubWindow-animations.cpp b/src-qt5/core/libLumina/RootSubWindow-animations.cpp
index ac813e3a..efab20fe 100644
--- a/src-qt5/core/libLumina/RootSubWindow-animations.cpp
+++ b/src-qt5/core/libLumina/RootSubWindow-animations.cpp
@@ -11,16 +11,20 @@ QStringList RootSubWindow::validAnimations(NativeWindow::Property prop){
QStringList valid;
if(prop == NativeWindow::Visible){
valid << "zoom" << "wipe-center-vertical" << "wipe-center-horizontal" << "shade-top" << "shade-right" << "shade-left" << "shade-bottom";
+ }else if(prop == NativeWindow::Size){
+ //Note: this is used for pretty much all geometry changes to the window where it is visible both before/after animation
+ valid << "direct";
}
return valid;
}
void RootSubWindow::loadAnimation(QString name, NativeWindow::Property prop, QVariant nval){
+ if(anim->state()==QAbstractAnimation::Running){ return; } //already running
animResetProp.clear();
//Special case - random animation each time
if(name=="random"){
QStringList valid = validAnimations(prop);
- name = valid.at(qrand()%valid.length());
+ if(!valid.isEmpty()){ name = valid.at(qrand()%valid.length()); }
}
//Now setup the animation
if(prop == NativeWindow::Visible){
@@ -57,6 +61,7 @@ void RootSubWindow::loadAnimation(QString name, NativeWindow::Property prop, QVa
}
if(nval.toBool()){
this->setGeometry( anim->startValue().toRect() ); //ensure the window is the initial geom before it becomes visible
+ //QTimer::singleShot( anim->duration()+5, this, SLOT(activate()) );
}else{
QVariant tmp = anim->startValue();
anim->setStartValue(anim->endValue());
@@ -68,6 +73,19 @@ void RootSubWindow::loadAnimation(QString name, NativeWindow::Property prop, QVa
anim->start();
this->show();
} //end of Visibility animation
+ else if(prop == NativeWindow::Size){
+ //This is pretty much all geometry animations where the window is visible->visible
+ anim->setPropertyName("geometry");
+ anim->setStartValue(this->geometry());
+ anim->setEndValue(nval.toRect());
+ /*if(name==""){
+ // TO-DO modify the path from beginning->end somehow
+ }*/
+ // Now start the animation
+ WinWidget->pause();
+ anim->start();
+ this->show();
+ }
}
void RootSubWindow::animFinished(){
@@ -87,11 +105,12 @@ void RootSubWindow::animFinished(){
//qDebug() << "Sub Window geometry:" << clientg;
WIN->setProperties(QList< NativeWindow::Property>() << NativeWindow::Size << NativeWindow::GlobalPos,
QList<QVariant>() << clientg.size() << clientg.topLeft() );
- WinWidget->resyncWindow(); //also let the window know about the current geometry
}
}
+ WinWidget->resyncWindow(); //also let the window know about the current geometry
}
animResetProp = QVariant(); //clear the variable
- WinWidget->resume();
-
+ //QTimer::singleShot(10, WinWidget, SLOT(resume()) );
+ WinWidget->resume();
+ emit windowAnimFinished();
}
diff --git a/src-qt5/core/libLumina/RootSubWindow.cpp b/src-qt5/core/libLumina/RootSubWindow.cpp
index 6341f923..5fb8ece4 100644
--- a/src-qt5/core/libLumina/RootSubWindow.cpp
+++ b/src-qt5/core/libLumina/RootSubWindow.cpp
@@ -10,6 +10,7 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTimer>
+#include <QScreen>
#define WIN_BORDER 5
@@ -56,7 +57,7 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset
if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner
return ResizeTopLeft;
}else if(pt.x() > (this->width()*4.0/5.0)){
- if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(pt.y()); } //difference from top-right corner
+ if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()); } //difference from top-right corner
return ResizeTopRight;
}else{
if(setoffset){ offset.setX(0); offset.setY(pt.y()); } //difference from top edge (X does not matter)
@@ -65,13 +66,13 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset
}else if(pt.y() > (this->height()-WIN_BORDER) ){
//One of the bottom options
if(pt.x() < this->width()/5){
- if(setoffset){ offset.setX(pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-left corner
+ if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()-this->height()); } //difference from bottom-left corner
return ResizeBottomLeft;
}else if(pt.x() > (this->width()*4.0/5.0)){
- if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner
+ if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()-this->height()); } //difference from bottom-right corner
return ResizeBottomRight;
}else{
- if(setoffset){ offset.setX(0); offset.setY(this->height() - pt.y()); } //difference from bottom edge (X does not matter)
+ if(setoffset){ offset.setX(0); offset.setY(pt.y()-this->height()); } //difference from bottom edge (X does not matter)
return ResizeBottom;
}
}else if(pt.x() < WIN_BORDER){
@@ -80,7 +81,7 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset
if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner
return ResizeTopLeft;
}else if(pt.y() > (this->height()*4.0/5.0)){
- if(setoffset){ offset.setX(pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-left corner
+ if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()-this->height()); } //difference from bottom-left corner
return ResizeBottomLeft;
}else{
if(setoffset){ offset.setX(pt.x()); offset.setY(0); } //difference from left edge (Y does not matter)
@@ -89,13 +90,13 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset
}else if(pt.x() > (this->width()-WIN_BORDER) ){
//Right side options
if(pt.y() < this->height()/5){
- if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(pt.y()); } //difference from top-right corner
+ if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()); } //difference from top-right corner
return ResizeTopRight;
}else if(pt.y() > (this->height()*4.0/5.0)){
- if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner
+ if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()-this->height()); } //difference from bottom-right corner
return ResizeBottomRight;
}else{
- if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(0); } //difference from right edge (Y does not matter)
+ if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(0); } //difference from right edge (Y does not matter)
return ResizeRight;
}
}else{
@@ -186,12 +187,14 @@ void RootSubWindow::initWindowFrame(){
titleBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
mainLayout->addWidget(titleBar);
mainLayout->addWidget(WinWidget);
+ mainLayout->setAlignment(titleBar, Qt::AlignTop);
//Setup the cursors for the buttons
closeB->setCursor(Qt::ArrowCursor);
minB->setCursor(Qt::ArrowCursor);
maxB->setCursor(Qt::ArrowCursor);
- otherM->setCursor(Qt::ArrowCursor);
+ otherB->setCursor(Qt::ArrowCursor);
titleLabel->setCursor(Qt::ArrowCursor);
+ WinWidget->setCursor(Qt::ArrowCursor);
//Now all the stylesheet options
this->setObjectName("WindowFrame");
closeB->setObjectName("Button_Close");
@@ -277,12 +280,44 @@ void RootSubWindow::LoadAllProperties(){
//Button Actions - public so they can be tied to key shortcuts and stuff as well
void RootSubWindow::toggleMinimize(){
- WIN->setProperty(NativeWindow::Visible, false);
- QTimer::singleShot(2000, this, SLOT(toggleMaximize()) );
+ WIN->toggleVisibility();
}
void RootSubWindow::toggleMaximize(){
- WIN->setProperty(NativeWindow::Visible, true);
+ //Get the current screen that this window is on
+ QList<QScreen*> screens = QApplication::screens();
+ QRect rect;
+ int primaryscreen = 0; //fallback value
+ for(int i=0; i<screens.length(); i++){
+ QRect intersect = screens[i]->geometry().intersected(this->geometry());
+ if( (intersect.width()-rect.width() + intersect.height()-rect.height()) > 0){
+ rect = intersect;
+ primaryscreen = i;
+ }
+ }
+ //Now that we have the screen dimensions, lets check/change the window
+ rect = screens[primaryscreen]->availableGeometry();
+ QList< NativeWindow::State > states = WIN->property(NativeWindow::States).value< QList< NativeWindow::State> >();
+ if(rect == this->geometry() || states.contains(NativeWindow::S_MAX_VERT) || states.contains(NativeWindow::S_MAX_HORZ)){
+ //Already maximized - try to restore it to the previous size/location
+ if(!lastMaxGeom.isNull()){
+ rect = lastMaxGeom;
+ }else{
+ // no last geometry - started out maximized?
+ // make it half the screen size and centered on the screen
+ QPoint center = rect.center();
+ rect.setWidth( rect.width()/2 );
+ rect.setHeight( rect.height()/2 );
+ rect.moveTopLeft( center - QPoint(rect.width()/2, rect.height()/2) );
+ }
+ lastMaxGeom = QRect(); //clear this saved geom
+ }else{
+ //Not maximized yet - go ahead and make it so
+ lastMaxGeom = this->geometry(); //save this for later;
+ }
+ //qDebug() << "Toggle Maximize:" << this->geometry() << rect;
+ QString anim_type = DesktopSettings::instance()->value(DesktopSettings::Animation, "window/move", "random").toString();
+ loadAnimation(anim_type, NativeWindow::Size, rect);
}
void RootSubWindow::triggerClose(){
@@ -290,11 +325,18 @@ void RootSubWindow::triggerClose(){
}
void RootSubWindow::toggleSticky(){
-
+ QList< NativeWindow::State> states = WIN->property(NativeWindow::States).value< QList< NativeWindow::State > >();
+ if(states.contains(NativeWindow::S_STICKY)){
+ states.removeAll(NativeWindow::S_STICKY);
+ }else{
+ states << NativeWindow::S_STICKY;
+ }
+ WIN->requestProperty(NativeWindow::States, QVariant::fromValue<QList <NativeWindow::State> >(states) );
}
void RootSubWindow::activate(){
- WIN->requestProperty(NativeWindow::Active, true);
+ //WinWidget->raiseWindow();
+ WIN->requestProperty(NativeWindow::Active, true, true);
}
//Mouse Interactivity
@@ -309,13 +351,15 @@ void RootSubWindow::startMoving(){
activeState = Move;
offset = this->mapFromGlobal(curpt);
setMouseCursor(activeState, true); //this one is an override cursor
- //WinWidget->pause();
- //Also need to capture the mouse
+ WinWidget->pause();
this->grabMouse();
}
void RootSubWindow::startResizing(){
-
+ activeState = getStateAtPoint( this->mapFromGlobal(QCursor::pos()), true); //also have it set the offset variable
+ setMouseCursor(activeState, true); //this one is an override cursor
+ WinWidget->pause();
+ this->grabMouse();
}
// === PRIVATE SLOTS ===
@@ -325,9 +369,11 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
//qDebug() << "RootSubWindow: Property Changed:" << props[i] << vals[i];
switch(props[i]){
case NativeWindow::Visible:
- //qDebug() << "Got Visibility Change:" << vals[i] << this->geometry() << WIN->geometry();
- if(vals[i].toBool()){ loadAnimation( DesktopSettings::instance()->value(DesktopSettings::Animation, "window/appear", "random").toString(), NativeWindow::Visible, vals[i]); }
- else{ loadAnimation( DesktopSettings::instance()->value(DesktopSettings::Animation, "window/disappear", "random").toString(), NativeWindow::Visible, vals[i]); }
+ if(!WinWidget->isPaused() && (this->isVisible()!=vals[i].toBool()) && activeState==Normal ){
+ qDebug() << "Got Visibility Change:" << vals[i] << this->geometry() << WIN->geometry();
+ if(vals[i].toBool()){ loadAnimation( DesktopSettings::instance()->value(DesktopSettings::Animation, "window/appear", "random").toString(), NativeWindow::Visible, vals[i]); }
+ else{ loadAnimation( DesktopSettings::instance()->value(DesktopSettings::Animation, "window/disappear", "random").toString(), NativeWindow::Visible, vals[i]); }
+ }
break;
case NativeWindow::Title:
titleLabel->setText(vals[i].toString());
@@ -338,6 +384,10 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
else{ otherB->setIcon(vals[i].value<QIcon>()); }
break;
case NativeWindow::GlobalPos:
+ if(vals[i].toPoint()!=QPoint(0,0)){
+ WinWidget->resyncWindow();
+ }
+ break;
case NativeWindow::Size:
//qDebug() << " - SIZE CHANGE";
if(WIN->property(NativeWindow::FrameExtents).isNull() && (i<props.indexOf(NativeWindow::FrameExtents)) ){
@@ -345,9 +395,11 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
props << props.takeAt(i);
vals << vals.takeAt(i);
i--;
- }else if(anim->state() != QPropertyAnimation::Running ){
- if(WIN->property(NativeWindow::Size).toSize() != WinWidget->size() && activeState==Normal ){
- this->setGeometry(WIN->geometry());
+ }else if(!WinWidget->isPaused() && activeState==Normal){
+ if(WIN->property(NativeWindow::Size).toSize() != WinWidget->size()){
+ qDebug() << "Got Direct Geometry Change:" << WIN->geometry();
+ this->setGeometry( QRect(this->geometry().topLeft(), WIN->geometry().size()) );
+ WinWidget->resyncWindow();
}
}
break;
@@ -370,7 +422,7 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
WinWidget->setMaximumSize(vals[i].toSize());
break;
case NativeWindow::Active:
- //if(vals[i].toBool()){ WinWidget->setFocus(); }
+ if(vals[i].toBool()){ activate(); } //WinWidget->raiseWindow(); }
break;
/*case NativeWindow::FrameExtents:
qDebug() << " - FRAME CHANGE";
@@ -382,6 +434,7 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
mainLayout->setContentsMargins( vals[i].value< QList<int> >().at(0),vals[i].value< QList<int> >().at(2) - titleLabel->height(),vals[i].value< QList<int> >().at(1),vals[i].value< QList<int> >().at(3));
break;*/
case NativeWindow::WinTypes:
+ qDebug() << "Got Window Types:" << vals[i].value< QList<NativeWindow::Type> >();
enableFrame(vals[i].value< QList<NativeWindow::Type> >().contains(NativeWindow::T_NORMAL) );
break;
default:
@@ -394,27 +447,24 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
void RootSubWindow::mousePressEvent(QMouseEvent *ev){
activate();
this->raise();
+ QFrame::mousePressEvent(ev);
//qDebug() << "Frame Mouse Press Event";
- offset.setX(0); offset.setY(0);
if(activeState != Normal){ return; } // do nothing - already in a state of grabbed mouse
- //this->activate();
- if(this->childAt(ev->pos())!=0){
- //Check for any non-left-click event and skip it
- if(ev->button()!=Qt::LeftButton){ return; }
- activeState = Move;
- offset.setX(ev->pos().x()); offset.setY(ev->pos().y());
- }else{
- //Clicked on the frame somewhere
- activeState = getStateAtPoint(ev->pos(), true); //also have it set the offset variable
+ offset.setX(0); offset.setY(0);
+ if(ev->button()==Qt::LeftButton){
+ if(this->childAt(ev->pos())!=0){
+ //Clicked on the titlebar
+ startMoving();
+ }else{
+ //Clicked on the frame somewhere
+ startResizing();
+ }
}
- setMouseCursor(activeState, true); //this one is an override cursor
- //if(activeState!=Normal){WinWidget->pause(); }
- if(activeState!=Normal && activeState!=Move){WinWidget->pause(); }
- QFrame::mousePressEvent(ev);
+
}
void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){
- activate(); //make sure this window is "Active"
+ QFrame::mouseMoveEvent(ev);
if(activeState == Normal){
setMouseCursor( getStateAtPoint(ev->pos()) ); //just update the mouse cursor
}else{
@@ -489,10 +539,15 @@ void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){
break;
}
//if( (geom.width()%2==0 && geom.height()%2==0) || activeState==Move){
- this->setGeometry(geom);
+ //qDebug() << " Change Window:" << this->geometry() << geom;
+ if(activeState==Move){ this->setGeometry(geom); }
+ else{
+ //qDebug() << " Change Window Dimensions:" << this->geometry() << geom;
+ //qDebug() << " - Mouse Pos:" << ev->globalPos() << ev->pos() << "Offset" << offset;
+ this->setGeometry(geom);
+ }
//}
}
- QFrame::mouseMoveEvent(ev);
}
void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){
@@ -500,28 +555,37 @@ void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){
//qDebug() << "Frame Mouse Release Event";
QFrame::mouseReleaseEvent(ev);
if( (activeState==Normal) && (titleBar->geometry().contains(ev->pos())) && (ev->button()==Qt::RightButton) ){
+ //WinWidget->raiseWindow();//need to ensure the native window is always on top of this frame but under the menu
otherM->popup(ev->globalPos());
return;
}
- if(activeState!=Normal){ WinWidget->resume(); }
- if(activeState!=Normal && activeState!=Move){WinWidget->resume(); }
- activeState = Normal;
- QApplication::restoreOverrideCursor();
- setMouseCursor( getStateAtPoint(ev->pos()) );
+ if(activeState!=Normal){
+ if(WinWidget->isPaused()){ WinWidget->resume(); }
+ activeState = Normal;
+ QApplication::restoreOverrideCursor();
+ setMouseCursor( getStateAtPoint(ev->pos()) );
+ }
if(QFrame::mouseGrabber() == this){ this->releaseMouse(); }
+ activate();
+ //QTimer::singleShot(0, WinWidget, SLOT(raiseWindow()) );
}
-void RootSubWindow::leaveEvent(QEvent *ev){
+/*void RootSubWindow::enterEvent(QEvent *ev){
+ QFrame::enterEvent(ev);
+ WinWidget->raiseWindow();
+}*/
+/*void RootSubWindow::leaveEvent(QEvent *ev){
QFrame::leaveEvent(ev);
if(activeState == Normal){
setMouseCursor(Normal);
}
-}
+ if(!QRect(QPoint(0,0),this->size()).contains( this->mapFromGlobal(QCursor::pos())) ){ WinWidget->lowerWindow(); }
+}*/
void RootSubWindow::moveEvent(QMoveEvent *ev){
//qDebug() << "Got Move Event:" << ev->pos() << WinWidget->geometry();
QFrame::moveEvent(ev);
- if(!closing && anim->state()!=QAbstractAnimation::Running){
+ if(!closing && !WinWidget->isPaused()){
moveTimer->start();
}
}
diff --git a/src-qt5/core/libLumina/RootSubWindow.h b/src-qt5/core/libLumina/RootSubWindow.h
index 0af77009..c1964724 100644
--- a/src-qt5/core/libLumina/RootSubWindow.h
+++ b/src-qt5/core/libLumina/RootSubWindow.h
@@ -55,7 +55,7 @@ private:
QPropertyAnimation *anim;
QVariant animResetProp;
QTimer *moveTimer;
- QRect lastGeom; //frame coordinates
+ QRect lastGeom, lastMaxGeom; //frame coordinates
void initWindowFrame();
void enableFrame(bool);
@@ -65,6 +65,10 @@ private:
static QStringList validAnimations(NativeWindow::Property);
public slots:
+ void giveMouseFocus(){ WinWidget->raiseWindow(); }
+ void removeMouseFocus(){ WinWidget->lowerWindow(); }
+ void giveKeyboardFocus(){ WIN->requestProperty(NativeWindow::Active, true, true); }
+
void clientClosed();
void LoadAllProperties();
@@ -91,11 +95,13 @@ protected:
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
- void leaveEvent(QEvent *ev);
-
+ //void leaveEvent(QEvent *ev);
+ //void enterEvent(QEvent *ev);
void moveEvent(QMoveEvent *ev);
-
+signals:
+ void windowMoved(RootSubWindow*);
+ void windowAnimFinished();
};
#endif
diff --git a/src-qt5/core/libLumina/RootWindow-mgmt.cpp b/src-qt5/core/libLumina/RootWindow-mgmt.cpp
index 00b3e336..24ea639b 100644
--- a/src-qt5/core/libLumina/RootWindow-mgmt.cpp
+++ b/src-qt5/core/libLumina/RootWindow-mgmt.cpp
@@ -7,13 +7,59 @@
#include "RootWindow.h"
//Primary/private function
-void RootWindow::arrangeWindows(RootSubWindow *primary, QString type){
+void RootWindow::arrangeWindows(RootSubWindow *primary, QString type, bool primaryonly){
+ if(type.isEmpty()){ type = "center"; }
if(primary==0){
//Get the currently active window and treat that as the primary
-
+ for(int i=0; i<WINDOWS.length(); i++){
+ if(WINDOWS[i]->nativeWindow()->property(NativeWindow::Active).toBool()){ primary = WINDOWS[i]; }
+ }
+ if(primary==0 && !WINDOWS.isEmpty()){ primary = WINDOWS[0]; } //just use the first one in the list
+ }
+ //Now get the current screen that the mouse cursor is over (and valid area)
+ QScreen *screen = screenUnderMouse();
+ QRect desktopArea = screen->availableGeometry();
+ //qDebug() << "Arrange Windows:" << primary->geometry() << type << primaryonly << desktopArea;
+ //Now start filtering out all the windows that need to be ignored
+ int wkspace = primary->nativeWindow()->property(NativeWindow::Workspace).toInt();
+ QList<RootSubWindow*> winlist = WINDOWS;
+ for(int i=0; i<winlist.length(); i++){
+ if(winlist[i]->nativeWindow()->property(NativeWindow::Workspace).toInt()!=wkspace
+ || !winlist[i]->nativeWindow()->property(NativeWindow::Visible).toBool()
+ || desktopArea.intersected(winlist[i]->geometry()).isNull() ){
+ //window is outside of the desired area or invisible - ignore it
+ winlist.removeAt(i);
+ i--;
+ }
}
- //Now loop over the windows and arrange them as needed
+ if(!winlist.contains(primary)){ winlist << primary; } //could be doing this on a window right before it is shown
+ else if(primaryonly){ winlist.removeAll(primary); winlist << primary; } //move primary window to last
+ //QRegion used;
+ for(int i=0; i<winlist.length(); i++){
+ if(primaryonly && winlist[i]!=primary){ continue; } //skip this window
+ //Now loop over the windows and arrange them as needed
+ QRect geom = winlist[i]->geometry();
+ //verify that the window is contained by the desktop area
+ if(geom.width()>desktopArea.width()){ geom.setWidth(desktopArea.width()); }
+ if(geom.height()>desktopArea.height()){ geom.setHeight(desktopArea.height()); }
+ //Now apply the proper placement routine
+ if(type=="center"){
+ QPoint ct = desktopArea.center();
+ winlist[i]->setGeometry( ct.x()-(geom.width()/2), ct.y()-(geom.height()/2), geom.width(), geom.height());
+ }else if(type=="snap"){
+
+ }else if(type=="single_max"){
+ winlist[i]->setGeometry( desktopArea.x(), desktopArea.y(), desktopArea.width(), desktopArea.height());
+ }else if(type=="under-mouse"){
+ QPoint ct = QCursor::pos();
+ geom = QRect(ct.x()-(geom.width()/2), ct.y()-(geom.height()/2), geom.width(), geom.height() );
+ //Now verify that the top of the window is still contained within the desktop area
+ if(geom.y() < desktopArea.y() ){ geom.moveTop(desktopArea.y()); }
+ winlist[i]->setGeometry(geom);
+ }
+ //qDebug() << " - New Geometry:" << winlist[i]->geometry();
+ } //end loop over winlist
}
// ================
@@ -21,13 +67,13 @@ void RootWindow::arrangeWindows(RootSubWindow *primary, QString type){
// ================
void RootWindow::ArrangeWindows(WId primary, QString type){
RootSubWindow* win = windowForId(primary);
- if(type.isEmpty()){ type = ""; } //grab the default arrangement format
+ if(type.isEmpty()){ type = "center"; } //grab the default arrangement format
arrangeWindows(win, type);
}
void RootWindow::TileWindows(WId primary, QString type){
RootSubWindow* win = windowForId(primary);
- if(type.isEmpty()){ type = ""; } //grab the default arrangement format for tiling
+ if(type.isEmpty()){ type = "single_max"; } //grab the default arrangement format for tiling
arrangeWindows(win, type);
}
@@ -45,5 +91,5 @@ void RootWindow::CheckWindowPosition(WId id, bool newwindow){
if(geom.height() < 20){ changed = true; geom.setHeight(100); }
if(changed){ win->setGeometry(geom); }
//Now run it through the window arrangement routine
- ArrangeWindows(id);
+ arrangeWindows(win, newwindow ?"center" : "snap", true);
}
diff --git a/src-qt5/core/libLumina/RootWindow.cpp b/src-qt5/core/libLumina/RootWindow.cpp
index 48c37c86..fdbc1eb8 100644
--- a/src-qt5/core/libLumina/RootWindow.cpp
+++ b/src-qt5/core/libLumina/RootWindow.cpp
@@ -10,12 +10,14 @@
#include <QScreen>
#include <QDebug>
-#define DEBUG 1
+#define DEBUG 0
// === PUBLIC ===
RootWindow::RootWindow() : QWidget(0, Qt::Window | Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint){
qRegisterMetaType<WId>("WId");
autoResizeTimer = 0;
+ lastActiveMouse = 0;
+ mouseFocusTimer = 0;
this->setMouseTracking(true);
}
@@ -33,6 +35,12 @@ void RootWindow::start(){
connect(QApplication::desktop(), SIGNAL(resized(int)), autoResizeTimer, SLOT(start()) );
connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), autoResizeTimer, SLOT(start()) );
}
+ if(mouseFocusTimer==0){
+ mouseFocusTimer = new QTimer(this);
+ mouseFocusTimer->setInterval(100);
+ connect(mouseFocusTimer, SIGNAL(timeout()), this, SLOT(checkMouseFocus()) );
+
+ }
this->show();
ResizeRoot();
emit RegisterVirtualRoot(this->winId());
@@ -108,6 +116,16 @@ RootSubWindow* RootWindow::windowForId(WId id){
return tmp;
}
+QScreen* RootWindow::screenUnderMouse(){
+ QPoint mpos = QCursor::pos();
+ QList<QScreen*> scrns = QApplication::screens();
+ for(int i=0; i<scrns.length(); i++){
+ if(scrns[i]->geometry().contains(mpos)){ return scrns[i]; }
+ }
+ //Could not find an exact match - just return the first one
+ return scrns.first();
+}
+
// === PUBLIC SLOTS ===
void RootWindow::ResizeRoot(){
if(DEBUG){ qDebug() << "Resize Root..."; }
@@ -182,6 +200,32 @@ void RootWindow::ChangeWallpaper(QString id, RootWindow::ScaleType scale, QStrin
}
+void RootWindow::checkMouseFocus(){
+ QPoint cpos = QCursor::pos();
+ if(lastCursorPos != cpos){ emit MouseMoved(); }
+ lastCursorPos = cpos;
+ QWidget *child = this->childAt(QCursor::pos());
+ while(child!=0 && child->whatsThis()!="RootSubWindow"){
+ child = child->parentWidget();
+ if(child==this){ child = 0;} //end of the line
+ }
+
+ if(child==lastActiveMouse){ return; } //nothing new to do
+ //Make sure the child is actually a RootSubWindow
+ if(lastActiveMouse!=0){ lastActiveMouse->removeMouseFocus(); lastActiveMouse = 0; }
+ if(child!=0){
+ lastActiveMouse = static_cast<RootSubWindow*>(child);
+
+ if(DesktopSettings::instance()->value(DesktopSettings::WM, "focusFollowsMouse", true).toBool()){
+ lastActiveMouse->giveKeyboardFocus();
+ if(DesktopSettings::instance()->value(DesktopSettings::WM, "raiseOnFocus", false).toBool()){
+ lastActiveMouse->raise();
+ }
+ }
+ lastActiveMouse->giveMouseFocus(); //always give mouse focus on mouseover
+ }
+}
+
void RootWindow::NewWindow(NativeWindow *win){
RootSubWindow *subwin = 0;
//qDebug() << "Got New Window:" << win->property(NativeWindow::Title);
@@ -190,19 +234,27 @@ void RootWindow::NewWindow(NativeWindow *win){
}
if(subwin==0){
subwin = new RootSubWindow(this, win);
+ subwin->setWhatsThis("RootSubWindow");
connect(win, SIGNAL(WindowClosed(WId)), this, SLOT(CloseWindow(WId)) );
+ connect(subwin, SIGNAL(windowAnimFinished()), this, SLOT(checkMouseFocus()) );
WINDOWS << subwin;
}
CheckWindowPosition(win->id(), true); //first-time run
- //win->setProperty(NativeWindow::Visible, true);
+ win->setProperty(NativeWindow::Visible, true);
//win->requestProperty( NativeWindow::Active, true);
- win->requestProperties(QList<NativeWindow::Property>() << NativeWindow::Visible << NativeWindow::Active, QList<QVariant>() << true << true);
+ //win->requestProperties(QList<NativeWindow::Property>() << NativeWindow::Visible << NativeWindow::Active, QList<QVariant>() << true << true, true);
+ if(!mouseFocusTimer->isActive()){ mouseFocusTimer->start(); }
}
void RootWindow::CloseWindow(WId win){
for(int i=0; i<WINDOWS.length(); i++){
- if(WINDOWS[i]->id() == win){ WINDOWS.takeAt(i)->clientClosed(); break; }
+ if(WINDOWS[i]->id() == win){
+ if(lastActiveMouse==WINDOWS[i]){ lastActiveMouse = 0; } //no longer valid
+ WINDOWS.takeAt(i)->clientClosed();
+ break;
+ }
}
+ if(WINDOWS.isEmpty()){ mouseFocusTimer->stop(); } //no windows to look for
}
// === PRIVATE SLOTS ===
@@ -210,11 +262,11 @@ void RootWindow::CloseWindow(WId win){
// === PROTECTED ===
void RootWindow::paintEvent(QPaintEvent *ev){
//qDebug() << "RootWindow: PaintEvent:" << ev->rect(); //<< QDateTime::currentDateTime()->toString(QDateTime::ShortDate);
- QWidget::paintEvent(ev);
+ //QWidget::paintEvent(ev);
bool found = false;
QPainter painter(this);
QRect geom = ev->rect();
- geom.adjust(-10,-10,10,10); //give it a few more pixels in each direction to repaint (noticing some issues in Qt 5.7.1)
+ geom.adjust(-100,-100,100,100); //give it a few more pixels in each direction to repaint (noticing some issues in Qt 5.7.1)
for(int i=0; i<WALLPAPERS.length(); i++){
if(WALLPAPERS[i].area.intersects(geom) ){
found = true;
diff --git a/src-qt5/core/libLumina/RootWindow.h b/src-qt5/core/libLumina/RootWindow.h
index 9b1334dc..c5cd44a0 100644
--- a/src-qt5/core/libLumina/RootWindow.h
+++ b/src-qt5/core/libLumina/RootWindow.h
@@ -18,6 +18,8 @@
#include <QTimer>
#include <QApplication>
#include <QPaintEvent>
+#include <QScreen>
+#include <QDebug>
#include "RootSubWindow.h"
@@ -43,7 +45,9 @@ private:
ScaleType scale;
QPixmap wallpaper; //Note: This pixmap will always be the same size as "area"
};
- QTimer *autoResizeTimer;
+ QTimer *autoResizeTimer, *mouseFocusTimer;
+ RootSubWindow *lastActiveMouse;
+ QPoint lastCursorPos;
QList<screeninfo> WALLPAPERS;
void updateScreenPixmap(screeninfo *info); //used for recalculating the wallpaper pixmap based on file/area/scale as needed
@@ -51,12 +55,16 @@ private:
//Window Management
QList<RootSubWindow*> WINDOWS;
RootSubWindow* windowForId(WId id);
- void arrangeWindows(RootSubWindow *primary = 0, QString type = "");
+ void arrangeWindows(RootSubWindow *primary = 0, QString type = "", bool primaryonly = false);
+
+ QScreen* screenUnderMouse();
+
public slots:
void ResizeRoot();
void ChangeWallpaper(QString id, RootWindow::ScaleType scale, QString file);
//Note: for "SingleColor" scaling the "file" variable should be "rgb(R,G,B)" or "#hexcode"
+ void checkMouseFocus();
void NewWindow(NativeWindow*);
void CloseWindow(WId); //automatically connected for any new native window
@@ -76,6 +84,8 @@ signals:
void RootResized(QRect);
void NewScreens(QStringList); // [screen_id_1, screen_id_2, etc..]
void RemovedScreens(QStringList); // [screen_id_1, screen_id_2, etc..]
+ void WorkspaceChanged(int);
+ void MouseMoved();
};
diff --git a/src-qt5/core/libLumina/XDGMime.cpp b/src-qt5/core/libLumina/XDGMime.cpp
index 3983f6b5..cbbeff75 100644
--- a/src-qt5/core/libLumina/XDGMime.cpp
+++ b/src-qt5/core/libLumina/XDGMime.cpp
@@ -12,6 +12,9 @@ static QStringList mimeglobs;
static qint64 mimechecktime;
QString XDGMime::fromFileName(QString filename){
+ if(QFile::exists(filename) && QFileInfo(filename).isDir()){
+ return "inode/directory";
+ }
//Convert a filename into a mimetype
return findAppMimeForFile(filename.section("/",-1),false);
}
bgstack15