commit 71c022543d4bb9e8c8acf16a3658d556e236e634
Author: Eric Seigne <eric.seigne@ryxeo.com>
Date:   Sun Jun 3 21:10:16 2012 +0200

    ca avance: on affiche la liste des images (hardcoded)

diff --git a/images/background.jpg b/images/background.jpg
new file mode 100644
index 0000000..fde923e
Binary files /dev/null and b/images/background.jpg differ
diff --git a/mymodel.cpp b/mymodel.cpp
new file mode 100644
index 0000000..a7e35b7
--- /dev/null
+++ b/mymodel.cpp
@@ -0,0 +1,629 @@
+/****************************************************************************
+*
+* This file is part of qtFM, a simple, fast file manager.
+* Copyright (C) 2010,2011 Wittfella
+*
+* This file is a local adaptation for RyXeo TagFile System
+* Copyright (C) 2012 Eric Seigne <eric.seigne@ryxeo.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>
+*
+* Contact e-mail: wittfella@qtfm.org
+*
+****************************************************************************/
+
+
+#include "mymodel.h"
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+//---------------------------------------------------------------------------------
+myModel::myModel()
+{
+    mimeGeneric = new QHash<QString,QString>;
+    mimeGlob = new QHash<QString,QString>;
+    mimeIcons = new QHash<QString,QIcon>;
+    folderIcons = new QHash<QString,QIcon>;
+    thumbs = new QHash<QString,QByteArray>;
+    icons = new QHash<QString,QIcon>;
+
+    QFile fileIcons(QDir::homePath() + "/.config/ryxeo/file.cache");
+    fileIcons.open(QIODevice::ReadOnly);
+    QDataStream out(&fileIcons);
+    out >> *mimeIcons;
+    fileIcons.close();
+
+    fileIcons.setFileName(QDir::homePath() + "/.config/ryxeo/folder.cache");
+    fileIcons.open(QIODevice::ReadOnly);
+    out.setDevice(&fileIcons);
+    out >> *folderIcons;
+    fileIcons.close();
+
+    rootItem = new myModelItem(QFileInfo("/"),new myModelItem(QFileInfo(),0));
+
+    currentRootPath = "/";
+
+    QDir root("/");
+    QFileInfoList drives = root.entryInfoList(QDir::AllEntries | QDir::Files | QDir::NoDotAndDotDot);
+
+    foreach(QFileInfo drive, drives)
+        new myModelItem(drive,rootItem);
+
+    rootItem->walked = true;
+    rootItem = rootItem->parent();
+
+    iconFactory = new QFileIconProvider();
+}
+
+//---------------------------------------------------------------------------------------
+myModel::~myModel()
+{
+    delete rootItem;
+    delete iconFactory;
+}
+
+//---------------------------------------------------------------------------------------
+QModelIndex myModel::index(int row, int column, const QModelIndex &parent) const
+{
+    if(parent.isValid() && parent.column() != 0)
+        return QModelIndex();
+
+    myModelItem *parentItem = static_cast<myModelItem*>(parent.internalPointer());
+    if(!parentItem) parentItem = rootItem;
+
+    myModelItem *childItem = parentItem->childAt(row);
+        if(childItem) return createIndex(row, column, childItem);
+
+    return QModelIndex();
+}
+
+//---------------------------------------------------------------------------------------
+QModelIndex myModel::index(const QString& path) const
+{
+//    qDebug() << "myModel::index " << path;
+
+    myModelItem *item = rootItem->matchPath(path.split(SEPARATOR),0);
+
+    if(item) return createIndex(item->childNumber(),0,item);
+
+    return QModelIndex();
+}
+
+//---------------------------------------------------------------------------------------
+QModelIndex myModel::parent(const QModelIndex &index) const
+{
+    if(!index.isValid()) return QModelIndex();
+
+    myModelItem *childItem = static_cast<myModelItem*>(index.internalPointer());
+
+    if(!childItem) return QModelIndex();
+
+    myModelItem *parentItem = childItem->parent();
+
+    if (!parentItem || parentItem == rootItem) return QModelIndex();
+
+    return createIndex(parentItem->childNumber(), 0, parentItem);
+}
+
+//---------------------------------------------------------------------------------------
+QFileInfo myModel::fileInfo(const QModelIndex &index)
+{
+    myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+
+    if(item) return item->fileInfo();
+
+    return QFileInfo();
+}
+
+//---------------------------------------------------------------------------------------
+qint64 myModel::size(const QModelIndex &index)
+{
+    myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+
+    if(item) return item->fileInfo().size();
+
+    return 0;
+}
+
+//---------------------------------------------------------------------------------------
+QString myModel::fileName(const QModelIndex &index)
+{
+    myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+
+    if(item) return item->fileName();
+
+    return "";
+}
+
+//---------------------------------------------------------------------------------------
+QString myModel::filePath(const QModelIndex &index)
+{
+    myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+
+    if(item) return item->fileInfo().filePath();
+
+    return false;
+}
+
+//---------------------------------------------------------------------------------
+bool myModel::setRootPath(const QString& path)
+{
+    qDebug() << "Changement de rootPath " << path;
+    currentRootPath = path;
+
+    myModelItem *item = rootItem->matchPath(path.split(SEPARATOR));
+
+    if(item->walked == 0)
+    {
+        populateItem(item);
+        return false;
+    }
+    else
+    if(item->dirty)                         //model is up to date, but view needs to be invalidated
+    {
+        item->dirty = 0;
+        return true;
+    }
+
+    return false;
+}
+
+void myModel::populateItem(QString nomFichier)
+{
+//    qDebug() << "populateItem:: " << nomFichier << " :: " << rootItem->absoluteFilePath();
+    beginInsertRows(index(rootItem->absoluteFilePath()),rootItem->childCount(),rootItem->childCount());
+    new myModelItem(QFileInfo(nomFichier),rootItem);
+    endInsertRows();
+}
+
+//---------------------------------------------------------------------------------------
+void myModel::populateItem(myModelItem *item)
+{
+//    qDebug() << "myModel::populateItem(1) : " << item->fileInfo().absolutePath() << " (" << currentRootPath << ")";
+    item->walked = 1;
+
+    QDir dir(item->absoluteFilePath());
+    QFileInfoList all = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
+
+//    addWatcher(item);
+
+    foreach(QFileInfo one, all)
+        new myModelItem(one,item);
+//    qDebug() << "myModel::populateItem :" << item->absoluteFilePath();
+}
+
+//---------------------------------------------------------------------------------
+int myModel::columnCount(const QModelIndex &parent) const
+{
+    return (parent.column() > 0) ? 0 : 5;
+}
+
+//---------------------------------------------------------------------------------------
+int myModel::rowCount(const QModelIndex &parent) const
+{
+    myModelItem *item = static_cast<myModelItem*>(parent.internalPointer());
+    if(item) return item->childCount();
+    return rootItem->childCount();
+}
+
+//---------------------------------------------------------------------------------
+void myModel::clear()
+{
+    beginResetModel();
+    rootItem->clearAll();
+    endResetModel();
+}
+
+//---------------------------------------------------------------------------------
+QModelIndex myModel::insertFile(QModelIndex parent)
+{
+    myModelItem *item = static_cast<myModelItem*>(parent.internalPointer());
+
+    int num = 0;
+    QString name;
+
+    do
+    {
+        num++;
+        name = QString("new_file%1").arg(num);
+    }
+    while (item->hasChild(name));
+
+
+    QFile temp(currentRootPath + "/" + name);
+    if(!temp.open(QIODevice::WriteOnly)) return QModelIndex();
+    temp.close();
+
+    beginInsertRows(parent,item->childCount(),item->childCount());
+    new myModelItem(QFileInfo(temp),item);
+    endInsertRows();
+
+    return index(item->childCount()-1,0,parent);
+}
+
+//---------------------------------------------------------------------------------
+Qt::DropActions myModel::supportedDropActions() const
+{
+    return Qt::CopyAction | Qt::MoveAction;
+}
+
+//---------------------------------------------------------------------------------
+QStringList myModel::mimeTypes() const
+{
+    return QStringList("text/uri-list");
+}
+
+//---------------------------------------------------------------------------------
+QMimeData * myModel::mimeData(const QModelIndexList & indexes) const
+{
+    QMimeData *data = new QMimeData();
+
+    QList<QUrl> files;
+
+    foreach(QModelIndex index, indexes)
+    {
+        myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+        files.append(QUrl::fromLocalFile(item->absoluteFilePath()));
+    }
+
+    data->setUrls(files);
+    return data;
+}
+
+
+//---------------------------------------------------------------------------------
+void myModel::cacheInfo()
+{
+    QFile fileIcons(QDir::homePath() + "/.config/ryxeo/file.cache");
+    fileIcons.open(QIODevice::WriteOnly);
+    QDataStream out(&fileIcons);
+    out << *mimeIcons;
+    fileIcons.close();
+
+    fileIcons.setFileName(QDir::homePath() + "/.config/ryxeo/folder.cache");
+    fileIcons.open(QIODevice::WriteOnly);
+    out.setDevice(&fileIcons);
+    out << *folderIcons;
+    fileIcons.close();
+
+    if(thumbs->count() > thumbCount)
+    {
+        fileIcons.setFileName(QDir::homePath() + "/.config/ryxeo/thumbs.cache");
+        fileIcons.open(QIODevice::WriteOnly);
+        out.setDevice(&fileIcons);
+        out << *thumbs;
+        fileIcons.close();
+    }
+}
+
+//---------------------------------------------------------------------------------
+void myModel::setMode(bool icons)
+{
+    showThumbs = icons;
+}
+
+//---------------------------------------------------------------------------------
+void myModel::loadMimeTypes() const
+{
+    QFile mimeInfo("/usr/share/mime/globs");
+    mimeInfo.open(QIODevice::ReadOnly);
+    QTextStream out(&mimeInfo);
+
+    do
+    {
+        QStringList line = out.readLine().split(":");
+        if(line.count() == 2)
+        {
+            QString suffix = line.at(1);
+            suffix.remove("*.");
+            QString mimeName = line.at(0);
+            mimeName.replace("/","-");
+            mimeGlob->insert(suffix,mimeName);
+        }
+    }
+    while (!out.atEnd());
+
+    mimeInfo.close();
+
+    mimeInfo.setFileName("/usr/share/mime/generic-icons");
+    mimeInfo.open(QIODevice::ReadOnly);
+    out.setDevice(&mimeInfo);
+
+    do
+    {
+        QStringList line = out.readLine().split(":");
+        if(line.count() == 2)
+        {
+            QString mimeName = line.at(0);
+            mimeName.replace("/","-");
+            QString icon = line.at(1);
+            mimeGeneric->insert(mimeName,icon);
+        }
+    }
+    while (!out.atEnd());
+
+    mimeInfo.close();
+}
+
+//---------------------------------------------------------------------------
+void myModel::loadThumbsQSL(QStringList indexes)
+{
+//    qDebug() << "myModel::loadThumbsQSL (1)" << indexes;
+    QStringList files,types;
+    types << "jpg" << "jpeg" << "png" << "bmp" << "ico" << "svg" << "gif";
+
+    foreach(QString item,indexes) {
+        if(types.contains(QFileInfo(item).suffix(),Qt::CaseInsensitive)) {
+            files.append(item);
+        }
+    }
+
+//    qDebug() << "loadThumbs pour " << files;
+
+    if(files.count())
+    {
+        if(thumbs->count() == 0)
+        {
+            QFile fileIcons(QDir::homePath() + "/.config/ryxeo/thumbs.cache");
+            fileIcons.open(QIODevice::ReadOnly);
+            QDataStream out(&fileIcons);
+            out >> *thumbs;
+            fileIcons.close();
+            thumbCount = thumbs->count();
+        }
+
+        foreach(QString item, files)
+        {
+            if(!thumbs->contains(item)) thumbs->insert(item,getThumb(item));
+            emit thumbUpdate(index(item));
+        }
+    }
+
+}
+
+//---------------------------------------------------------------------------
+QByteArray myModel::getThumb(QString item)
+{
+    qDebug() << "Creation du thumbnail pour " << item;
+    QImage theThumb, background;
+    QImageReader pic(item);
+    int w,h,target;
+    w = pic.size().width();
+    h = pic.size().height();
+
+    if( w > 128 || h > 128)
+    {
+        target = 114;
+        background.load(":/images/background.jpg");
+    }
+    else
+    {
+        target = 64;
+        background = QImage(128,128,QImage::Format_ARGB32);
+        background.fill(QApplication::palette().color(QPalette::Base).rgb());
+    }
+
+    if(w > h)
+    {
+        int newHeight = h * target / w;
+        pic.setScaledSize(QSize(target,newHeight));
+    }
+    else
+    {
+        int newWidth = w * target / h;
+        pic.setScaledSize(QSize(newWidth,target));
+    }
+
+    theThumb = pic.read();
+
+    int thumbWidth = theThumb.width();
+    int thumbHeight = theThumb.height();
+
+    QPainter painter(&background);
+    painter.drawImage(QPoint((128-thumbWidth)/2,(128 - thumbHeight)/2),theThumb);
+
+    QBuffer buffer;
+    QImageWriter writer(&buffer,"jpg");
+    writer.setQuality(50);
+    writer.write(background);
+
+    return buffer.buffer();
+}
+
+//---------------------------------------------------------------------------------
+QVariant myModel::data(const QModelIndex & index, int role) const
+{
+    myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+
+    if(role == Qt::ForegroundRole)
+    {
+        QFileInfo type(item->fileInfo());
+
+        if(cutItems.contains(type.filePath())) return QBrush(QColor(Qt::lightGray));
+        if(type.isHidden()) return QBrush(QColor(Qt::darkGray));
+        if(type.isSymLink()) return QBrush(QColor(Qt::blue));
+        if(type.isDir()) return QBrush(QColor(Qt::black));
+        if(type.isExecutable()) return QBrush(QColor(Qt::darkGreen));
+    }
+    else
+    if(role == Qt::TextAlignmentRole)
+    {
+        if(index.column() == 1) return Qt::AlignRight + Qt::AlignVCenter;
+    }
+    else
+    if(role == Qt::DisplayRole)
+    {
+        QVariant data;
+        switch(index.column())
+        {
+            case 0:
+                data = item->fileName();
+                break;
+            case 1:
+                if(item->fileInfo().isDir()) data = "";
+                else data = formatSize(item->fileInfo().size());
+                break;
+            case 2:
+                if(item->fileInfo().isDir()) data = "folder";
+                else data = item->fileInfo().suffix();
+                if(data == "") data = "file";
+                break;
+            case 3:
+                data = item->fileInfo().lastModified().toString(Qt::LocalDate);
+                break;
+            case 4:
+                {
+                    QString str;
+
+                    QFlags<QFile::Permissions> perms = item->fileInfo().permissions();
+                    if(perms.testFlag(QFile::ReadOwner)) str.append("r"); else str.append(("-"));
+                    if(perms.testFlag(QFile::WriteOwner)) str.append("w"); else str.append(("-"));
+                    if(perms.testFlag(QFile::ExeOwner)) str.append("x"); else str.append(("-"));
+                    if(perms.testFlag(QFile::ReadGroup)) str.append("r"); else str.append(("-"));
+                    if(perms.testFlag(QFile::WriteGroup)) str.append("w"); else str.append(("-"));
+                    if(perms.testFlag(QFile::ExeGroup)) str.append("x"); else str.append(("-"));
+                    if(perms.testFlag(QFile::ReadOther)) str.append("r"); else str.append(("-"));
+                    if(perms.testFlag(QFile::WriteOther)) str.append("w"); else str.append(("-"));
+                    if(perms.testFlag(QFile::ExeOther)) str.append("x"); else str.append(("-"));
+                    str.append(" " + item->fileInfo().owner() + " " + item->fileInfo().group());
+                    return str;
+                }
+            default:
+                data = "";
+                break;
+        }
+        return data;
+    }
+    else
+    if(role == Qt::DecorationRole)
+    {
+        if(index.column() != 0) return QVariant();
+
+        QFileInfo type(item->fileInfo());
+
+        if(type.isDir())
+        {
+            if(folderIcons->contains(type.fileName())) return folderIcons->value(type.fileName());
+            return iconFactory->icon(type);
+        }
+        else
+        {
+            if(showThumbs)
+            {
+                if(icons->contains(item->absoluteFilePath())) return icons->value(item->absoluteFilePath());
+                else
+                    if(thumbs->contains(type.filePath()))
+                    {
+                        QPixmap pic;
+                        pic.loadFromData(thumbs->value(item->absoluteFilePath()));
+                        icons->insert(item->absoluteFilePath(),QIcon(pic));
+                        return icons->value(item->absoluteFilePath());
+                    }
+            }
+
+            QString suffix = type.suffix();
+            if(mimeIcons->contains(suffix)) return mimeIcons->value(suffix);
+
+            QIcon theIcon;
+
+            if(suffix.isEmpty())
+            {
+                if(type.isExecutable()) suffix = "exec";
+                else suffix = "none";
+
+                if(mimeIcons->contains(suffix)) return mimeIcons->value(suffix);
+
+                if(suffix == "exec") theIcon = QIcon::fromTheme("application-x-executable");
+                else theIcon = QIcon(qApp->style()->standardIcon(QStyle::SP_FileIcon));
+             }
+            else
+            {
+                if(mimeGlob->count() == 0) loadMimeTypes();
+
+                //try mimeType as it is
+                QString mimeType = mimeGlob->value(type.suffix().toLower());
+                if(QIcon::hasThemeIcon(mimeType)) theIcon = QIcon::fromTheme(mimeType);
+                else
+                {
+                    //try matching generic icon
+                    if(QIcon::hasThemeIcon(mimeGeneric->value(mimeType))) theIcon = QIcon::fromTheme(mimeGeneric->value(mimeType));
+                    else
+                    {
+                        //last resort try adding "-x-generic" to base type
+                        if(QIcon::hasThemeIcon(mimeType.split("-").at(0) + "-x-generic")) theIcon = QIcon::fromTheme(mimeType.split("-").at(0) + "-x-generic");
+                        else theIcon = QIcon(qApp->style()->standardIcon(QStyle::SP_FileIcon));
+                    }
+                }
+            }
+
+            mimeIcons->insert(suffix,theIcon);
+            return theIcon;
+        }
+    }
+    else
+    if(role == Qt::EditRole)
+    {
+        return item->fileName();
+    }
+//    if(role == Qt::StatusTipRole)
+//    {
+//        return item->fileName();
+//    }
+
+    return QVariant();
+}
+
+//---------------------------------------------------------------------------------
+bool myModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+    //can only set the filename
+    myModelItem *item = static_cast<myModelItem*>(index.internalPointer());
+
+    //physically change the name on disk
+    bool ok = QFile::rename(item->absoluteFilePath(),item->parent()->absoluteFilePath() + SEPARATOR + value.toString());
+
+    //change the details in the modelItem
+    if(ok)
+    {
+        item->changeName(value.toString());
+        emit dataChanged(index,index);
+    }
+
+    return ok;
+}
+
+//---------------------------------------------------------------------------------
+QVariant myModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if(role == Qt::DisplayRole)
+	switch(section)
+	{
+        case 0: return tr("Name");
+        case 1: return tr("Size");
+        case 2: return tr("Type");
+        case 4: return tr("Owner");
+        case 3: return tr("Date Modified");
+	    default: return QVariant();
+	}
+
+    return QVariant();
+}
+
+//---------------------------------------------------------------------------------------
+Qt::ItemFlags myModel::flags(const QModelIndex &index) const
+{
+    if(!index.isValid()) return 0;
+    return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
+}
+
diff --git a/mymodel.h b/mymodel.h
new file mode 100644
index 0000000..b7cf2a7
--- /dev/null
+++ b/mymodel.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+* This file is part of qtFM, a simple, fast file manager.
+* Copyright (C) 2010,2011 Wittfella
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>
+*
+* Contact e-mail: wittfella@qtfm.org
+*
+****************************************************************************/
+
+
+#ifndef MYMODEL_H
+#define MYMODEL_H
+
+#include <QtGui>
+#include "mymodelitem.h"
+
+QString formatSize(qint64);
+
+
+class myModel : public QAbstractItemModel
+{
+    Q_OBJECT
+
+public:
+        myModel();
+        ~myModel();
+
+        void loadMimeTypes() const;
+        void cacheInfo();
+        void setMode(bool);
+        void loadThumbsQSL(QStringList);
+        void populateItem(QString nomFichier);
+        void populateItem(myModelItem *item);
+        void clear();
+
+        bool setRootPath(const QString& path);
+
+        QModelIndex index(int row, int column, const QModelIndex &parent) const;
+        QModelIndex index(const QString& path) const;
+        QModelIndex parent(const QModelIndex &index) const;
+        QModelIndex insertFile(QModelIndex parent);
+
+        int rowCount(const QModelIndex &parent) const;
+        qint64 size(const QModelIndex &index);
+
+        QString fileName(const QModelIndex &index);
+        QString filePath(const QModelIndex &index);
+
+        QStringList mimeTypes() const;
+
+        QByteArray getThumb(QString item);
+
+        QFileInfo fileInfo(const QModelIndex &index);
+
+        Qt::DropActions supportedDropActions () const;
+        QMimeData * mimeData(const QModelIndexList & indexes) const;
+
+        QHash<QString,QIcon> *mimeIcons;
+        QHash<QString,QIcon> *folderIcons;
+        QHash<QString,QIcon> *icons;
+
+signals:
+        void dragDropPaste(const QMimeData * data, QString newPath, QStringList cutList);
+        void thumbUpdate(const QModelIndex index);
+
+protected:
+        QVariant data(const QModelIndex & index, int role) const;
+        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+        bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
+        int columnCount(const QModelIndex &parent) const;
+
+        Qt::ItemFlags flags(const QModelIndex &index) const;
+
+private:
+
+        bool showThumbs;
+        int thumbCount;
+        QStringList cutItems;
+        QHash<QString,QString> *mimeGlob;
+        QHash<QString,QString> *mimeGeneric;
+        QHash<QString,QByteArray> *thumbs;
+
+        myModelItem* rootItem;
+        QString currentRootPath;
+        QFileIconProvider* iconFactory;
+
+        QHash<int, QString> watchers;
+};
+
+//---------------------------------------------------------------------------------
+// Global functions.
+//---------------------------------------------------------------------------------
+QString formatSize(qint64);
+
+#endif // MYMODEL_H
diff --git a/mymodelitem.cpp b/mymodelitem.cpp
new file mode 100644
index 0000000..f6efdd9
--- /dev/null
+++ b/mymodelitem.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+* This file is part of qtFM, a simple, fast file manager.
+* Copyright (C) 2010,2011 Wittfella
+*
+* This file is a local adaptation for RyXeo TagFile System
+* Copyright (C) 2012 Eric Seigne <eric.seigne@ryxeo.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>
+*
+* Contact e-mail: wittfella@qtfm.org
+*
+****************************************************************************/
+
+#include "mymodelitem.h"
+#include <QtGui>
+
+
+//---------------------------------------------------------------------------------------
+myModelItem::myModelItem(const QFileInfo& fileInfo, myModelItem* parent)
+{
+    mParent = parent;
+    mFileInfo = fileInfo;
+    walked = false;
+    dirty = false;
+
+    if(parent)
+    {
+          parent->addChild(this);
+          mAbsFilePath = fileInfo.filePath();
+    }
+    else
+    {
+        walked = true;
+        mAbsFilePath = "";
+    }
+}
+
+//---------------------------------------------------------------------------------------
+myModelItem::~myModelItem()
+{
+    qDeleteAll(mChildren);
+}
+
+//---------------------------------------------------------------------------------------
+myModelItem* myModelItem::childAt(int position)
+{
+    return mChildren.value(position,0);
+}
+
+//---------------------------------------------------------------------------------------
+int myModelItem::childCount() const
+{
+    if(walked) return mChildren.count();
+    return 1;
+}
+
+//---------------------------------------------------------------------------------------
+bool myModelItem::hasChild(QString fileName)
+{
+    foreach(myModelItem * item, mChildren)
+        if(item->fileName() == fileName) return true;
+
+    return false;
+}
+
+//---------------------------------------------------------------------------------------
+int myModelItem::childNumber() const
+{
+    if(mParent)
+    {
+      return mParent->mChildren.indexOf(const_cast<myModelItem*>(this));
+    }
+
+    return 0;
+}
+
+//---------------------------------------------------------------------------------------
+QList<myModelItem*> myModelItem::children()
+{
+    return mChildren;
+}
+
+//---------------------------------------------------------------------------------------
+myModelItem* myModelItem::parent()
+{
+    return mParent;
+}
+
+//---------------------------------------------------------------------------------------
+QString myModelItem::absoluteFilePath()const
+{
+    return mAbsFilePath;
+}
+
+//---------------------------------------------------------------------------------------
+QString myModelItem::fileName() const
+{
+    if(mAbsFilePath == "/") return "/";
+    else return mFileInfo.fileName();
+
+}
+
+//---------------------------------------------------------------------------------------
+QFileInfo myModelItem::fileInfo() const
+{
+    return mFileInfo;
+}
+
+//---------------------------------------------------------------------------------------
+void myModelItem::refreshFileInfo()
+{
+    mFileInfo.refresh();
+}
+
+//---------------------------------------------------------------------------------------
+void myModelItem::addChild(myModelItem *child)
+{
+    if(!mChildren.contains(child))
+    {
+      mChildren.append(child);
+    }
+}
+
+//---------------------------------------------------------------------------------------
+void myModelItem::removeChild(myModelItem *child)
+{
+    mChildren.removeOne(child);
+    delete child;
+}
+
+//---------------------------------------------------------------------------------------
+void myModelItem::clearAll()
+{
+    foreach(myModelItem *child, mChildren)
+        delete child;
+    mChildren.clear();
+    walked = 0;
+}
+
+//---------------------------------------------------------------------------------------
+void myModelItem::changeName(QString newName)
+{
+    mAbsFilePath = mParent->absoluteFilePath() + SEPARATOR + newName;
+    mFileInfo.setFile(mAbsFilePath);
+    clearAll();
+}
+
+//---------------------------------------------------------------------------------------
+myModelItem* myModelItem::matchPath(const QStringList& path, int startIndex)
+{
+    QStringList temp = path;
+    temp.replace(0,"/");
+    temp.removeAll("");
+
+    if(walked == 0)     //not populated yet
+    {
+        walked = true;
+    }
+
+    foreach(myModelItem* child, mChildren)
+    {
+        QString match = temp.at(startIndex);
+
+        if(child->fileName() == match)
+        {
+            if(startIndex + 1 == temp.count()) return child;
+            else return child->matchPath(path,startIndex + 1);
+        }
+    }
+
+    return 0;
+}
+
+//---------------------------------------------------------------------------------------
+
+
diff --git a/mymodelitem.h b/mymodelitem.h
new file mode 100644
index 0000000..5f6ef12
--- /dev/null
+++ b/mymodelitem.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+* This file is part of qtFM, a simple, fast file manager.
+* Copyright (C) 2010,2011 Wittfella
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>
+*
+* Contact e-mail: wittfella@qtfm.org
+*
+****************************************************************************/
+
+#ifndef MYMODELITEM_H
+#define MYMODELITEM_H
+
+#include <QDir>
+#include <QFileIconProvider>
+#include <QDateTime>
+
+#define SEPARATOR QString("/")
+
+
+
+class myModelItem
+{
+
+public:
+
+    myModelItem(const QFileInfo& fileInfo, myModelItem* parent);
+    ~myModelItem();
+
+    myModelItem* childAt(int position);
+    myModelItem* parent();
+    myModelItem* matchPath(const QStringList& path, int startIndex = 0);
+
+    QList<myModelItem*> children();
+
+    int childCount() const;
+    int childNumber() const;
+    bool hasChild(QString);
+
+    QString absoluteFilePath() const;
+    QString fileName() const;
+
+    QFileInfo fileInfo() const;
+
+    void refreshFileInfo();
+    void addChild(myModelItem *child);
+    void removeChild(myModelItem *child);
+    void changeName(QString);
+    void clearAll();
+
+    bool walked;
+    bool dirty;
+
+private:
+
+    QList<myModelItem*> mChildren;
+    myModelItem* mParent;
+    QFileInfo mFileInfo;
+    QString mAbsFilePath;
+};
+
+
+#endif // MYMODELITEM_H
diff --git a/original.cpp b/original.cpp
index 0bd241f..80be230 100644
--- a/original.cpp
+++ b/original.cpp
@@ -24,6 +24,91 @@
 #include "original.h"
 #include "ui_original.h"
 
