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.cpp2
-rw-r--r--src-qt5/core/libLumina/DesktopSettings.h2
-rw-r--r--src-qt5/core/libLumina/ExternalProcess.h6
-rw-r--r--src-qt5/core/libLumina/NativeEmbedWidget.cpp83
-rw-r--r--src-qt5/core/libLumina/NativeEmbedWidget.h8
-rw-r--r--src-qt5/core/libLumina/NativeWindow.cpp15
-rw-r--r--src-qt5/core/libLumina/RootSubWindow-animations.cpp97
-rw-r--r--src-qt5/core/libLumina/RootSubWindow.cpp87
-rw-r--r--src-qt5/core/libLumina/RootSubWindow.h7
-rw-r--r--src-qt5/core/libLumina/RootWindow-mgmt.cpp34
-rw-r--r--src-qt5/core/libLumina/RootWindow.cpp18
-rw-r--r--src-qt5/core/libLumina/RootWindow.h11
-rw-r--r--src-qt5/core/libLumina/RootWindow.pri5
-rw-r--r--src-qt5/core/libLumina/XDGMime.cpp362
-rw-r--r--src-qt5/core/libLumina/XDGMime.h57
-rw-r--r--src-qt5/core/libLumina/XDGMime.pri10
16 files changed, 718 insertions, 86 deletions
diff --git a/src-qt5/core/libLumina/DesktopSettings.cpp b/src-qt5/core/libLumina/DesktopSettings.cpp
index 47dc29de..8bda1ac5 100644
--- a/src-qt5/core/libLumina/DesktopSettings.cpp
+++ b/src-qt5/core/libLumina/DesktopSettings.cpp
@@ -238,6 +238,8 @@ QString DesktopSettings::rel_path(DesktopSettings::File file){
name="keys"; break;
case DesktopSettings::Theme:
name="theme"; break;
+ case DesktopSettings::Animation:
+ name="animations"; break;
}
return FILEPREFIX+name+".conf";
}
diff --git a/src-qt5/core/libLumina/DesktopSettings.h b/src-qt5/core/libLumina/DesktopSettings.h
index 57a85791..dcb10bb6 100644
--- a/src-qt5/core/libLumina/DesktopSettings.h
+++ b/src-qt5/core/libLumina/DesktopSettings.h
@@ -25,7 +25,7 @@
class DesktopSettings : public QObject{
Q_OBJECT
public:
- enum File{ System, Favorites, Environment, Session, Desktop, ContextMenu, Keys, Theme };
+ enum File{ System, Favorites, Environment, Session, Desktop, ContextMenu, Keys, Theme, Animation };
DesktopSettings(QObject *parent = 0);
~DesktopSettings();
diff --git a/src-qt5/core/libLumina/ExternalProcess.h b/src-qt5/core/libLumina/ExternalProcess.h
index a8a5e605..b1e56af8 100644
--- a/src-qt5/core/libLumina/ExternalProcess.h
+++ b/src-qt5/core/libLumina/ExternalProcess.h
@@ -44,7 +44,7 @@ private slots:
}
public:
- ExternalProcess(QString logfile = "", bool manageCursors = true) : QProcess(){
+ ExternalProcess(QString logfile = "", bool manageCursors = false) : QProcess(){
this->setProcessChannelMode(QProcess::MergedChannels);
cursorRestored = !manageCursors;
if(logfile.isEmpty()){
@@ -62,9 +62,9 @@ public:
}*/
}
- static void launch(QString program, QStringList args = QStringList()){
+ static void launch(QString program, QStringList args = QStringList(), bool manageCursors = false){
//Quick launch of a process with logging disabled and automatic cleanup
- ExternalProcess *tmp = new ExternalProcess();
+ ExternalProcess *tmp = new ExternalProcess("", manageCursors);
if(args.isEmpty()){ tmp->start(program); }
else{ tmp->start(program, args); }
}
diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.cpp b/src-qt5/core/libLumina/NativeEmbedWidget.cpp
index 80c843e0..ed0fd89c 100644
--- a/src-qt5/core/libLumina/NativeEmbedWidget.cpp
+++ b/src-qt5/core/libLumina/NativeEmbedWidget.cpp
@@ -37,8 +37,8 @@ inline void registerClientEvents(WId id){
// ============
//Simplification functions for the XCB/XLib interactions
void NativeEmbedWidget::syncWinSize(QSize sz){
- if(WIN==0){ return; }
- if(!sz.isValid()){ sz = this->size(); } //use the current widget size
+ 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()};
@@ -58,9 +58,11 @@ void NativeEmbedWidget::hideWindow(){
void NativeEmbedWidget::showWindow(){
xcb_map_window(QX11Info::connection(), WIN->id());
+ QTimer::singleShot(0,this, SLOT(repaintWindow()));
}
QImage NativeEmbedWidget::windowImage(QRect geom){
+ //if(paused){ return QImage(); }
//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);
@@ -85,7 +87,8 @@ QImage NativeEmbedWidget::windowImage(QRect geom){
// ============
NativeEmbedWidget::NativeEmbedWidget(QWidget *parent) : QWidget(parent){
WIN = 0; //nothing embedded yet
- this->setSizeIncrement(2,2);
+ paused = false;
+ //this->setSizeIncrement(2,2);
}
bool NativeEmbedWidget::embedWindow(NativeWindow *window){
@@ -141,9 +144,22 @@ bool NativeEmbedWidget::isEmbedded(){
// ==============
// PUBLIC SLOTS
// ==============
+//Pause/resume
+void NativeEmbedWidget::pause(){
+ 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
+}
+
void NativeEmbedWidget::resyncWindow(){
if(WIN==0){ return; }
- return; //skip the stuff below (not working)
+ /*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;
@@ -158,12 +174,28 @@ void NativeEmbedWidget::resyncWindow(){
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);
-
- xcb_flush(QX11Info::connection());
+ */
+ //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);
+ xcb_flush(QX11Info::connection());
+ syncWinSize();
+ QTimer::singleShot(0, this, SLOT(repaintWindow()) );
}
void NativeEmbedWidget::repaintWindow(){
- this->update();
+ //qDebug() << "Update Window Image:" << !paused;
+ if(paused){ return; }
+ QImage tmp = windowImage( QRect(QPoint(0,0), this->size()) );
+ if(!tmp.isNull()){
+ winImage = tmp;
+ }//else{ qDebug() << "Got Null Image!!"; }
+ this->parentWidget()->update();
}
// ==============
// PROTECTED
@@ -186,23 +218,30 @@ void NativeEmbedWidget::hideEvent(QHideEvent *ev){
}
void NativeEmbedWidget::paintEvent(QPaintEvent *ev){
- //QWidget::paintEvent(ev); //ensure all the Qt-compositing is done first
- if(this->size()!=winSize){ return; } //do not paint here - waiting to re-sync the sizes
if(WIN==0){ 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
- QRect geom = QRect(0,0,this->width(), this->height()); //always paint the whole window
- //qDebug() << "Get Paint image:" << ev->rect() << geom;
- //geom = ev->rect(); //atomic updates
- //geom.adjust(-1,-1,1,1); //add an additional pixel in each direction to be painted
- //geom = geom.intersected(QRect(0,0,this->width(), this->height())); //ensure intersection with actual window
- QImage img = windowImage(geom);
- if(!img.isNull()){
- if(img.size() != geom.size()){ 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; }
QPainter P(this);
- P.drawImage( geom , img, QRect(geom.topLeft(), img.size()), Qt::NoOpaqueDetection); //1-to-1 mapping
- //qDebug() << "Painted Rect:" << ev->rect() << this->geometry();
+ P.setClipping(true);
+ P.setClipRect(0,0,this->width(), this->height());
+ //qDebug() << "Paint Embed Window:" << geom << winImage.size();
+ if(winImage.size() == this->size()){
+ 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.
+ }else{
+ P.drawImage( geom , winImage);
+ }
+ //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 most of the time.
- }
- //qDebug() << "Done Painting";
+ // Since this is an embedded image - we fully expect there to be transparency all/most of the time.
+
}
diff --git a/src-qt5/core/libLumina/NativeEmbedWidget.h b/src-qt5/core/libLumina/NativeEmbedWidget.h
index 78c11dfc..65e03c51 100644
--- a/src-qt5/core/libLumina/NativeEmbedWidget.h
+++ b/src-qt5/core/libLumina/NativeEmbedWidget.h
@@ -24,6 +24,8 @@ class NativeEmbedWidget : public QWidget{
private:
NativeWindow *WIN;
QSize winSize;
+ QImage winImage;
+ bool paused;
private slots:
//Simplification functions
@@ -41,7 +43,13 @@ public:
bool detachWindow();
bool isEmbedded(); //status of the embed
+
+
public slots:
+ //Pause/resume
+ void pause();
+ void resume();
+
void resyncWindow();
void repaintWindow();
diff --git a/src-qt5/core/libLumina/NativeWindow.cpp b/src-qt5/core/libLumina/NativeWindow.cpp
index 94d39cb7..3c76ed00 100644
--- a/src-qt5/core/libLumina/NativeWindow.cpp
+++ b/src-qt5/core/libLumina/NativeWindow.cpp
@@ -86,14 +86,23 @@ void NativeWindow::requestProperties(QList<NativeWindow::Property> props, QList<
QRect NativeWindow::geometry(){
//Calculate the "full" geometry of the window + frame (if any)
- QRect geom( hash.value(NativeWindow::GlobalPos).toPoint(), hash.value(NativeWindow::Size).toSize() );
+ //Check that the size is between the min/max limitations
+ QSize size = hash.value(NativeWindow::Size).toSize();
+ QSize min = hash.value(NativeWindow::MinSize).toSize();
+ QSize max = hash.value(NativeWindow::MaxSize).toSize();
+ if(min.isValid() && min.width() > size.width() ){ size.setWidth(min.width()); }
+ if(min.isValid() && min.height() > size.height()){ size.setHeight(min.height()); }
+ if(max.isValid() && max.width() < size.width() && max.width()>min.width()){ size.setWidth(max.width()); }
+ if(max.isValid() && max.height() < size.height() && max.height()>min.height()){ size.setHeight(max.height()); }
+ //Assemble the full geometry
+ QRect geom( hash.value(NativeWindow::GlobalPos).toPoint(), size );
//Now adjust the window geom by the frame margins
QList<int> frame = hash.value(NativeWindow::FrameExtents).value< QList<int> >(); //Left,Right,Top,Bottom
- qDebug() << "Calculate Geometry:" << geom << frame;
+ //qDebug() << "Calculate Geometry:" << geom << frame;
if(frame.length()==4){
geom = geom.adjusted( -frame[0], -frame[2], frame[1], frame[3] );
}
- qDebug() << " - Total:" << geom;
+ //qDebug() << " - Total:" << geom;
return geom;
}
// ==== PUBLIC SLOTS ===
diff --git a/src-qt5/core/libLumina/RootSubWindow-animations.cpp b/src-qt5/core/libLumina/RootSubWindow-animations.cpp
new file mode 100644
index 00000000..ac813e3a
--- /dev/null
+++ b/src-qt5/core/libLumina/RootSubWindow-animations.cpp
@@ -0,0 +1,97 @@
+//===========================================
+// Lumina Desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "RootSubWindow.h"
+#include <QDebug>
+
+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";
+ }
+ return valid;
+}
+
+void RootSubWindow::loadAnimation(QString name, NativeWindow::Property prop, QVariant nval){
+ animResetProp.clear();
+ //Special case - random animation each time
+ if(name=="random"){
+ QStringList valid = validAnimations(prop);
+ name = valid.at(qrand()%valid.length());
+ }
+ //Now setup the animation
+ if(prop == NativeWindow::Visible){
+ //NOTE: Assigns values for "invisible->visible" animation: will reverse it afterwards as needed
+ anim->setPropertyName("geometry");
+ QRect geom = this->geometry();
+ if(name == "zoom"){
+ //Zoom to/from the center point
+ anim->setStartValue( QRect(geom.center(), QSize(0,0)) );
+ anim->setEndValue(geom);
+ }else if(name == "wipe-center-vertical"){
+ anim->setStartValue( QRect( geom.center().x(), geom.y(), 0, geom.height()) );
+ anim->setEndValue( geom );
+ }else if(name == "wipe-center-horizontal"){
+ anim->setStartValue( QRect( geom.x(), geom.center().y(), geom.width(), 0) );
+ anim->setEndValue( geom );
+ }else if(name == "shade-top"){
+ anim->setStartValue( QRect( geom.x(), geom.y(), geom.width(), 0) );
+ anim->setEndValue( geom );
+ }else if(name == "shade-bottom"){
+ anim->setStartValue( QRect( geom.x(), geom.y()+geom.height(), geom.width(), 0) );
+ anim->setEndValue( geom );
+ }else if(name == "shade-left"){
+ anim->setStartValue( QRect( geom.x(), geom.y(), 0, geom.height()) );
+ anim->setEndValue( geom );
+ }else if(name == "shade-right"){
+ anim->setStartValue( QRect( geom.x()+geom.width(), geom.y(), 0, geom.height()) );
+ anim->setEndValue( geom );
+ }else{
+ //Invalid/None animation
+ if(nval.toBool()){ this->show(); }
+ else{ this->hide(); }
+ return;
+ }
+ if(nval.toBool()){
+ this->setGeometry( anim->startValue().toRect() ); //ensure the window is the initial geom before it becomes visible
+ }else{
+ QVariant tmp = anim->startValue();
+ anim->setStartValue(anim->endValue());
+ anim->setEndValue(tmp);
+ animResetProp = anim->startValue();
+ QTimer::singleShot(anim->duration(), this, SLOT(hide()) );
+ }
+ WinWidget->pause();
+ anim->start();
+ this->show();
+ } //end of Visibility animation
+}
+
+void RootSubWindow::animFinished(){
+ if(closing){ this->close(); return;}
+ else if(anim->propertyName()=="geometry"){
+ if(!animResetProp.isNull()){
+ /*qDebug() << "Animation Finished, Reset Geometry:" << animResetProp.toRect();
+ qDebug() << " - Starting Value:" << anim->startValue().toRect();
+ qDebug() << " - Ending Value:" << anim->endValue().toRect();*/
+ this->setGeometry( animResetProp.toRect() );
+ //Also ensure that the proper geometry is saved to the window structure
+ QRect curg = this->geometry();
+ QRect wing = WIN->geometry();
+ //qDebug() << " - After Animation Reset:" << curg << wing;
+ if(curg!=wing){
+ QRect clientg = clientGlobalGeom();
+ //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
+ }
+ }
+ }
+ animResetProp = QVariant(); //clear the variable
+ WinWidget->resume();
+
+}
diff --git a/src-qt5/core/libLumina/RootSubWindow.cpp b/src-qt5/core/libLumina/RootSubWindow.cpp
index 9ef6464e..322e524f 100644
--- a/src-qt5/core/libLumina/RootSubWindow.cpp
+++ b/src-qt5/core/libLumina/RootSubWindow.cpp
@@ -14,6 +14,7 @@
#define WIN_BORDER 5
#include <LIconCache.h>
+#include <DesktopSettings.h>
// === PUBLIC ===
RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){
@@ -246,6 +247,14 @@ void RootSubWindow::LoadProperties( QList< NativeWindow::Property> list){
propertiesChanged(list, vals);
}
+QRect RootSubWindow::clientGlobalGeom(){
+ QRect tot = this->geometry();
+ QList<int> frame = WIN->property(NativeWindow::FrameExtents).value< QList<int> >();
+ //Now adjust this to take out the frame
+ tot.adjust(frame[0], frame[2], -frame[1], -frame[3]);
+ return tot;
+}
+
// === PUBLIC SLOTS ===
void RootSubWindow::clientClosed(){
//qDebug() << "Client Closed";
@@ -257,9 +266,9 @@ void RootSubWindow::LoadAllProperties(){
QList< NativeWindow::Property> list;
list << NativeWindow::WinTypes << NativeWindow::WinActions << NativeWindow::States
<< NativeWindow::MinSize << NativeWindow::MaxSize << NativeWindow::Title << NativeWindow::ShortTitle
- << NativeWindow::Icon << NativeWindow::Size << NativeWindow::GlobalPos << NativeWindow::Visible << NativeWindow::Active;
+ << NativeWindow::Icon << NativeWindow::Size << NativeWindow::GlobalPos;// << NativeWindow::Visible << NativeWindow::Active;
LoadProperties(list);
- WIN->requestProperty(NativeWindow::Visible, true);
+ //WIN->requestProperty(NativeWindow::Visible, true);
}
//Button Actions - public so they can be tied to key shortcuts and stuff as well
@@ -296,6 +305,7 @@ 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
this->grabMouse();
}
@@ -312,24 +322,8 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
switch(props[i]){
case NativeWindow::Visible:
//qDebug() << "Got Visibility Change:" << vals[i] << this->geometry() << WIN->geometry();
- if(vals[i].toBool()){
- if(lastGeom.isNull()){ animResetProp = this->geometry(); }
- else{ animResetProp = lastGeom; }
- anim->setPropertyName("geometry");
- anim->setStartValue( QRect(animResetProp.toRect().center(), QSize(0,0)) );
- anim->setEndValue(animResetProp);
- this->setGeometry( anim->startValue().toRect() ); //ensure the window is the initial geom before it becomes visible
- anim->start();
- this->show();
- }else{
- animResetProp = this->geometry(); //hide event - should already be the right geom
- lastGeom = this->geometry();
- anim->setPropertyName("geometry");
- anim->setStartValue(this->geometry());
- anim->setEndValue( QRect(this->geometry().center(), QSize(0,0) ) );
- anim->start();
- QTimer::singleShot(anim->duration(), this, SLOT(hide()) );
- }
+ 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());
@@ -340,11 +334,6 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
else{ otherB->setIcon(vals[i].value<QIcon>()); }
break;
case NativeWindow::GlobalPos:
- //qDebug() << "Got Global Pos:" << this->pos() << WinWidget->mapToGlobal(QPoint(0,0)) << WIN->geometry().topLeft() << vals[i].toPoint();
- if(activeState == RootSubWindow::Normal){
- this->move( WIN->geometry().topLeft() );
- }
- break;
case NativeWindow::Size:
//qDebug() << " - SIZE CHANGE";
if(WIN->property(NativeWindow::FrameExtents).isNull() && (i<props.indexOf(NativeWindow::FrameExtents)) ){
@@ -353,16 +342,25 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
vals << vals.takeAt(i);
i--;
}else if(anim->state() != QPropertyAnimation::Running ){
- if(vals[i].toSize() != WinWidget->size() && activeState==Normal){
- //qDebug() << "Got Widget Size Change:" << vals[i].toSize() << WinWidget->size();
- WinWidget->resize(vals[i].toSize());
- this->resize( WIN->geometry().size() );
- //qDebug() << " - Size after change:" << WinWidget->size() << this->size() << WIN->geometry();
+ if(WIN->property(NativeWindow::Size).toSize() != WinWidget->size() && activeState==Normal ){
+ this->setGeometry(WIN->geometry());
}
}
break;
case NativeWindow::MinSize:
- WinWidget->setMinimumSize(vals[i].toSize());
+ if(vals[i].toSize().isValid()){
+ //Just larger than titlebar, with enough space for 8 characters in the titlebar (+4 buttons)
+ //qDebug() << "Got invalid Min Size: Set a reasonable default minimum";
+ WinWidget->setMinimumSize( QSize( this->fontMetrics().height()*4 + this->fontMetrics().width("O")*10, this->fontMetrics().height()*10) );
+ WIN->setProperty(NativeWindow::MinSize, WinWidget->minimumSize());
+ }else{
+ WinWidget->setMinimumSize(vals[i].toSize());
+ }
+ if(WIN->property(NativeWindow::Size).toSize().width() < WinWidget->minimumSize().width() \
+ || WIN->property(NativeWindow::Size).toSize().height() < WinWidget->minimumSize().height() ){
+ WIN->setProperty(NativeWindow::Size, WinWidget->minimumSize(), true); //force this
+ //WinWidget->resize(WinWidget->minimumSize());
+ }
break;
case NativeWindow::MaxSize:
WinWidget->setMaximumSize(vals[i].toSize());
@@ -388,20 +386,6 @@ void RootSubWindow::propertiesChanged(QList<NativeWindow::Property> props, QList
}
}
-void RootSubWindow::animFinished(){
- if(closing){ this->close(); return;}
- else if(anim->propertyName()=="geometry"){
- if(!animResetProp.isNull()){
- /*qDebug() << "Animation Finished, Reset Geometry:" << animResetProp;
- qDebug() << " - Starting Value:" << anim->startValue();
- qDebug() << " - Ending Value:" << anim->endValue();
- qDebug() << " - Current Value:" << this->geometry();*/
- this->setGeometry( animResetProp.toRect() );
- }
- }
- animResetProp = QVariant(); //clear the variable
-}
-
// === PROTECTED ===
void RootSubWindow::mousePressEvent(QMouseEvent *ev){
activate();
@@ -409,7 +393,7 @@ void RootSubWindow::mousePressEvent(QMouseEvent *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();
+ //this->activate();
if(this->childAt(ev->pos())!=0){
//Check for any non-left-click event and skip it
if(ev->button()!=Qt::LeftButton){ return; }
@@ -420,6 +404,8 @@ void RootSubWindow::mousePressEvent(QMouseEvent *ev){
activeState = getStateAtPoint(ev->pos(), true); //also have it set the offset variable
}
setMouseCursor(activeState, true); //this one is an override cursor
+ //if(activeState!=Normal){WinWidget->pause(); }
+ if(activeState!=Normal && activeState!=Move){WinWidget->pause(); }
QFrame::mousePressEvent(ev);
}
@@ -498,7 +484,9 @@ void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){
default:
break;
}
- this->setGeometry(geom);
+ //if( (geom.width()%2==0 && geom.height()%2==0) || activeState==Move){
+ this->setGeometry(geom);
+ //}
}
QFrame::mouseMoveEvent(ev);
}
@@ -511,6 +499,8 @@ void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){
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()) );
@@ -529,8 +519,5 @@ void RootSubWindow::moveEvent(QMoveEvent *ev){
QFrame::moveEvent(ev);
if(!closing && anim->state()!=QAbstractAnimation::Running){
moveTimer->start();
- //WinWidget->resyncWindow();
- //WIN->requestProperty(NativeWindow::GlobalPos, ev->pos() );
}
-
}
diff --git a/src-qt5/core/libLumina/RootSubWindow.h b/src-qt5/core/libLumina/RootSubWindow.h
index de6aba89..079a6201 100644
--- a/src-qt5/core/libLumina/RootSubWindow.h
+++ b/src-qt5/core/libLumina/RootSubWindow.h
@@ -39,6 +39,7 @@ private:
//Functions for getting/setting state
ModState getStateAtPoint(QPoint pt, bool setoffset = false); //generally used for mouse location detection
void setMouseCursor(ModState, bool override = false); //Update the mouse cursor based on state
+
//Native window embed objects
NativeWindow *WIN;
NativeEmbedWidget *WinWidget;
@@ -60,10 +61,14 @@ private:
void LoadProperties( QList< NativeWindow::Property> list);
+ static QStringList validAnimations(NativeWindow::Property);
+
public slots:
void clientClosed();
void LoadAllProperties();
+ QRect clientGlobalGeom();
+
//Button Actions - public so they can be tied to key shortcuts and stuff as well
void toggleMinimize();
void toggleMaximize();
@@ -77,6 +82,8 @@ public slots:
private slots:
void propertiesChanged(QList<NativeWindow::Property>, QList<QVariant>);
+
+ void loadAnimation(QString name, NativeWindow::Property, QVariant nval); //new val
void animFinished();
protected:
diff --git a/src-qt5/core/libLumina/RootWindow-mgmt.cpp b/src-qt5/core/libLumina/RootWindow-mgmt.cpp
new file mode 100644
index 00000000..e00ce9ce
--- /dev/null
+++ b/src-qt5/core/libLumina/RootWindow-mgmt.cpp
@@ -0,0 +1,34 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2016, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "RootWindow.h"
+
+//Primary/private function
+void RootWindow::arrangeWindows(RootSubWindow *primary, QString type){
+ if(primary==0){
+ //Get the currently active window and treat that as the primary
+
+ }
+ //Now loop over the windows and arrange them as needed
+
+}
+
+// ================
+// Public slots for starting the arrangement routine(s) above
+// ================
+void RootWindow::ArrangeWindows(WId *primary, QString type){
+
+}
+
+void RootWindow::TileWindows(WId *primary, QString type){
+
+}
+
+void RootWindow::CheckWindowPosition(WId, bool newwindow){
+ //used after a "drop" to validate/snap/re-arrange window(s) as needed
+ // if "newwindow" is true, then this is the first-placement routine for a window before it initially appears
+
+}
diff --git a/src-qt5/core/libLumina/RootWindow.cpp b/src-qt5/core/libLumina/RootWindow.cpp
index 31faaf50..596755df 100644
--- a/src-qt5/core/libLumina/RootWindow.cpp
+++ b/src-qt5/core/libLumina/RootWindow.cpp
@@ -10,6 +10,8 @@
#include <QScreen>
#include <QDebug>
+#define DEBUG 1
+
// === PUBLIC ===
RootWindow::RootWindow() : QWidget(0, Qt::Window | Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint){
qRegisterMetaType<WId>("WId");
@@ -100,12 +102,14 @@ void RootWindow::updateScreenPixmap(screeninfo *info){
// === PUBLIC SLOTS ===
void RootWindow::ResizeRoot(){
+ if(DEBUG){ qDebug() << "Resize Root..."; }
QList<QScreen*> scrns = QApplication::screens();
//Update all the screen locations and ID's in the WALLPAPERS list
QRect fullscreen;
QStringList valid;
//Update the size of the rootWindow itself
for(int i=0; i<scrns.length(); i++){
+ if(DEBUG){ qDebug() << " - Found Screen:" << scrns[i]->name() << scrns[i]->geometry(); }
fullscreen = fullscreen.united(scrns[i]->geometry());
valid << scrns[i]->name();
for(int j=0; j<WALLPAPERS.length(); j++){
@@ -129,11 +133,13 @@ void RootWindow::ResizeRoot(){
}
}
//Trigger a repaint and send out any signals
+ if(DEBUG){ qDebug() << " - FullScreen Geometry:" << fullscreen; }
this->setGeometry(fullscreen);
this->update();
emit RootResized(fullscreen);
if(!valid.isEmpty()){ emit NewScreens(valid); }
if(!invalid.isEmpty()){ emit RemovedScreens(invalid); }
+ if(DEBUG){ qDebug() << " - Geom after change:" << this->geometry(); }
}
void RootWindow::ChangeWallpaper(QString id, RootWindow::ScaleType scale, QString file){
@@ -148,6 +154,7 @@ void RootWindow::ChangeWallpaper(QString id, RootWindow::ScaleType scale, QStrin
}
}
if(!found){
+ ResizeRoot();
//Need to create a new screeninfo structure
QList<QScreen*> scrns = QApplication::screens();
for(int i=0; i<scrns.length(); i++){
@@ -169,7 +176,7 @@ void RootWindow::ChangeWallpaper(QString id, RootWindow::ScaleType scale, QStrin
void RootWindow::NewWindow(NativeWindow *win){
RootSubWindow *subwin = 0;
- qDebug() << "Got New Window:" << win->property(NativeWindow::Title);
+ //qDebug() << "Got New Window:" << win->property(NativeWindow::Title);
for(int i=0; i<WINDOWS.length() && subwin==0; i++){
if(WINDOWS[i]->id() == win->id()){ subwin = WINDOWS[i]; }
}
@@ -178,6 +185,7 @@ void RootWindow::NewWindow(NativeWindow *win){
connect(win, SIGNAL(WindowClosed(WId)), this, SLOT(CloseWindow(WId)) );
WINDOWS << subwin;
}
+ CheckWindowPosition(win->id(), true); //first-time run
//win->setProperty(NativeWindow::Visible, true);
//win->requestProperty( NativeWindow::Active, true);
win->requestProperties(QList<NativeWindow::Property>() << NativeWindow::Visible << NativeWindow::Active, QList<QVariant>() << true << true);
@@ -185,7 +193,7 @@ void RootWindow::NewWindow(NativeWindow *win){
void RootWindow::CloseWindow(WId win){
for(int i=0; i<WINDOWS.length(); i++){
- if(WINDOWS[i]->id() == win){ qDebug() << "Remove Window From Root List"; WINDOWS.takeAt(i)->clientClosed(); break; }
+ if(WINDOWS[i]->id() == win){ WINDOWS.takeAt(i)->clientClosed(); break; }
}
}
@@ -196,10 +204,12 @@ void RootWindow::paintEvent(QPaintEvent *ev){
//qDebug() << "RootWindow: PaintEvent:" << ev->rect(); //<< QDateTime::currentDateTime()->toString(QDateTime::ShortDate);
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)
for(int i=0; i<WALLPAPERS.length(); i++){
- if(WALLPAPERS[i].area.intersects(ev->rect()) ){
+ if(WALLPAPERS[i].area.intersects(geom) ){
found = true;
- QRect intersect = WALLPAPERS[i].area.intersected(ev->rect());
+ QRect intersect = WALLPAPERS[i].area.intersected(geom);
painter.drawPixmap( intersect, WALLPAPERS[i].wallpaper, intersect.translated(-WALLPAPERS[i].area.x(), -WALLPAPERS[i].area.y()) );
}
}
diff --git a/src-qt5/core/libLumina/RootWindow.h b/src-qt5/core/libLumina/RootWindow.h
index a7792752..2fd76cea 100644
--- a/src-qt5/core/libLumina/RootWindow.h
+++ b/src-qt5/core/libLumina/RootWindow.h
@@ -18,10 +18,11 @@
#include <QTimer>
#include <QApplication>
#include <QPaintEvent>
-//#include <QMdiArea>
#include "RootSubWindow.h"
-#include "NativeWindow.h"
+
+#include <NativeWindow.h>
+#include <DesktopSettings.h>
class RootWindow : public QWidget{
Q_OBJECT
@@ -49,6 +50,7 @@ private:
//Window Management
QList<RootSubWindow*> WINDOWS;
+ void arrangeWindows(RootSubWindow *primary = 0, QString type = "");
public slots:
void ResizeRoot();
@@ -58,6 +60,11 @@ public slots:
void NewWindow(NativeWindow*);
void CloseWindow(WId); //automatically connected for any new native window
+ //Window arrangement functions - defined in "RootWindow-mgmt.cpp"
+ void ArrangeWindows(WId *primary = 0, QString type = "");
+ void TileWindows(WId *primary = 0, QString type = "");
+ void CheckWindowPosition(WId, bool newwindow = false); //used after a "drop" to validate/snap/re-arrange window(s) as needed
+
private slots:
protected:
diff --git a/src-qt5/core/libLumina/RootWindow.pri b/src-qt5/core/libLumina/RootWindow.pri
index e4d5f00b..9426b6b4 100644
--- a/src-qt5/core/libLumina/RootWindow.pri
+++ b/src-qt5/core/libLumina/RootWindow.pri
@@ -1,7 +1,9 @@
# Files
SOURCES *= $${PWD}/RootWindow.cpp \
- $${PWD}/RootSubWindow.cpp
+ $${PWD}/RootWindow-mgmt.cpp \
+ $${PWD}/RootSubWindow.cpp \
+ $${PWD}/RootSubWindow-animations.cpp
HEADERS *= $${PWD}/RootWindow.h \
$${PWD}/RootSubWindow.h
@@ -12,3 +14,4 @@ INCLUDEPATH *= ${PWD}
include(LUtils.pri)
include(NativeWindow.pri)
include(LIconCache.pri)
+include(DesktopSettings.pri)
diff --git a/src-qt5/core/libLumina/XDGMime.cpp b/src-qt5/core/libLumina/XDGMime.cpp
new file mode 100644
index 00000000..3983f6b5
--- /dev/null
+++ b/src-qt5/core/libLumina/XDGMime.cpp
@@ -0,0 +1,362 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2013-2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "XDGMime.h"
+#include <LUtils.h>
+#include <LuminaOS.h>
+
+static QStringList mimeglobs;
+static qint64 mimechecktime;
+
+QString XDGMime::fromFileName(QString filename){
+ //Convert a filename into a mimetype
+ return findAppMimeForFile(filename.section("/",-1),false);
+}
+
+QStringList XDGMime::listFromFileName(QString filename){
+ //Convert a filename into a list of mimetypes (arranged in descending priority)
+ return findAppMimeForFile(filename.section("/",-1),true).split("::::");
+}
+QString XDGMime::toIconName(QString mime){
+ if(!mime.contains("/") || mime.isEmpty() ){ return "unknown"; } //not a mime type
+ //Mime type to icon name
+ mime.replace("/","-"); //translate to icon mime name
+ return mime;
+}
+
+
+QStringList XDGMime::toFileExtensions(QString mime){
+ QStringList out;
+ QStringList mimes = XDGMime::loadMimeFileGlobs2().filter(mime);
+ for(int i=0; i<mimes.length(); i++){
+ out << mimes[i].section(":",2,2); // "*.<extension>"
+ }
+ //qDebug() << "Mime to Files:" << mime << out;
+ return out;
+}
+
+// ====================
+// BACKEND FUNCTIONS
+// ====================
+QStringList XDGMime::systemMimeDirs(){
+ //Returns a list of all the directories where *.xml MIME files can be found
+ QStringList appDirs = QString(getenv("XDG_DATA_HOME")).split(":");
+ appDirs << QString(getenv("XDG_DATA_DIRS")).split(":");
+ if(appDirs.isEmpty()){ appDirs << "/usr/local/share" << "/usr/share"; }
+ //Now create a valid list
+ QStringList out;
+ for(int i=0; i<appDirs.length(); i++){
+ if( QFile::exists(appDirs[i]+"/mime") ){
+ out << appDirs[i]+"/mime";
+ }
+ }
+ return out;
+}
+
+QString XDGMime::findAppMimeForFile(QString filename, bool multiple){
+ QString out;
+ QString extension = filename.section(".",1,-1);
+ if("."+extension == filename){ extension.clear(); } //hidden file without extension
+ //qDebug() << "MIME SEARCH:" << filename << extension;
+ QStringList mimefull = XDGMime::loadMimeFileGlobs2();
+ QStringList mimes;
+ //Just in case the filename is a mimetype itself
+ if( mimefull.filter(":"+filename+":").length() == 1){
+ return filename;
+ }
+while(mimes.isEmpty()){
+ //Check for an exact mimetype match
+ if(mimefull.filter(":"+extension+":").length() == 1){
+ return extension;
+ }
+ //Look for globs at the end of the filename
+ if(!extension.isEmpty()){
+ mimes = mimefull.filter(":*."+extension);
+ //If nothing found, try a case-insensitive search
+ if(mimes.isEmpty()){ mimes = mimefull.filter(":*."+extension, Qt::CaseInsensitive); }
+ //Now ensure that the filter was accurate (*.<extention>.<something> will still be caught)
+ for(int i=0; i<mimes.length(); i++){
+ if(!filename.endsWith( mimes[i].section(":*",-1), Qt::CaseInsensitive )){ mimes.removeAt(i); i--; }
+ else if(mimes[i].section(":",0,0).length()==2){ mimes[i].prepend("0"); } //ensure 3-character priority number
+ else if(mimes[i].section(":",0,0).length()==1){ mimes[i].prepend("00"); } //ensure 3-character priority number
+ }
+ }
+ //Look for globs at the start of the filename
+ if(mimes.isEmpty()){
+ mimes = mimefull.filter(":"+filename.left(2)); //look for the first 2 characters initially
+ //Note: This initial filter will only work if the wildcard (*) is not within the first 2 characters of the pattern
+ //Now ensure that the filter was accurate
+ for(int i=0; i<mimes.length(); i++){
+ if(!filename.startsWith( mimes[i].section(":",3,-1,QString::SectionSkipEmpty).section("*",0,0), Qt::CaseInsensitive )){ mimes.removeAt(i); i--; }
+ }
+ }
+ if(mimes.isEmpty()){
+ if(extension.contains(".")){ extension = extension.section(".",1,-1); }
+ else{ break; }
+ }
+ } //end of mimes while loop
+ mimes.sort(); //this automatically puts them in reverse weight order (100 on down)
+ QStringList matches;
+ //qDebug() << "Mimes:" << mimes;
+ for(int m=mimes.length()-1; m>=0; m--){
+ QString mime = mimes[m].section(":",1,1,QString::SectionSkipEmpty);
+ matches << mime;
+ }
+ //qDebug() << "Matches:" << matches;
+ if(multiple && !matches.isEmpty() ){ out = matches.join("::::"); }
+ else if( !matches.isEmpty() ){ out = matches.first(); }
+ else{ //no mimetype found - assign one (internal only - no system database changes)
+ if(extension.isEmpty()){ out = "unknown/"+filename.toLower(); }
+ else{ out = "unknown/"+extension.toLower(); }
+ }
+ //qDebug() << "Out:" << out;
+ return out;
+}
+
+QStringList XDGMime::listFileMimeDefaults(){
+ //This will spit out a itemized list of all the mimetypes and relevant info
+ // Output format: <mimetype>::::<extension>::::<default>::::<localized comment>
+ QStringList mimes = XDGMime::loadMimeFileGlobs2();
+ //Now start filling the output list
+ QStringList out;
+ for(int i=0; i<mimes.length(); i++){
+ QString mimetype = mimes[i].section(":",1,1);
+ QStringList tmp = mimes.filter(mimetype);
+ //Collect all the different extensions with this mimetype
+ QStringList extlist;
+ for(int j=0; j<tmp.length(); j++){
+ mimes.removeAll(tmp[j]);
+ extlist << tmp[j].section(":",2,2);
+ }
+ extlist.removeDuplicates(); //just in case
+ //Now look for a current default for this mimetype
+ QString dapp = XDGMime::findDefaultAppForMime(mimetype); //default app;
+
+ //Create the output entry
+ //qDebug() << "Mime entry:" << i << mimetype << dapp;
+ out << mimetype+"::::"+extlist.join(", ")+"::::"+dapp+"::::"+XDGMime::findMimeComment(mimetype);
+
+ i--; //go back one (continue until the list is empty)
+ }
+ return out;
+}
+
+QString XDGMime::findMimeComment(QString mime){
+ QString comment;
+ QStringList dirs = XDGMime::systemMimeDirs();
+ QString lang = QString(getenv("LANG")).section(".",0,0);
+ QString shortlang = lang.section("_",0,0);
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i]+"/"+mime+".xml")){
+ QStringList info = LUtils::readFile(dirs[i]+"/"+mime+".xml");
+ QStringList filter = info.filter("<comment xml:lang=\""+lang+"\">");
+ //First look for a full language match, then short language, then general comment
+ if(filter.isEmpty()){ filter = info.filter("<comment xml:lang=\""+shortlang+"\">"); }
+ if(filter.isEmpty()){ filter = info.filter("<comment>"); }
+ if(!filter.isEmpty()){
+ comment = filter.first().section(">",1,1).section("</",0,0);
+ break;
+ }
+ }
+ }
+ return comment;
+}
+
+QString XDGMime::findDefaultAppForMime(QString mime){
+ //First get the priority-ordered list of default file locations
+ QStringList dirs;
+ dirs << QString(getenv("XDG_CONFIG_HOME"))+"/lumina-mimeapps.list" \
+ << QString(getenv("XDG_CONFIG_HOME"))+"/mimeapps.list";
+ QStringList tmp = QString(getenv("XDG_CONFIG_DIRS")).split(":");
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/lumina-mimeapps.list"; }
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/mimeapps.list"; }
+ dirs << QString(getenv("XDG_DATA_HOME"))+"/applications/lumina-mimeapps.list" \
+ << QString(getenv("XDG_DATA_HOME"))+"/applications/mimeapps.list";
+ tmp = QString(getenv("XDG_DATA_DIRS")).split(":");
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/applications/lumina-mimeapps.list"; }
+ for(int i=0; i<tmp.length(); i++){ dirs << tmp[i]+"/applications/mimeapps.list"; }
+
+ //Now go through all the files in order of priority until a default is found
+ QString cdefault;
+ for(int i=0; i<dirs.length() && cdefault.isEmpty(); i++){
+ if(!QFile::exists(dirs[i])){ continue; }
+ QStringList info = LUtils::readFile(dirs[i]);
+ if(info.isEmpty()){ continue; }
+ QStringList white; //lists to keep track of during the search (black unused at the moment)
+ QString workdir = dirs[i].section("/",0,-2); //just the directory
+ // qDebug() << "Check File:" << mime << dirs[i] << workdir;
+ int def = info.indexOf("[Default Applications]"); //find this line to start on
+ if(def>=0){
+ for(int d=def+1; d<info.length(); d++){
+ //qDebug() << "Check Line:" << info[d];
+ if(info[d].startsWith("[")){ break; } //starting a new section now - finished with defaults
+ if(info[d].contains(mime+"=") ){
+ white = info[d].section("=",1,-1).split(";") + white; //exact mime match - put at front of list
+ break; //already found exact match
+ }else if(info[d].contains("*") && info[d].contains("=") ){
+ QRegExp rg(info[d].section("=",0,0), Qt::CaseSensitive, QRegExp::WildcardUnix);
+ if(rg.exactMatch(mime)){
+ white << info[d].section("=",1,-1).split(";"); //partial mime match - put at end of list
+ }
+ }
+ }
+ }
+ // Now check for any white-listed files in this work dir
+ // find the full path to the file (should run even if nothing in this file)
+ //qDebug() << "WhiteList:" << white;
+ for(int w=0; w<white.length(); w++){
+ if(white[w].isEmpty()){ continue; }
+ //First check for absolute paths to *.desktop file
+ if( white[w].startsWith("/") ){
+ if( QFile::exists(white[w]) ){ cdefault=white[w]; break; }
+ else{ white.removeAt(w); w--; } //invalid file path - remove it from the list
+ }
+ //Now check for relative paths to file (in current priority-ordered work dir)
+ else if( QFile::exists(workdir+"/"+white[w]) ){ cdefault=workdir+"/"+white[w]; break; }
+ //Now go through the XDG DATA dirs and see if the file is in there
+ else{
+ white[w] = LUtils::AppToAbsolute(white[w]);
+ if(QFile::exists(white[w])){ cdefault = white[w]; }
+ }
+ }
+ /* WRITTEN BUT UNUSED CODE FOR MIMETYPE ASSOCIATIONS
+ //Skip using this because it is simply an alternate/unsupported standard that conflicts with
+ the current mimetype database standards. It is better/faster to parse 1 or 2 database glob files
+ rather than have to iterate through hundreds of *.desktop files *every* time you need to
+ find an application
+
+ if(addI<0 && remI<0){
+ //Simple Format: <mimetype>=<*.desktop file>;<*.desktop file>;.....
+ // (usually only one desktop file listed)
+ info = info.filter(mimetype+"=");
+ //Load the listed default(s)
+ for(int w=0; w<info.length(); w++){
+ white << info[w].section("=",1,50).split(";");
+ }
+ }else{
+ //Non-desktop specific mimetypes file: has a *very* convoluted/inefficient algorithm (required by spec)
+ if(addI<0){ addI = info.length(); } //no add section
+ if(remI<0){ remI = info.length(); } // no remove section:
+ //Whitelist items
+ for(int a=addI+1; a!=remI && a<info.length(); a++){
+ if(info[a].contains(mimetype+"=")){
+ QStringList tmp = info[a].section("=",1,50).split(";");
+ for(int t=0; t<tmp.length(); t++){
+ if(!black.contains(tmp[t])){ white << tmp[t]; } //make sure this item is not on the black list
+ }
+ break;
+ }
+ }
+ //Blacklist items
+ for(int a=remI+1; a!=addI && a<info.length(); a++){
+ if(info[a].contains(mimetype+"=")){ black << info[a].section("=",1,50).split(";"); break;}
+ }
+
+ //STEPS 3/4 not written yet
+
+ } //End of non-DE mimetypes file */
+
+ } //End loop over file
+ return cdefault;
+}
+
+QStringList XDGMime::findAvailableAppsForMime(QString mime){
+ QStringList dirs = LUtils::systemApplicationDirs();
+ QStringList out;
+ //Loop over all possible directories that contain *.destop files
+ // and check for the mimeinfo.cache file
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i]+"/mimeinfo.cache")){
+ QStringList matches = LUtils::readFile(dirs[i]+"/mimeinfo.cache").filter(mime+"=");
+ //Find any matches for our mimetype in the cache
+ for(int j=0; j<matches.length(); j++){
+ QStringList files = matches[j].section("=",1,1).split(";",QString::SkipEmptyParts);
+ //Verify that each file exists before putting the full path to the file in the output
+ for(int m=0; m<files.length(); m++){
+ if(QFile::exists(dirs[i]+"/"+files[m])){
+ out << dirs[i]+"/"+files[m];
+ }else if(files[m].contains("-")){ //kde4-<filename> -> kde4/<filename> (stupid KDE variations!!)
+ files[m].replace("-","/");
+ if(QFile::exists(dirs[i]+"/"+files[m])){
+ out << dirs[i]+"/"+files[m];
+ }
+ }
+ }
+ }
+ }
+ }
+ //qDebug() << "Found Apps for Mime:" << mime << out << dirs;
+ return out;
+}
+
+void XDGMime::setDefaultAppForMime(QString mime, QString app){
+ QString filepath = QString(getenv("XDG_CONFIG_HOME"))+"/lumina-mimeapps.list";
+ QStringList cinfo = LUtils::readFile(filepath);
+ //If this is a new file, make sure to add the header appropriately
+ if(cinfo.isEmpty()){ cinfo << "#Automatically generated with lumina-config" << "# DO NOT CHANGE MANUALLY" << "[Default Applications]"; }
+ //Check for any current entry for this mime type
+ QStringList tmp = cinfo.filter(mime+"=");
+ int index = -1;
+ if(!tmp.isEmpty()){ index = cinfo.indexOf(tmp.first()); }
+ //Now add the new default entry (if necessary)
+ if(app.isEmpty()){
+ if(index>=0){ cinfo.removeAt(index); } //Remove entry
+ }else{
+ if(index<0){
+ cinfo << mime+"="+app+";"; //new entry
+ }else{
+ cinfo[index] = mime+"="+app+";"; //overwrite existing entry
+ }
+ }
+ LUtils::writeFile(filepath, cinfo, true);
+ return;
+}
+
+QStringList XDGMime::findAVFileExtensions(){
+ //output format: QDir name filter for valid A/V file extensions
+ QStringList globs = XDGMime::loadMimeFileGlobs2();
+ QStringList av = globs.filter(":audio/");
+ av << globs.filter(":video/");
+ for(int i=0; i<av.length(); i++){
+ //Just use all audio/video mimetypes (for now)
+ av[i] = av[i].section(":",2,2);
+ //Qt5 Auto detection (broken - QMediaPlayer seg faults with Qt 5.3 - 11/24/14)
+ /*if( QMultimedia::NotSupported != QMediaPlayer::hasSupport(av[i].section(":",1,1)) ){ av[i] = av[i].section(":",2,2); }
+ else{ av.removeAt(i); i--; }*/
+ }
+ av.removeDuplicates();
+ return av;
+}
+
+QStringList XDGMime::loadMimeFileGlobs2(){
+ //output format: <weight>:<mime type>:<file extension (*.something)>
+ if(mimeglobs.isEmpty() || (mimechecktime < (QDateTime::currentMSecsSinceEpoch()-30000)) ){
+ //qDebug() << "Loading globs2 mime DB files";
+ mimeglobs.clear();
+ mimechecktime = QDateTime::currentMSecsSinceEpoch(); //save the current time this was last checked
+ QStringList dirs = XDGMime::systemMimeDirs();
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i]+"/globs2")){
+ QFile file(dirs[i]+"/globs2");
+ if(!file.exists()){ continue; }
+ if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ continue; }
+ QTextStream in(&file);
+ while(!in.atEnd()){
+ QString line = in.readLine();
+ if(!line.startsWith("#")){
+ mimeglobs << line.simplified();
+ }
+ }
+ file.close();
+ }
+ if(i==dirs.length()-1 && mimeglobs.isEmpty()){
+ //Could not find the mimetype database on the system - use the fallback file distributed with Lumina
+ if(dirs.last()!= LOS::LuminaShare()){ dirs << LOS::LuminaShare(); } //just in case it was not installed
+ }
+ }//end loop over dirs
+ }
+ return mimeglobs;
+}
diff --git a/src-qt5/core/libLumina/XDGMime.h b/src-qt5/core/libLumina/XDGMime.h
new file mode 100644
index 00000000..a8d91142
--- /dev/null
+++ b/src-qt5/core/libLumina/XDGMime.h
@@ -0,0 +1,57 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2013-2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// These structures/classes are for conforming to the FreeDesktop standards
+// REFERENCE:
+// (DATABASE) https://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec/
+// (APPLICATIONS) https://www.freedesktop.org/wiki/Specifications/mime-apps-spec/
+// Mime Application Version Compliance: 1.0.1 (11/14/14) (Skips random *.desktop parsing: ~80% compliant)
+//===========================================
+#ifndef _LUMINA_LIBRARY_XDG_MIME_CLASS_H
+#define _LUMINA_LIBRARY_XDG_MIME_CLASS_H
+
+#include <QFile>
+#include <QDir>
+#include <QFileInfo>
+#include <QStringList>
+#include <QString>
+#include <QList>
+#include <QDateTime>
+#include <QTextStream>
+#include <QRegExp>
+#include <QDebug>
+
+class XDGMime{
+public:
+ // PRIMARY FUNCTIONS
+ static QString fromFileName(QString filename); //Convert a filename into a mimetype
+ static QStringList listFromFileName(QString filename); //Convert a filename into a list of mimetypes (arranged in descending priority)
+ static QString toIconName(QString mime); //Mime type to icon name
+ //Find the file extension for a particular mime-type
+ static QStringList toFileExtensions(QString mime);
+
+ // LESSER-USED FUNCTIONS
+ //List all the mime-type directories
+ static QStringList systemMimeDirs();
+ //Find the mime-type of a particular file extension
+ static QString findAppMimeForFile(QString filename, bool multiple = false);
+ // Simplification function for finding all info regarding current mime defaults
+ static QStringList listFileMimeDefaults();
+ //Find the localized comment string for a particular mime-type
+ static QString findMimeComment(QString mime);
+ //Find the default application for a mime-type
+ static QString findDefaultAppForMime(QString mime);
+ //Fine the available applications for a mime-type
+ static QStringList findAvailableAppsForMime(QString mime);
+ //Set the default application for a mime-type
+ static void setDefaultAppForMime(QString mime, QString app);
+ //List all the registered audio/video file extensions
+ static QStringList findAVFileExtensions();
+ //Load all the "globs2" mime database files
+ static QStringList loadMimeFileGlobs2();
+
+};
+#endif
diff --git a/src-qt5/core/libLumina/XDGMime.pri b/src-qt5/core/libLumina/XDGMime.pri
new file mode 100644
index 00000000..f7cb8b6d
--- /dev/null
+++ b/src-qt5/core/libLumina/XDGMime.pri
@@ -0,0 +1,10 @@
+#QT *= multimedia svg
+
+#XDG Mime Files
+SOURCES *= $${PWD}/XDGMime.cpp
+HEADERS *= $${PWD}/XDGMime.h
+
+INCLUDEPATH *= ${PWD}
+
+#include LUtils and LuminaOS
+include(LUtils.pri)
bgstack15