diff options
Diffstat (limited to 'src-qt5/core/libLumina')
-rw-r--r-- | src-qt5/core/libLumina/DesktopSettings.cpp | 2 | ||||
-rw-r--r-- | src-qt5/core/libLumina/DesktopSettings.h | 2 | ||||
-rw-r--r-- | src-qt5/core/libLumina/ExternalProcess.h | 6 | ||||
-rw-r--r-- | src-qt5/core/libLumina/NativeEmbedWidget.cpp | 83 | ||||
-rw-r--r-- | src-qt5/core/libLumina/NativeEmbedWidget.h | 8 | ||||
-rw-r--r-- | src-qt5/core/libLumina/NativeWindow.cpp | 15 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow-animations.cpp | 97 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow.cpp | 87 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow.h | 7 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootWindow-mgmt.cpp | 34 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootWindow.cpp | 18 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootWindow.h | 11 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootWindow.pri | 5 | ||||
-rw-r--r-- | src-qt5/core/libLumina/XDGMime.cpp | 362 | ||||
-rw-r--r-- | src-qt5/core/libLumina/XDGMime.h | 57 | ||||
-rw-r--r-- | src-qt5/core/libLumina/XDGMime.pri | 10 |
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) |