+
+//---------------------------------------------------------------------------------
+bool viewsSortProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+    if(this->filterRegExp().isEmpty()) return true;
+
+    QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
+    myModel* fileModel = qobject_cast<myModel*>(sourceModel());
+
+    if(fileModel->fileInfo(index0).isHidden()) return false;
+    else return true;
+}
+
+
+//---------------------------------------------------------------------------------
+bool viewsSortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+    myModel* fsModel = dynamic_cast<myModel*>(sourceModel());
+
+    if(left.column() == 1)          //size
+    {
+        if(fsModel->size(left) > fsModel->size(right)) return true;
+        else return false;
+    }
+    else
+    if(left.column() == 3)          //date
+    {
+        if(fsModel->fileInfo(left).lastModified() > fsModel->fileInfo(right).lastModified()) return true;
+        else return false;
+    }
+
+    return QSortFilterProxyModel::lessThan(left,right);
+}
+
+//---------------------------------------------------------------------------------
+QStringList myCompleter::splitPath(const QString& path) const
+{
+    QStringList parts = path.split("/");
+    parts[0] = "/";
+
+    return parts;
+}
+
+//---------------------------------------------------------------------------------
+QString myCompleter::pathFromIndex(const QModelIndex& index) const
+{
+    if(!index.isValid()) return "";
+
+    QModelIndex idx = index;
+    QStringList list;
+    do
+    {
+        QString t = model()->data(idx, Qt::EditRole).toString();
+        list.prepend(t);
+        QModelIndex parent = idx.parent();
+        idx = parent.sibling(parent.row(), index.column());
+    }
+    while (idx.isValid());
+
+    list[0].clear() ; // the join below will provide the separator
+
+    return list.join("/");
+}
+
+//---------------------------------------------------------------------------
+QString formatSize(qint64 num)
+{
+    QString total;
+    const qint64 kb = 1024;
+    const qint64 mb = 1024 * kb;
+    const qint64 gb = 1024 * mb;
+    const qint64 tb = 1024 * gb;
+
+    if (num >= tb) total = QString("%1TB").arg(QString::number(qreal(num) / tb, 'f', 2));
+    else if(num >= gb) total = QString("%1GB").arg(QString::number(qreal(num) / gb, 'f', 2));
+    else if(num >= mb) total = QString("%1MB").arg(QString::number(qreal(num) / mb, 'f', 1));
+    else if(num >= kb) total = QString("%1KB").arg(QString::number(qreal(num) / kb,'f', 1));
+    else total = QString("%1 bytes").arg(num);
+
+    return total;
+}
+
+// =========================================================================================================================
+
+
 original::original(QWidget *parent) :
     QMainWindow(parent),
     ui(new Ui::original)
