示例#1
0
 template < class ResourceType, class LoaderType > static void RegisterLoader ( QMimeType fileType ) {
     SharedResourceLoaderPtr ldr = LoaderType::Instance ();
     mResourceLoaders [ fileType.name () ] = ldr;
 }
示例#2
0
QString ImageshackTalker::mimeType(const QString& path)
{
    QMimeDatabase db;
    QMimeType ptr = db.mimeTypeForUrl(QUrl::fromLocalFile(path));
    return ptr.name();
}
示例#3
0
文件: main.cpp 项目: BoltsJ/albert
/** ***************************************************************************/
vector<shared_ptr<Files::File>>
Files::FilesPrivate::indexFiles(const IndexSettings &indexSettings) const {

    // Get a new index
    std::vector<shared_ptr<File>> newIndex;
    std::set<QString> indexedDirs;
    QMimeDatabase mimeDatabase;
    std::vector<QRegExp> mimeFilters;
    for (const QString &re : indexSettings.filters)
        mimeFilters.emplace_back(re, Qt::CaseInsensitive, QRegExp::Wildcard);

    // Prepare the iterator properties
    QDir::Filters filters = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot;
    if (indexSettings.indexHidden)
        filters |= QDir::Hidden;

    // Anonymous function that implemnents the index recursion
    std::function<void(const QFileInfo&, vector<IgnoreEntry>)> indexRecursion =
            [&](const QFileInfo& fileInfo, const vector<IgnoreEntry> &ignoreEntries){

        if (abort) return;

        const QString canonicalPath = fileInfo.canonicalFilePath();
        const QMimeType mimetype = mimeDatabase.mimeTypeForFile(canonicalPath);
        const QString mimeName = mimetype.name();

        // If the file matches the index options, index it
        if ( std::any_of(mimeFilters.begin(), mimeFilters.end(),
                         [&](const QRegExp &re){ return re.exactMatch(mimeName); }) )
            newIndex.push_back(std::make_shared<File>(canonicalPath, mimetype));

        if (fileInfo.isDir()) {

            emit q->statusInfo(QString("Indexing %1.").arg(canonicalPath));

            // Skip if this dir has already been indexed
            if ( indexedDirs.find(canonicalPath) != indexedDirs.end() )
                return;

            // Remember that this dir has been indexed to avoid loops
            indexedDirs.insert(canonicalPath);

            // Read the ignore file, see http://doc.qt.io/qt-5/qregexp.html#wildcard-matching
            vector<IgnoreEntry> localIgnoreEntries = ignoreEntries;
            QFile file(QDir(canonicalPath).filePath(IGNOREFILE));
            if ( file.open(QIODevice::ReadOnly | QIODevice::Text) ) {
                QTextStream in(&file);
                while ( !in.atEnd() ) {
                    QString pattern = QDir::cleanPath(in.readLine());

                    if ( pattern.isEmpty() || pattern.startsWith("#") )
                        continue;

                    // Replace ** and * by their regex analogons
                    pattern.replace(QRegularExpression("(?<!\\*)\\*(?!\\*)"), "[^\\/]*");
                    pattern.replace(QRegularExpression("\\*{2,}"), ".*");

                    // Determine pattern type
                    PatternType patternType = PatternType::Exclude;
                    if ( pattern.startsWith('!') ) {
                        patternType = PatternType::Include;
                        pattern = pattern.mid(1, -1);
                    }

                    // Respect files beginning with excalmation mark
                    if ( pattern.startsWith("\\!") )
                        pattern = pattern.mid(1, -1);

                    if ( pattern.startsWith("/") ) {
                        pattern = QString("^%1$").arg(QDir(fileInfo.filePath()).filePath(pattern.mid(1, -1)));
                        localIgnoreEntries.emplace_back(QRegularExpression(pattern), patternType);
                    } else {
                        pattern = QString("%1$").arg(pattern);
                        localIgnoreEntries.emplace_back(QRegularExpression(pattern), patternType);
                    }
                }
                file.close();
            }

            // Index all children in the dir
            QDirIterator dirIterator(canonicalPath, filters, QDirIterator::NoIteratorFlags);
            while ( dirIterator.hasNext() ) {
                dirIterator.next();
                const QFileInfo & fileInfo = dirIterator.fileInfo();

                // Skip if this file depending on ignore patterns
                PatternType patternType = PatternType::Include;
                for ( const IgnoreEntry &ignoreEntry : localIgnoreEntries )
                    if ( ignoreEntry.regex.match(fileInfo.filePath()).hasMatch() )
                        patternType = ignoreEntry.type;
                if ( patternType == PatternType::Exclude )
                    continue;

                // Skip if this file is a symlink and we should skip symlinks
                if ( fileInfo.isSymLink() && !indexSettings.followSymlinks )
                    continue;

                // Index this file
                indexRecursion(fileInfo, localIgnoreEntries);
            }
        }
    };

    // Start the indexing
    for ( const QString &rootDir : indexSettings.rootDirs ) {
        // Index rootdir, ignore ignorefile by default
        vector<IgnoreEntry> ignores = {IgnoreEntry(
                                       QRegularExpression(QString("%1$").arg(IGNOREFILE)),
                                       PatternType::Exclude)};
        indexRecursion(QFileInfo(rootDir), ignores);
        if ( abort )
            return vector<shared_ptr<Files::File>>();
    }

    // Serialize data
    QFile file(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).
                   filePath(QString("%1.txt").arg(q->Core::Extension::id)));
    if ( file.open(QIODevice::WriteOnly|QIODevice::Text) ) {
        qDebug() << qPrintable(QString("Serializing files to '%1'").arg(file.fileName()));
        QTextStream out(&file);
        for (const shared_ptr<File> &item : newIndex)
            out << item->path() << endl << item->mimetype().name() << endl;
    } else
        qWarning() << qPrintable(QString("Could not write to file '%1': %2").arg(file.fileName(), file.errorString()));

    return newIndex;
}
示例#4
0
void KRSearchMod::scanLocalDir(QUrl urlToScan)
{
    QString dir = vfs::ensureTrailingSlash(urlToScan).path();

    QT_DIR* d = QT_OPENDIR(dir.toLocal8Bit());
    if (!d) return ;

    QT_DIRENT* dirEnt;

    while ((dirEnt = QT_READDIR(d)) != NULL) {
        QString name = QString::fromLocal8Bit(dirEnt->d_name);

        // we don't scan the ".",".." enteries
        if (name == "." || name == "..") continue;

        QT_STATBUF stat_p;
        QT_LSTAT((dir + name).toLocal8Bit(), &stat_p);

        QUrl url = QUrl::fromLocalFile(dir + name);

        QString mime;
        if (query->searchInArchives() || !query->hasMimeType()) {
            QMimeDatabase db;
            QMimeType mt = db.mimeTypeForUrl(url);
            if (mt.isValid())
                mime = mt.name();
        }

        // creating a vfile object for matching with krquery
        vfile * vf = new vfile(name, (KIO::filesize_t)stat_p.st_size, KRpermHandler::mode2QString(stat_p.st_mode),
                               stat_p.st_mtime, S_ISLNK(stat_p.st_mode), false/*FIXME*/, stat_p.st_uid, stat_p.st_gid,
                               mime, "", stat_p.st_mode);
        vf->vfile_setUrl(url);

        if (query->isRecursive()) {
            if (S_ISLNK(stat_p.st_mode) && query->followLinks())
                unScannedUrls.push(QUrl::fromLocalFile(QDir(dir + name).canonicalPath()));
            else if (S_ISDIR(stat_p.st_mode))
                unScannedUrls.push(url);
        }
        if (query->searchInArchives()) {
            if (KRarcHandler::arcSupported(mime)) {
                QUrl archiveURL = url;
                bool encrypted;
                QString realType = arcHandler.getType(encrypted, url.path(), mime);

                if (!encrypted) {
                    if (realType == "tbz" || realType == "tgz" || realType == "tarz" || realType == "tar" || realType == "tlz")
                        archiveURL.setScheme("tar");
                    else
                        archiveURL.setScheme("krarc");

                    unScannedUrls.push(archiveURL);
                }
            }
        }

        if (query->match(vf)) {
            // if we got here - we got a winner
            results.append(dir + name);
            emit found(name, dir, (KIO::filesize_t) stat_p.st_size, stat_p.st_mtime,
                       KRpermHandler::mode2QString(stat_p.st_mode), stat_p.st_uid, stat_p.st_gid, query->foundText());
        }
        delete vf;

        if (timer.elapsed() >= EVENT_PROCESS_DELAY) {
            qApp->processEvents();
            timer.start();
            if (stopSearch) return;
        }
    }
    // clean up
    QT_CLOSEDIR(d);
}
示例#5
0
shared_ptr<ZLMimeType> QtZLFSManager::mimeType(const std::string &path) const
{
    QMimeDatabase database;
    QMimeType type = database.mimeTypeForFile(QString::fromStdString(path));
    return ZLMimeType::get(type.name().toStdString());
}
示例#6
0
QString ContentList::getMimetype(QString filePath)
{
    QMimeDatabase db;
    QMimeType mime = db.mimeTypeForFile(filePath);
    return mime.name();
}
示例#7
0
bool PackageJobThread::installPackage(const QString &src, const QString &dest, OperationType operation)
{
    QDir root(dest);
    if (!root.exists()) {
        QDir().mkpath(dest);
        if (!root.exists()) {
            d->errorMessage = i18n("Could not create package root directory: %1", dest);
            d->errorCode = Package::JobError::RootCreationError;
            //qWarning() << "Could not create package root directory: " << dest;
            return false;
        }
    }

    QFileInfo fileInfo(src);
    if (!fileInfo.exists()) {
        d->errorMessage = i18n("No such file: %1", src);
        d->errorCode = Package::JobError::PackageFileNotFoundError;
        return false;
    }

    QString path;
    QTemporaryDir tempdir;
    bool archivedPackage = false;

    if (fileInfo.isDir()) {
        // we have a directory, so let's just install what is in there
        path = src;
        // make sure we end in a slash!
        if (!path.endsWith('/')) {
            path.append('/');
        }
    } else {
        KArchive *archive = 0;
        QMimeDatabase db;
        QMimeType mimetype = db.mimeTypeForFile(src);
        if (mimetype.inherits(QStringLiteral("application/zip"))) {
            archive = new KZip(src);
        } else if (mimetype.inherits(QStringLiteral("application/x-compressed-tar")) ||
                   mimetype.inherits(QStringLiteral("application/x-tar")) ||
                   mimetype.inherits(QStringLiteral("application/x-bzip-compressed-tar")) ||
                   mimetype.inherits(QStringLiteral("application/x-xz")) ||
                   mimetype.inherits(QStringLiteral("application/x-lzma"))) {
            archive = new KTar(src);
        } else {
            //qWarning() << "Could not open package file, unsupported archive format:" << src << mimetype.name();
            d->errorMessage = i18n("Could not open package file, unsupported archive format: %1 %2", src, mimetype.name());
            d->errorCode = Package::JobError::UnsupportedArchiveFormatError;
            return false;
        }

        if (!archive->open(QIODevice::ReadOnly)) {
            //qWarning() << "Could not open package file:" << src;
            delete archive;
            d->errorMessage = i18n("Could not open package file: %1", src);
            d->errorCode = Package::JobError::PackageOpenError;
            return false;
        }

        archivedPackage = true;
        path = tempdir.path() + '/';

        d->installPath = path;

        const KArchiveDirectory *source = archive->directory();
        source->copyTo(path);

        QStringList entries = source->entries();
        if (entries.count() == 1) {
            const KArchiveEntry *entry = source->entry(entries[0]);
            if (entry->isDirectory()) {
                path.append(entry->name()).append("/");
            }
        }

        delete archive;
    }

    QDir packageDir(path);
    QFileInfoList entries = packageDir.entryInfoList(*metaDataFiles);
    KPluginMetaData meta;
    if (!entries.isEmpty()) {
        const QString metadataFilePath = entries.first().filePath();
        if (metadataFilePath.endsWith(QLatin1String(".desktop")))
            meta = KPluginMetaData(metadataFilePath);
        else {
            QFile f(metadataFilePath);
            if(!f.open(QIODevice::ReadOnly)){
                qWarning() << "Couldn't open metadata file" << src << path;
                d->errorMessage = i18n("Could not open metadata file: %1", src);
                d->errorCode = Package::JobError::MetadataFileMissingError;
                return false;
            }
            QJsonObject metadataObject = QJsonDocument::fromJson(f.readAll()).object();
            meta = KPluginMetaData(metadataObject, QString(), metadataFilePath);
        }
    }

    if (!meta.isValid()) {
        qDebug() << "No metadata file in package" << src << path;
        d->errorMessage = i18n("No metadata file in package: %1", src);
        d->errorCode = Package::JobError::MetadataFileMissingError;
        return false;
    }


    QString pluginName = meta.pluginId();
    qDebug() << "pluginname: " << meta.pluginId();
    if (pluginName.isEmpty()) {
        //qWarning() << "Package plugin name not specified";
        d->errorMessage = i18n("Package plugin name not specified: %1", src);
        d->errorCode = Package::JobError::PluginNameMissingError;
        return false;
    }

    // Ensure that package names are safe so package uninstall can't inject
    // bad characters into the paths used for removal.
    QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period.
    if (!validatePluginName.exactMatch(pluginName)) {
        //qDebug() << "Package plugin name " << pluginName << "contains invalid characters";
        d->errorMessage = i18n("Package plugin name %1 contains invalid characters", pluginName);
        d->errorCode = Package::JobError::PluginNameInvalidError;
        return false;
    }

    QString targetName = dest;
    if (targetName[targetName.size() - 1] != '/') {
        targetName.append('/');
    }
    targetName.append(pluginName);

    if (QFile::exists(targetName)) {
        if (operation == Update) {
            KPluginMetaData oldMeta(targetName + QLatin1String("/metadata.desktop"));

            if (oldMeta.serviceTypes() != meta.serviceTypes()) {
                d->errorMessage = i18n("The new package has a different type from the old version already installed.", meta.version(), meta.pluginId(), oldMeta.version());
                d->errorCode = Package::JobError::UpdatePackageTypeMismatchError;
            } else if (isVersionNewer(oldMeta.version(), meta.version())) {
                const bool ok = uninstallPackage(targetName);
                if (!ok) {
                    d->errorMessage = i18n("Impossible to remove the old installation of %1 located at %2. error: %3", pluginName, targetName, d->errorMessage);
                    d->errorCode = Package::JobError::OldVersionRemovalError;
                }
            } else {
                d->errorMessage = i18n("Not installing version %1 of %2. Version %3 already installed.", meta.version(), meta.pluginId(), oldMeta.version());
                d->errorCode = Package::JobError::NewerVersionAlreadyInstalledError;
            }
        } else {
            d->errorMessage = i18n("%1 already exists", targetName);
            d->errorCode = Package::JobError::PackageAlreadyInstalledError;
        }

        if (d->errorCode != KJob::NoError) {
            d->installPath = targetName;
            return false;
        }
    }

    //install dependencies
    const QStringList dependencies = KPluginMetaData::readStringList(meta.rawData(), QStringLiteral("X-KPackage-Dependencies"));
    for(const QString &dep : dependencies) {
        QUrl depUrl(dep);
        if (!installDependency(depUrl)) {
            d->errorMessage = i18n("Could not install dependency: %1", dep);
            d->errorCode = Package::JobError::PackageCopyError;
            return false;
        }
    }

    if (archivedPackage) {
        // it's in a temp dir, so just move it over.
        const bool ok = copyFolder(path, targetName);
        removeFolder(path);
        if (!ok) {
            //qWarning() << "Could not move package to destination:" << targetName;
            d->errorMessage = i18n("Could not move package to destination: %1", targetName);
            d->errorCode = Package::JobError::PackageMoveError;
            return false;
        }
    } else {
        // it's a directory containing the stuff, so copy the contents rather
        // than move them
        const bool ok = copyFolder(path, targetName);
        if (!ok) {
            //qWarning() << "Could not copy package to destination:" << targetName;
            d->errorMessage = i18n("Could not copy package to destination: %1", targetName);
            d->errorCode = Package::JobError::PackageCopyError;
            return false;
        }
    }

    if (archivedPackage) {
        // no need to remove the temp dir (which has been successfully moved if it's an archive)
        tempdir.setAutoRemove(false);
    }


    indexDirectory(dest, QStringLiteral("kpluginindex.json"));


    d->installPath = targetName;

    //qWarning() << "Not updating kbuildsycoca4, since that will go away. Do it yourself for now if needed.";
    return true;
}
void KateProjectTreeViewContextMenu::exec(const QString &filename, const QPoint &pos, QWidget *parent)
{
    /**
     * create context menu
     */
    QMenu menu;

    QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy Filename"));

    /**
     * handle "open with"
     * find correct mimetype to query for possible applications
     */
    QMenu *openWithMenu = menu.addMenu(i18n("Open With"));
    QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filename);
    KService::List offers = KMimeTypeTrader::self()->query(mimeType.name(), QStringLiteral("Application"));

    /**
     * for each one, insert a menu item...
     */
    for (KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) {
        KService::Ptr service = *it;
        if (service->name() == QStringLiteral("Kate")) {
            continue;    // omit Kate
        }
        QAction *action = openWithMenu->addAction(QIcon::fromTheme(service->icon()), service->name());
        action->setData(service->entryPath());
    }

    /**
     * perhaps disable menu, if no entries!
     */
    openWithMenu->setEnabled(!openWithMenu->isEmpty());

    KMoreToolsMenuFactory menuFactory(QLatin1String("kate/addons/project/git-tools"));

    if (isGit(filename)) {

        auto gitMenu = menuFactory.createMenuFromGroupingNames({ QLatin1String("git-clients-and-actions") },
                                                               QUrl::fromLocalFile(filename));

        menu.addSection(i18n("Git:"));
        Q_FOREACH(auto action, gitMenu->actions()) {
            menu.addAction(action);
        }
    }

    /**
     * run menu and handle the triggered action
     */
    if (QAction *action = menu.exec(pos)) {

        // handle apps
        if (copyAction == action) {
            QApplication::clipboard()->setText(filename);
        } else {
            // handle "open with"
            const QString openWith = action->data().toString();
            if (KService::Ptr app = KService::serviceByDesktopPath(openWith)) {
                QList<QUrl> list;
                list << QUrl::fromLocalFile(filename);
                KRun::runService(*app, list, parent);
            }
        }
    }
}
示例#9
0
/** ***************************************************************************/
void Files::Extension::Indexer::run() {

    // Notification
    qDebug("[%s] Start indexing in background thread", extension_->name_);
    emit statusInfo("Indexing files ...");

    // Prepare the iterator properties
    QDir::Filters filters = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot;
    if (extension_->indexHidden_)
        filters |= QDir::Hidden;

    // Get a new index
    std::vector<shared_ptr<File>> newIndex;
    std::set<QString> indexedDirs;


    // Anonymous function that implemnents the index recursion
    std::function<void(const QFileInfo&)> indexRecursion =
            [this, &newIndex, &indexedDirs, &filters, &indexRecursion](const QFileInfo& fileInfo){
        if (abort_) return;

        const QString canonicalPath = fileInfo.canonicalFilePath();


        if (fileInfo.isFile()) {

            // If the file matches the index options, index it
            QMimeType mimetype = mimeDatabase_.mimeTypeForFile(canonicalPath);
            const QString mimeName = mimetype.name();
            if ((extension_->indexAudio_ && mimeName.startsWith("audio"))
                    ||(extension_->indexVideo_ && mimeName.startsWith("video"))
                    ||(extension_->indexImage_ && mimeName.startsWith("image"))
                    ||(extension_->indexDocs_ &&
                       (mimeName.startsWith("application") || mimeName.startsWith("text")))) {
                newIndex.push_back(std::make_shared<File>(canonicalPath, mimetype));
            }
        } else if (fileInfo.isDir()) {

            emit statusInfo(QString("Indexing %1.").arg(canonicalPath));

            // Skip if this dir has already been indexed
            if (indexedDirs.find(canonicalPath)!=indexedDirs.end())
                return;

            // Remember that this dir has been indexed to avoid loops
            indexedDirs.insert(canonicalPath);

            // If the dir matches the index options, index it
            if (extension_->indexDirs_) {
                QMimeType mimetype = mimeDatabase_.mimeTypeForFile(canonicalPath);
                newIndex.push_back(std::make_shared<File>(canonicalPath, mimetype));
            }

            // Ignore ignorefile by default
            std::vector<QRegExp> ignores;
            ignores.push_back(QRegExp(extension_->IGNOREFILE, Qt::CaseSensitive, QRegExp::Wildcard));

            // Read the ignore file, see http://doc.qt.io/qt-5/qregexp.html#wildcard-matching
            QFile file(QDir(canonicalPath).filePath(extension_->IGNOREFILE));
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream in(&file);
                while (!in.atEnd())
                    ignores.push_back(QRegExp(in.readLine().trimmed(), Qt::CaseSensitive, QRegExp::Wildcard));
                file.close();
            }

            // Index all children in the dir
            QDirIterator dirIterator(canonicalPath, filters, QDirIterator::NoIteratorFlags);
            while (dirIterator.hasNext()) {
                const QString fileName = dirIterator.next();

                // Skip if this file matches one of the ignore patterns
                for (const QRegExp& ignore : ignores)
                    if(ignore.exactMatch(fileName))
                        goto SKIP_THIS;

                // Skip if this file is a symlink and we shoud skip symlinks
                if (dirIterator.fileInfo().isSymLink() && !extension_->followSymlinks_)
                    goto SKIP_THIS;

                // Index this file
                indexRecursion(dirIterator.fileInfo());
                SKIP_THIS:;
            }
        }
    };


    // Start the indexing
    for (const QString &rootDir : extension_->rootDirs_) {
        indexRecursion(QFileInfo(rootDir));
        if (abort_) return;
    }


    // Sort the new index for linear usage copy [O(n*log(n))]
    emit statusInfo("Sorting ... ");
    std::sort(newIndex.begin(), newIndex.end(), [](const shared_ptr<File> &lhs, const shared_ptr<File> &rhs) {
                  return QString::compare(lhs->path(), rhs->path(), Qt::CaseInsensitive) < 0;
              });


    // Copy the usagecounters  [O(n)]
    emit statusInfo("Copy usage statistics ... ");
    size_t i=0, j=0;
    while (i < extension_->index_.size() && j < newIndex.size()) {
        if (extension_->index_[i]->path() == newIndex[j]->path()) {
            newIndex[j]->setUsage(extension_->index_[i]->usage());
            ++i;++j;
        } else if (extension_->index_[i]->path() < newIndex[j]->path()) {
            ++i;
        } else {// if ((*_fileIndex)[i]->path > (*newIndex)[j]->path) {
            ++j;
        }
    }

    /*
     *  ▼ CRITICAL ▼
     */

    // Lock the access
    QMutexLocker locker(&extension_->indexAccess_);

    // Abortion requested while block
    if (abort_)
        return;

    // Set the new index (use swap to shift destruction out of critical area)
    std::swap(extension_->index_, newIndex);

    // Rebuild the offline index
    extension_->offlineIndex_.clear();
    for (auto &item : extension_->index_)
        extension_->offlineIndex_.add(item);

    // Notification
    qDebug("[%s] Indexing done (%d items)", extension_->name_, static_cast<int>(extension_->index_.size()));
    emit statusInfo(QString("Indexed %1 files").arg(extension_->index_.size()));
}
示例#10
0
/** ***************************************************************************/
void Files::Indexer::run() {

    // Notification
    QString msg("Indexing files ...");
    emit statusInfo(msg);
    qDebug() << "[Files]" << msg;


    // Prepare the iterator properties
    QDir::Filters filters = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot;
    if (_extension->_indexHidden)
        filters |= QDir::Hidden;
    QDirIterator::IteratorFlags flags;
    if (_extension->_followSymlinks)
        flags = QDirIterator::FollowSymlinks;


    // Get a new index
    std::vector<shared_ptr<File>> newIndex;
    std::set<QString> indexedDirs;


    // Anonymous function that implemnents the index recursion
    std::function<void(const QFileInfo&)> indexRecursion =
            [this, &newIndex, &indexedDirs, &filters, &flags, &indexRecursion](const QFileInfo& fileInfo){
        if (_abort) return;

        QString canonicalPath = fileInfo.canonicalFilePath();


        if (fileInfo.isFile()) {

            // If the file matches the index options, index it
            QMimeType mimetype = _mimeDatabase.mimeTypeForFile(canonicalPath);
            QString mimeName = mimetype.name();
            if ((_extension->_indexAudio && mimeName.startsWith("audio"))
                    ||(_extension->_indexVideo && mimeName.startsWith("video"))
                    ||(_extension->_indexImage && mimeName.startsWith("image"))
                    ||(_extension->_indexDocs &&
                       (mimeName.startsWith("application") || mimeName.startsWith("text")))) {
                newIndex.push_back(std::make_shared<File>(canonicalPath, mimetype));
            }
        } else if (fileInfo.isDir()) {

            emit statusInfo(QString("Indexing %1.").arg(canonicalPath));

            // Skip if this dir has already been indexed
            if (indexedDirs.find(canonicalPath)!=indexedDirs.end()){
                return;
            }

            // If the dir matches the index options, index it
            if (_extension->_indexDirs) {
                QMimeType mimetype = _mimeDatabase.mimeTypeForFile(canonicalPath);
                newIndex.push_back(std::make_shared<File>(canonicalPath, mimetype));
            }

            // Ignore ignorefile by default
            std::vector<QRegExp> ignores;
            ignores.push_back(QRegExp(_extension->IGNOREFILE, Qt::CaseSensitive, QRegExp::Wildcard));

            // Read the ignore file, see http://doc.qt.io/qt-5/qregexp.html#wildcard-matching
            QFile file(QDir(canonicalPath).filePath(_extension->IGNOREFILE));
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream in(&file);
                while (!in.atEnd())
                    ignores.push_back(QRegExp(in.readLine().trimmed(), Qt::CaseSensitive, QRegExp::Wildcard));
                file.close();
            }

            // Index all children in the dir
            QDirIterator dirIterator(canonicalPath, filters, flags);
            while (dirIterator.hasNext()) {
                dirIterator.next();

                // Skip if this file matches one of the ignore patterns
                for (QRegExp& ignore : ignores){
                    QString s = dirIterator.fileName(); // This is insane works only if its a lvalue
                    if(ignore.exactMatch(s))
                        goto SKIP_THIS;
                }

                // Index this file
                indexRecursion(dirIterator.fileInfo());
                SKIP_THIS:;
            }

            // Remember that this dir has been indexed to avoid loops
            indexedDirs.insert(canonicalPath);
        }
    };


    // Start the indexing
    for (const QString& rootDir : _extension->_rootDirs) {
        indexRecursion(QFileInfo(rootDir));
        if (_abort) return;
    }


    // Sort the new index for linear usage copy [O(n*log(n))]
    emit statusInfo("Sorting ... ");
    std::sort(newIndex.begin(), newIndex.end(), [](const shared_ptr<File> &lhs, const shared_ptr<File> &rhs) {
                  return QString::compare(lhs->path(), rhs->path(), Qt::CaseInsensitive) < 0;
              });


    // Copy the usagecounters  [O(n)]
    emit statusInfo("Copy usage statistics ... ");
    size_t i=0, j=0;
    while (i < _extension->_fileIndex.size() && j < newIndex.size()) {
        if (_extension->_fileIndex[i]->path_ == newIndex[j]->path_) {
            newIndex[j]->usage_ = _extension->_fileIndex[i]->usage_;
            ++i;++j;
        } else if (_extension->_fileIndex[i]->path_ < newIndex[j]->path_) {
            ++i;
        } else {// if ((*_fileIndex)[i]->path > (*newIndex)[j]->path) {
            ++j;
        }
    }

    /*
     *  ▼ CRITICAL ▼
     */

    // Lock the access
    _extension->_indexAccess.lock();

    // Set the new index
    _extension->_fileIndex = std::move(newIndex);

    // Reset the offline index
    emit statusInfo("Build offline index... ");
    _extension->_searchIndex.clear();

    // Build the new offline index
    for (shared_ptr<IIndexable> i : _extension->_fileIndex)
        _extension->_searchIndex.add(i);

    // Unlock the accress
    _extension->_indexAccess.unlock();

    /*
     *  ▲ CRITICAL ▲
     */


    // Notification
    msg = QString("Indexed %1 files.").arg(_extension->_fileIndex.size());
    emit statusInfo(msg);
    qDebug() << "[Files]" << msg;
}