Commit 9f032d46 authored by Christoph Cullmann's avatar Christoph Cullmann

do work in own thread

parent aae03193
......@@ -19,16 +19,10 @@
*/
#include "kateproject.h"
#include "kateprojectworker.h"
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <KMimeType>
#include <KIconLoader>
#include <qjson/parser.h>
......@@ -36,6 +30,7 @@ KateProject::KateProject ()
: QObject ()
, m_worker (new KateProjectWorker (this))
, m_model (new QStandardItemModel (this))
, m_file2Item (new QMap<QString, QStandardItem *>())
{
/**
* move worker object over and start our worker thread
......@@ -56,6 +51,11 @@ KateProject::~KateProject ()
* delete worker, before thread is deleted
*/
delete m_worker;
/**
* delete other data
*/
delete m_file2Item;
}
bool KateProject::load (const QString &fileName)
......@@ -113,261 +113,41 @@ bool KateProject::reload ()
m_projectMap = globalProject;
/**
* now, clear model once and load other stuff that is possible in all groups
* now, clear some stuff and before triggering worker thread to do the work ;)
*/
m_model->clear ();
m_file2Item.clear ();
loadProject (m_model->invisibleRootItem(), globalProject);
/**
* done ok ;)
*/
return true;
}
void KateProject::loadProject (QStandardItem *parent, const QVariantMap &project)
{
/**
* recurse to sub-projects FIRST
*/
QVariantList subGroups = project["projects"].toList ();
foreach (const QVariant &subGroupVariant, subGroups) {
/**
* convert to map and get name, else skip
*/
QVariantMap subProject = subGroupVariant.toMap ();
if (subProject["name"].toString().isEmpty())
continue;
/**
* recurse
*/
QStandardItem *subProjectItem = new KateProjectItem (KateProjectItem::Project, subProject["name"].toString());
loadProject (subProjectItem, subProject);
parent->appendRow (subProjectItem);
}
/**
* load all specified files
*/
QVariantList files = project["files"].toList ();
foreach (const QVariant &fileVariant, files)
loadFilesEntry (parent, fileVariant.toMap ());
}
/**
* small helper to construct directory parent items
* @param dir2Item map for path => item
* @param path current path we need item for
* @return correct parent item for given path, will reuse existing ones
*/
static QStandardItem *directoryParent (QMap<QString, QStandardItem *> &dir2Item, QString path)
{
/**
* throw away simple /
*/
if (path == "/")
path = "";
/**
* quick check: dir already seen?
*/
if (dir2Item.contains (path))
return dir2Item[path];
/**
* else: construct recursively
*/
int slashIndex = path.lastIndexOf ('/');
/**
* no slash?
* simple, no recursion, append new item toplevel
*/
if (slashIndex < 0) {
dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, path);
dir2Item[""]->appendRow (dir2Item[path]);
return dir2Item[path];
}
/**
* else, split and recurse
*/
QString leftPart = path.left (slashIndex);
QString rightPart = path.right (path.size() - (slashIndex + 1));
m_file2Item->clear ();
/**
* special handling if / with nothing on one side are found
* trigger worker
*/
if (leftPart.isEmpty() || rightPart.isEmpty ())
return directoryParent (dir2Item, leftPart.isEmpty() ? rightPart : leftPart);
QMetaObject::invokeMethod (m_worker, "loadProject", Qt::QueuedConnection, Q_ARG(QString, m_fileName), Q_ARG(QVariantMap, m_projectMap));
/**
* else: recurse on left side
* done ok ;)
*/
dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, rightPart);
directoryParent (dir2Item, leftPart)->appendRow (dir2Item[path]);
return dir2Item[path];
return true;
}
void KateProject::loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry)
void KateProject::loadProjectDone (void *topLevel, void *file2Item)
{
/**
* get directory to open or skip
*/
QDir dir (QFileInfo (m_fileName).absoluteDir());
if (!dir.cd (filesEntry["directory"].toString()))
return;
/**
* get recursive attribute, default is TRUE
*/
const bool recursive = !filesEntry.contains ("recursive") || filesEntry["recursive"].toBool();
/**
* now: choose between different methodes to get files in the directory
*/
QStringList files;
/**
* use GIT
*/
if (filesEntry["git"].toBool()) {
/**
* try to run git with ls-files for this directory
*/
QProcess git;
git.setWorkingDirectory (dir.absolutePath());
QStringList args;
args << "ls-files" << ".";
git.start("git", args);
if (!git.waitForStarted() || !git.waitForFinished())
return;
/**
* get output and split up into files
*/
QStringList relFiles = QString::fromLocal8Bit (git.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);
/**
* prepend the directory path
*/
foreach (QString relFile, relFiles) {
/**
* skip non-direct files if not recursive
*/
if (!recursive && (relFile.indexOf ("/") != -1))
continue;
files.append (dir.absolutePath() + "/" + relFile);
}
}
/**
* use SVN
*/
else if (filesEntry["svn"].toBool()) {
/**
* try to run git with ls-files for this directory
*/
QProcess svn;
svn.setWorkingDirectory (dir.absolutePath());
QStringList args;
args << "list" << ".";
if (recursive)
args << "--depth=infinity";
svn.start("svn", args);
if (!svn.waitForStarted() || !svn.waitForFinished())
return;
/**
* get output and split up into files
*/
QStringList relFiles = QString::fromLocal8Bit (svn.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);
/**
* prepend the directory path
*/
foreach (QString relFile, relFiles)
files.append (dir.absolutePath() + "/" + relFile);
}
/**
* fallback to use QDirIterator and search files ourself!
*/
else {
/**
* default filter: only files!
*/
dir.setFilter (QDir::Files);
/**
* set name filters, if any
*/
QStringList filters = filesEntry["filters"].toStringList();
if (!filters.isEmpty())
dir.setNameFilters (filters);
/**
* construct flags for iterator
*/
QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
if (recursive)
flags = flags | QDirIterator::Subdirectories;
/**
* create iterator and collect all files
*/
QDirIterator dirIterator (dir, flags);
while (dirIterator.hasNext()) {
dirIterator.next();
files.append (dirIterator.filePath());
}
}
/**
* sort them
* convert to right types
*/
files.sort ();
QStandardItem *topLevelItem = static_cast<QStandardItem*> (topLevel);
QMap<QString, QStandardItem *> *file2ItemMap = static_cast<QMap<QString, QStandardItem *>*> (file2Item);
/**
* construct paths first in tree and items in a map
* setup model data
*/
QMap<QString, QStandardItem *> dir2Item;
dir2Item[""] = parent;
QList<QPair<QStandardItem *, QStandardItem *> > item2ParentPath;
foreach (QString filePath, files) {
/**
* get file info and skip NON-files
*/
QFileInfo fileInfo (filePath);
if (!fileInfo.isFile())
continue;
/**
* skip dupes
*/
if (m_file2Item.contains(filePath))
continue;
/**
* construct the item with right directory prefix
* already hang in directories in tree
*/
QStandardItem *fileItem = new KateProjectItem (KateProjectItem::File, fileInfo.fileName());
item2ParentPath.append (QPair<QStandardItem *, QStandardItem *>(fileItem, directoryParent(dir2Item, dir.relativeFilePath (fileInfo.absolutePath()))));
fileItem->setData (filePath, Qt::UserRole);
m_file2Item[filePath] = fileItem;
}
m_model->invisibleRootItem()->appendColumn (topLevelItem->takeColumn (0));
delete topLevelItem;
/**
* plug in the file items to the tree
* setup file => item map
*/
QList<QPair<QStandardItem *, QStandardItem *> >::const_iterator i = item2ParentPath.constBegin();
while (i != item2ParentPath.constEnd()) {
i->second->appendRow (i->first);
++i;
}
delete m_file2Item;
m_file2Item = file2ItemMap;
}
// kate: space-indent on; indent-width 2; replace-tabs on;
......@@ -25,7 +25,7 @@
#include <QStandardItemModel>
#include <QMap>
#include "kateprojectitem.h"
#include "kateprojectworker.h"
/**
* Class representing a project.
......@@ -102,7 +102,7 @@ class KateProject : public QObject
*/
QStringList files ()
{
return m_file2Item.keys ();
return m_file2Item->keys ();
}
/**
......@@ -112,24 +112,16 @@ class KateProject : public QObject
*/
QStandardItem *itemForFile (const QString &file)
{
return m_file2Item.value (file);
return m_file2Item->value (file);
}
private:
/**
* Load one project inside the project tree.
* Fill data from JSON storage to model and recurse to sub-projects.
* @param parent parent standard item in the model
* @param project variant map for this group
*/
void loadProject (QStandardItem *parent, const QVariantMap &project);
private Q_SLOTS:
/**
* Load one files entry in the current parent item.
* @param parent parent standard item in the model
* @param filesEntry one files entry specification to load
* Used for worker to send back the results of project loading
* @param topLevel new toplevel element for model
* @param file2Item new file => item mapping
*/
void loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry);
void loadProjectDone (void *topLevel, void *file2Item);
private:
/**
......@@ -140,7 +132,7 @@ class KateProject : public QObject
/**
* the worker inside the background thread
*/
class KateProjectWorker *m_worker;
QObject *m_worker;
/**
* project file name
......@@ -165,7 +157,7 @@ class KateProject : public QObject
/**
* mapping files => items
*/
QMap<QString, QStandardItem *> m_file2Item;
QMap<QString, QStandardItem *> *m_file2Item;
};
#endif
......
......@@ -20,7 +20,13 @@
#include "kateprojectworker.h"
KateProjectWorker::KateProjectWorker (KateProject *project)
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
KateProjectWorker::KateProjectWorker (QObject *project)
: QObject ()
, m_project (project)
{
......@@ -30,4 +36,276 @@ KateProjectWorker::~KateProjectWorker ()
{
}
void KateProjectWorker::loadProject (QString fileName, QVariantMap projectMap)
{
/**
* setup project file name
* name should be FIX after initial setting
*/
Q_ASSERT (m_fileName.isEmpty() || (m_fileName == fileName));
m_fileName = fileName;
/**
* Create dummy top level parent item and load the project recursively
*/
QStandardItem *topLevel = new QStandardItem ();
QMap<QString, QStandardItem *> *file2Item = new QMap<QString, QStandardItem *> ();
loadProject (topLevel, projectMap, *file2Item);
/**
* feed back our results
*/
/**
* trigger worker
*/
QMetaObject::invokeMethod (m_project, "loadProjectDone", Qt::QueuedConnection, Q_ARG(void *, topLevel), Q_ARG(void *, file2Item));
}
void KateProjectWorker::loadProject (QStandardItem *parent, const QVariantMap &project, QMap<QString, QStandardItem *> &file2Item)
{
/**
* recurse to sub-projects FIRST
*/
QVariantList subGroups = project["projects"].toList ();
foreach (const QVariant &subGroupVariant, subGroups) {
/**
* convert to map and get name, else skip
*/
QVariantMap subProject = subGroupVariant.toMap ();
if (subProject["name"].toString().isEmpty())
continue;
/**
* recurse
*/
QStandardItem *subProjectItem = new KateProjectItem (KateProjectItem::Project, subProject["name"].toString());
loadProject (subProjectItem, subProject, file2Item);
parent->appendRow (subProjectItem);
}
/**
* load all specified files
*/
QVariantList files = project["files"].toList ();
foreach (const QVariant &fileVariant, files)
loadFilesEntry (parent, fileVariant.toMap (), file2Item);
}
/**
* small helper to construct directory parent items
* @param dir2Item map for path => item
* @param path current path we need item for
* @return correct parent item for given path, will reuse existing ones
*/
static QStandardItem *directoryParent (QMap<QString, QStandardItem *> &dir2Item, QString path)
{
/**
* throw away simple /
*/
if (path == "/")
path = "";
/**
* quick check: dir already seen?
*/
if (dir2Item.contains (path))
return dir2Item[path];
/**
* else: construct recursively
*/
int slashIndex = path.lastIndexOf ('/');
/**
* no slash?
* simple, no recursion, append new item toplevel
*/
if (slashIndex < 0) {
dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, path);
dir2Item[""]->appendRow (dir2Item[path]);
return dir2Item[path];
}
/**
* else, split and recurse
*/
QString leftPart = path.left (slashIndex);
QString rightPart = path.right (path.size() - (slashIndex + 1));
/**
* special handling if / with nothing on one side are found
*/
if (leftPart.isEmpty() || rightPart.isEmpty ())
return directoryParent (dir2Item, leftPart.isEmpty() ? rightPart : leftPart);
/**
* else: recurse on left side
*/
dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, rightPart);
directoryParent (dir2Item, leftPart)->appendRow (dir2Item[path]);
return dir2Item[path];
}
void KateProjectWorker::loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry, QMap<QString, QStandardItem *> &file2Item)
{
/**
* get directory to open or skip
*/
QDir dir (QFileInfo (m_fileName).absoluteDir());
if (!dir.cd (filesEntry["directory"].toString()))
return;
/**
* get recursive attribute, default is TRUE
*/
const bool recursive = !filesEntry.contains ("recursive") || filesEntry["recursive"].toBool();
/**
* now: choose between different methodes to get files in the directory
*/
QStringList files;
/**
* use GIT
*/
if (filesEntry["git"].toBool()) {
/**
* try to run git with ls-files for this directory
*/
QProcess git;
git.setWorkingDirectory (dir.absolutePath());
QStringList args;
args << "ls-files" << ".";
git.start("git", args);
if (!git.waitForStarted() || !git.waitForFinished())
return;
/**
* get output and split up into files
*/
QStringList relFiles = QString::fromLocal8Bit (git.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);
/**
* prepend the directory path
*/
foreach (QString relFile, relFiles) {
/**
* skip non-direct files if not recursive
*/
if (!recursive && (relFile.indexOf ("/") != -1))
continue;
files.append (dir.absolutePath() + "/" + relFile);
}
}
/**
* use SVN
*/
else if (filesEntry["svn"].toBool()) {
/**
* try to run git with ls-files for this directory
*/
QProcess svn;
svn.setWorkingDirectory (dir.absolutePath());
QStringList args;
args << "list" << ".";
if (recursive)
args << "--depth=infinity";
svn.start("svn", args);
if (!svn.waitForStarted() || !svn.waitForFinished())
return;
/**
* get output and split up into files
*/
QStringList relFiles = QString::fromLocal8Bit (svn.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);
/**
* prepend the directory path
*/
foreach (QString relFile, relFiles)
files.append (dir.absolutePath() + "/" + relFile);
}
/**
* fallback to use QDirIterator and search files ourself!
*/
else {
/**
* default filter: only files!
*/
dir.setFilter (QDir::Files);
/**
* set name filters, if any
*/
QStringList filters = filesEntry["filters"].toStringList();
if (!filters.isEmpty())
dir.setNameFilters (filters);
/**
* construct flags for iterator
*/
QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
if (recursive)
flags = flags | QDirIterator::Subdirectories;
/**
* create iterator and collect all files
*/
QDirIterator dirIterator (dir, flags);
while (dirIterator.hasNext()) {
dirIterator.next();
files.append (dirIterator.filePath());
}
}
/**
* sort them
*/
files.sort ();
/**
* construct paths first in tree and items in a map
*/
QMap<QString, QStandardItem *> dir2Item;
dir2Item[""] = parent;
QList<QPair<QStandardItem *, QStandardItem *> > item2ParentPath;
foreach (QString filePath, files) {
/**
* get file info and skip NON-files
*/
QFileInfo fileInfo (filePath);
if (!fileInfo.isFile())
continue;
/**
* skip dupes
*/
if (file2Item.contains(filePath))
continue;
/**
* construct the item with right directory prefix
* already hang in directories in tree
*/
QStandardItem *fileItem = new KateProjectItem (KateProjectItem::File, fileInfo.fileName());
item2ParentPath.append (QPair<QStandardItem *, QStandardItem *>(fileItem, directoryParent(dir2Item, dir.relativeFilePath (fileInfo.absolutePath()))));
fileItem->setData (filePath, Qt::UserRole);
file2Item[filePath] = fileItem;
}
/**
* plug in the file items to the tree
*/
QList<QPair<QStandardItem *, QStandardItem *> >::const_iterator i = item2ParentPath.constBegin();
while (i != item2ParentPath.constEnd()) {
i->second->appendRow (i->first);
++i;
}
}
// kate: space-indent on; indent-width 2; replace-tabs on;
......@@ -21,9 +21,10 @@
#ifndef KATE_PROJECT_WORKER_H
#define KATE_PROJECT_WORKER_H
#include <QObject>
#include <QStandardItemModel>
#include <QMap>
#include "kateproject.h"
#include "kateprojectitem.h"
/**
* Class representing a project background worker.
......@@ -34,22 +35,60 @@ class KateProjectWorker : public QObject
Q_OBJECT
public:
/**
* Type for QueuedConnection
*/
typedef QMap<QString, QStandardItem *> MapString2Item;
/**
* construct project worker for given project
* @param project our project
*/
KateProjectWorker (KateProject *project);
KateProjectWorker (QObject *project);
/**
* deconstruct worker
*/
~KateProjectWorker ();
private Q_SLOTS:
/**
* Load the project.
* Will be used to load project in background.
* Will inform the project after loading was done and pass over all needed data!
* @param fileName project file name, should stay the same after initial setup
* @param projectMap full map containing the whole project as copy to work on
*/
void loadProject (QString fileName, QVariantMap projectMap);
private:
/**
* Load one project inside the project tree.
* Fill data from JSON storage to model and recurse to sub-projects.
* @param parent parent standard item in the model
* @param project variant map for this group
* @param file2Item mapping file => item, will be filled
*/
void loadProject (QStandardItem *parent, const QVariantMap &project, QMap<QString, QStandardItem *> &file2Item);
/**
* Load one files entry in the current parent item.
* @param parent parent standard item in the model
* @param filesEntry one files entry specification to load