@@ -165,3 +250,41 @@ void original::on_btnExport_clicked()
         ui->statusBar->showMessage(trUtf8("Fichier en cours: %1 [hq] [mq] [lq] [meta] ... terminé").arg(list.at(i).fileName()),3000);
     }
 }
+
+void original::on_action_Nouvel_album_triggered()
+{
+    QTreeWidgetItem *i = new QTreeWidgetItem(ui->treeWidget);
+    i->setText(0, trUtf8("Nouvel album"));
+}
+
+//---------------------------------------------------------------------------
+void original::thumbUpdate(QModelIndex index)
+{
+    qDebug() << "original::thumbUpdate";
+    ui->lvResult->update(m_modelView->mapFromSource(index));
+}
+
+void original::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
+{
+    QStringList items;
+    m_modelList = new myModel();
+    m_modelList->setRootPath("/tmp/");
+
+    m_modelList->populateItem("/tmp/t/20120529-lea_plongee_cala_montjoi-000.jpg");
+    m_modelList->populateItem("/tmp/t/20120529-lea_plongee_cala_montjoi-001.jpg");
+    m_modelList->populateItem("/tmp/t/20120529-lea_plongee_cala_montjoi-002.jpg");
+    m_modelList->populateItem("/tmp/t/20120529-lea_plongee_cala_montjoi-003.jpg");
+    items.append("/tmp/t/20120529-lea_plongee_cala_montjoi-000.jpg");
+    items.append("/tmp/t/20120529-lea_plongee_cala_montjoi-001.jpg");
+    items.append("/tmp/t/20120529-lea_plongee_cala_montjoi-002.jpg");
+    items.append("/tmp/t/20120529-lea_plongee_cala_montjoi-003.jpg");
+    QtConcurrent::run(m_modelList,&myModel::loadThumbsQSL,items);
+
+    m_modelView = new viewsSortProxyModel();
+    m_modelView->setSourceModel(m_modelList);
+    m_modelView->setSortCaseSensitivity(Qt::CaseInsensitive);
+    ui->lvResult->setModel(m_modelView);
+
+    connect(m_modelList,SIGNAL(thumbUpdate(QModelIndex)),this,SLOT(thumbUpdate(QModelIndex)));
+
+}
diff --git a/original.h b/original.h
index cd9145e..4bfef1c 100644
--- a/original.h
+++ b/original.h
@@ -29,6 +29,8 @@
 #include <QMainWindow>
 #include <exiv2/image.hpp>
 #include <exiv2/exif.hpp>
+#include "mymodelitem.h"
+#include "mymodel.h"
 
 namespace Ui {
 class original;
@@ -44,9 +46,50 @@ public:
     
 private slots:
     void on_btnExport_clicked();
+    void on_action_Nouvel_album_triggered();
+    void on_treeWidget_itemClicked(QTreeWidgetItem *item, int column);
+
+public slots:
+    void thumbUpdate(QModelIndex);
 
 private:
     Ui::original *ui;
+    myModel *m_modelList;
+    QSortFilterProxyModel *m_modelView;
+
+};
+
+
+//---------------------------------------------------------------------------------
+// Subclass QSortFilterProxyModel and override 'filterAcceptsRow' to only show
+// directories in tree and not files.
+//---------------------------------------------------------------------------------
+class mainTreeFilterProxyModel : public QSortFilterProxyModel
+{
+protected:
+    virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
+};
+
+
+//---------------------------------------------------------------------------------
+// Subclass QSortFilterProxyModel and override 'lessThan' for sorting in list/details views
+//---------------------------------------------------------------------------------
+class viewsSortProxyModel : public QSortFilterProxyModel
+{
+protected:
+    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+    bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
+};
+
+
+//---------------------------------------------------------------------------------
+// Subclass QCompleter so we can use the SortFilterProxyModel above for the address bar.
+//---------------------------------------------------------------------------------
+class myCompleter : public QCompleter
+{
+public:
+    QString pathFromIndex(const QModelIndex& index) const;
+    QStringList splitPath(const QString& path) const;
 };
 
 #endif // ORIGINAL_H
diff --git a/original.pro b/original.pro
index 6865a7e..7619ce4 100644
--- a/original.pro
+++ b/original.pro
@@ -11,9 +11,13 @@ TEMPLATE = app
 
 
 SOURCES += main.cpp\
-        original.cpp
+        original.cpp \
+    mymodel.cpp \
+    mymodelitem.cpp
 
-HEADERS  += original.h
+HEADERS  += original.h \
+    mymodel.h \
+    mymodelitem.h
 
 FORMS    += original.ui
 
@@ -22,3 +26,6 @@ FORMS    += original.ui
 
 unix:LIBS += -lexiv2
 win32:LIBS += -lexiv2
+
+RESOURCES += \
+    resources.qrc
diff --git a/original.ui b/original.ui
index b8aab73..6c67461 100644
--- a/original.ui
+++ b/original.ui
@@ -6,63 +6,129 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>738</width>
-    <height>300</height>
+    <width>843</width>
+    <height>457</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>original</string>
   </property>
   <widget class="QWidget" name="centralWidget">
-   <widget class="QPushButton" name="btnExport">
-    <property name="geometry">
-     <rect>
-      <x>70</x>
-      <y>70</y>
-      <width>92</width>
-      <height>27</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>PushButton</string>
-    </property>
-   </widget>
-   <widget class="QLineEdit" name="leDest">
-    <property name="geometry">
-     <rect>
-      <x>70</x>
-      <y>40</y>
-      <width>113</width>
-      <height>27</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>/tmp/w</string>
-    </property>
-   </widget>
-   <widget class="QLineEdit" name="leSource">
-    <property name="geometry">
-     <rect>
-      <x>70</x>
-      <y>10</y>
-      <width>113</width>
-      <height>27</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>/tmp/t</string>
-    </property>
-   </widget>
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="1">
+     <widget class="QFrame" name="frame_2">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+      <layout class="QGridLayout" name="gridLayout_3">
+       <item row="0" column="0">
+        <widget class="QLineEdit" name="leSource">
+         <property name="text">
+          <string>/tmp/t</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLineEdit" name="leDest">
+         <property name="text">
+          <string>/tmp/w</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QPushButton" name="btnExport">
+         <property name="text">
+          <string>PushButton</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0" colspan="3">
+        <widget class="QListView" name="lvResult">
+         <property name="iconSize">
+          <size>
+           <width>96</width>
+           <height>96</height>
+          </size>
+         </property>
+         <property name="gridSize">
+          <size>
+           <width>128</width>
+           <height>128</height>
+          </size>
+         </property>
+         <property name="viewMode">
+          <enum>QListView::IconMode</enum>
+         </property>
+         <property name="uniformItemSizes">
+          <bool>true</bool>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item row="0" column="0">
+     <widget class="QTreeWidget" name="treeWidget">
+      <property name="maximumSize">
+       <size>
+        <width>150</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="editTriggers">
+       <set>QAbstractItemView::AllEditTriggers</set>
+      </property>
+      <property name="alternatingRowColors">
+       <bool>true</bool>
+      </property>
+      <attribute name="headerVisible">
+       <bool>false</bool>
+      </attribute>
+      <column>
+       <property name="text">
+        <string notr="true">1</string>
+       </property>
+      </column>
+     </widget>
+    </item>
+   </layout>
   </widget>
   <widget class="QMenuBar" name="menuBar">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>738</width>
+     <width>843</width>
      <height>25</height>
     </rect>
    </property>
+   <widget class="QMenu" name="menu_Fichier">
+    <property name="title">
+     <string>&amp;Fichier</string>
+    </property>
+    <addaction name="action_Nouvel_album"/>
+    <addaction name="separator"/>
+    <addaction name="action_Quiter"/>
+   </widget>
+   <widget class="QMenu" name="menu_dition">
+    <property name="title">
+     <string>&amp;Édition</string>
+    </property>
+   </widget>
+   <addaction name="menu_Fichier"/>
+   <addaction name="menu_dition"/>
   </widget>
   <widget class="QToolBar" name="mainToolBar">
    <attribute name="toolBarArea">
@@ -73,8 +139,41 @@
    </attribute>
   </widget>
   <widget class="QStatusBar" name="statusBar"/>
+  <action name="action_Quiter">
+   <property name="text">
+    <string>&amp;Quitter</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+Q</string>
+   </property>
+  </action>
+  <action name="action_Nouvel_album">
+   <property name="text">
+    <string>&amp;Nouvel album</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+N</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <resources/>
- <connections/>
+ <connections>
+  <connection>
+   <sender>action_Quiter</sender>
+   <signal>triggered()</signal>
+   <receiver>original</receiver>
+   <slot>close()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>368</x>
+     <y>149</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
 </ui>
diff --git a/resources.qrc b/resources.qrc
new file mode 100644
index 0000000..26e0802
--- /dev/null
+++ b/resources.qrc
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>images/background.jpg</file>
+    </qresource>
+</RCC>
