Stuffed another memory leak
[mdictionary] / trunk / src / base / backbone / backbone.cpp
index 7500884..360448d 100644 (file)
     Copyright 2010 Comarch S.A.
 
 *******************************************************************************/
+/*! /file backbone.cpp
+\brief Backbone/core main file \see Backbone
 
-// Created by Bartosz Szatkowski
+
+\author Bartosz Szatkowski <bulislaw@linux.com>
+*/
 
 #include "backbone.h"
+#include <QDebug>
+
+int Backbone::_searchLimit;
+
+// Sadly QtConcurent mapped dont let me use something like calling method of
+// some class with supplied argument
+QString mappedSearch;
+QList<Translation*> mapSearch(CommonDictInterface *dict) {
+    if(dict)
+        return dict->searchWordList(mappedSearch, Backbone::_searchLimit);
+    return QList<Translation*>();
+}
+
+class TranslationPtr {
+    Translation* _tr;
+public:
+    TranslationPtr(Translation* tr) :_tr(tr) {}
+    QString toHtml() const {
+        QString trans;
+        trans = _tr->toHtml();
+        return trans;
+
+    }
+};
+
+void Backbone::init() {
+
+   if(!_configPath.size())
+       _configPath = QDir::homePath() + "/.mdictionary/mdictionary.config";
+   if(!_defaultConfigPath.size())
+       _defaultConfigPath = QDir::homePath() + "/.mdictionary/mdictionary.defaults";
+   if(!_pluginPath.size())
+       _pluginPath = "/usr/lib/mdictionary";
+   _historyLen = 10;
+   _searchLimit = 15;
+
+   loadPrefs(_defaultConfigPath);
+   _defaultPluginPath = _pluginPath;
+   _defaultHistoryLen = _historyLen;
+   _defaultSearchLimit = _searchLimit;
+   loadPrefs(_configPath);
 
-Backbone::Backbone(QObject *parent)
+   loadPlugins();
+
+   loadDicts(_defaultConfigPath, true);
+   loadDicts(_configPath);
+
+   connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
+   connect(&_htmlResultWatcher, SIGNAL(finished()), this,
+           SLOT(htmlTranslationReady()));
+   connect(&_bookmarkWatcher, SIGNAL(finished()), this,
+           SLOT(bookmarksListReady()));
+   connect(&_bookmarkSearchWatcher, SIGNAL(finished()), this,
+           SLOT(translationReady()));
+
+   QThreadPool::globalInstance()->setMaxThreadCount(
+           QThreadPool::globalInstance()->maxThreadCount()+1);
+
+   _history = new History(5, this);
+   _dictNum = 0;
+}
+
+
+
+Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
+                   QObject *parent)
     : QObject(parent)
 {
-   searchLimitv = 10;
+    _pluginPath = pluginPath;
+    _configPath = configPath;
+    _defaultConfigPath = configPath;
+    dryRun = false;
+    if(dry)
+        dryRun = true;
+    init();
 }
 
+
+
 Backbone::~Backbone()
 {
+    QListIterator<CommonDictInterface*> it(_dicts.keys());
+
+    while(it.hasNext())
+        delete it.next();
+
+    it = QListIterator<CommonDictInterface*>(_plugins);
+    while(it.hasNext())
+        delete it.next();
+
+    QHashIterator<QString, Translation*> it2(_result);
+    while(it2.hasNext())
+        delete it2.next().value();
 
 }
 
-Backbone::Backbone(const Backbone &b){
-    dicts = QHash<CommonDictInterface*, bool > (b.dicts);
-    plugins = QList<CommonDictInterface* > (b.plugins);
-    resultv = QHash<QString, Translation* > (b.resultv);
-    searchLimitv = b.searchLimit();
+
+
+
+Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
+    _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
+    _plugins = QList<CommonDictInterface* > (b._plugins);
+    _result = QHash<QString, Translation* > (b._result);
+    _searchLimit = b.searchLimit();
 }
 
+
+
+
 int Backbone::searchLimit() const {
-    return searchLimitv;
+    return _searchLimit;
 }
 
+
+
 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
-    return dicts;
+    return _dicts;
 }
 
+
+
 QList<CommonDictInterface* > Backbone::getPlugins() {
-    return plugins;
+    return _plugins;
 }
 
-QList<QString> Backbone::getHistory() {
-    //TODO code needed
+
+
+History* Backbone::history() {
+    return _history;
 }
 
-QHash<QString, Translation*> Backbone::result() {
-    return resultv;
+
+
+QMultiHash<QString, Translation*> Backbone::result() {
+    return _result;
 }
 
+
+
 void Backbone::stopSearching() {
-    foreach(CommonDictInterface* dict, dicts.keys())
+    if(stopped)
+        return;
+
+    foreach(CommonDictInterface* dict, _dicts.keys())
         dict->stop();
+    stopped = true;
+    _innerHtmlResult.cancel();
+    _innerResult.cancel();
+    Q_EMIT searchCanceled();
 }
 
-void Backbone::search(QString word) {
-    //TODO add running searches in new threads
-    foreach(CommonDictInterface* dict, dicts.keys())
-        if(dicts[dict] == 1)
-            dict->search(word, searchLimit());
+
+
+void Backbone::search(QString word){
+    _result.clear();
+    mappedSearch = word.toLower();
+
+    stopped = false;
+    dictFin = !_searchDicts;
+    bookmarkFin = !_searchBookmarks;
+
+    if (_searchDicts) {
+        _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
+        _resultWatcher.setFuture(_innerResult);
+    }
+
+    if(_searchBookmarks) {
+        _innerBookmarks = QtConcurrent::run(_bookmarks,
+                &Bookmarks::searchWordList, word);
+        _bookmarkSearchWatcher.setFuture(_innerBookmarks);
+    }
 }
 
- void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
-     foreach(CommonDictInterface* dict, dicts.keys())
-         if(activeDicts.contains(dict))
-             dicts[dict] = 1;
-         else
-             dicts[dict] = 0;
+
+
+void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
+    foreach(CommonDictInterface* dict, _dicts.keys())
+        if(activeDicts.contains(dict))
+            _dicts[dict] = 1;
+        else
+            _dicts[dict] = 0;
+    dictUpdated();
  }
 
- void Backbone::addDictionary(CommonDictInterface* dict) {
-     dicts[dict] = 1;
+
+
+void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
+    addInternalDictionary(dict,active);
+    dictUpdated();
+}
+
+
+
+ void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
+     dict->setHash(++_dictNum);
+     _dicts[dict] = active;
+     connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
+     connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
+             SIGNAL(notify(Notify::NotifyType,QString)));
  }
 
+ void Backbone::removeDictionary(CommonDictInterface *dict) {
+     _dicts.remove(dict);
+     delete dict;
+     dictUpdated();
+
+ }
+
+
+
  void Backbone::quit() {
     stopSearching();
     Q_EMIT closeOk();
@@ -92,4 +243,314 @@ void Backbone::search(QString word) {
 
 
 
+void Backbone::translationReady() {
+    if(!dictFin && _innerResult.isFinished()) {
+        dictFin = 1;
+        QFutureIterator<QList<Translation*> > it(_innerResult);
+
+        while(it.hasNext()) {
+            QList<Translation* > list = it.next();
+            foreach(Translation* trans, list)
+                _result.insert(trans->key().toLower(), trans);
+        }
+    }
+
+    if(!bookmarkFin && _innerBookmarks.isFinished()) {
+        bookmarkFin = 1;
+        QList<Translation*> list = _innerBookmarks.result();
+
+        foreach(Translation* trans, list)
+                _result.insert(trans->key().toLower(), trans);
+    }
+
+    if(!stopped && bookmarkFin && dictFin)
+        Q_EMIT ready();
+}
+
+QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
+    QDir plug(QDir::toNativeSeparators(dir));
+    if(!plug.exists()) {
+        qDebug() << plug.absolutePath() << " folder dosen't exists";
+        Q_EMIT notify(Notify::Warning,
+                QString("%1 folder dosen't exists.").arg(plug.path()));
+        return QStringList();
+    }
+    plug.setFilter(QDir::Files);
+    QStringList list = plug.entryList(nameFilter);
+
+    for(int i = 0; i < list.size(); i++)
+        list[i] = plug.absoluteFilePath(list.at(i));
+    return list;
+}
+
+
+void Backbone::loadPlugins() {
+    if(dryRun)
+        return;
+    QStringList nameFilter;
+    nameFilter << "*.so";
+    QStringList files = getFilesFromDir(_pluginPath, nameFilter);
+
+    foreach(QString file, files) {
+        QPluginLoader loader(file);
+        if(!loader.load()) {
+            Q_EMIT notify(Notify::Error,
+                    QString("%1 plugin cannot be loaded: %2.")
+                    .arg(file).arg(loader.errorString()));
+            qDebug()<< file << " " << loader.errorString();
+            continue;
+        }
+        QObject *pl = loader.instance();
+
+        CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
+        _plugins.append(plugin);
+    }
+}
 
+
+
+CommonDictInterface* Backbone::plugin(QString type) {
+    foreach(CommonDictInterface* plugin, _plugins)
+        if(plugin->type() == type)
+            return plugin;
+    return 0;
+}
+
+
+
+void Backbone::loadPrefs(QString fileName) {
+    if(dryRun)
+        return;
+    QFileInfo file(QDir::toNativeSeparators(fileName));
+    QDir confDir(file.dir());
+    if(!confDir.exists()){
+        qDebug() << "Configuration file dosn't exists ("
+                << file.filePath() << ")";
+        Q_EMIT notify(Notify::Warning,
+                QString("%1 configurationfile dosen't exists.")
+                .arg(file.filePath()));
+        return;
+    }
+    QSettings set(file.filePath(), QSettings::IniFormat);
+    _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
+    _historyLen = set.value("general/history_size", 10).toInt();
+    _searchLimit = set.value("general/search_limit", 15).toInt();
+    _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
+    _searchDicts = set.value("general/search_dictionaries",1).toBool();
+}
+
+
+
+void Backbone::savePrefs(QSettings *set) {
+    if(dryRun)
+        return;
+    set->setValue("general/plugin_path", _pluginPath);
+    set->setValue("general/history_size", _historyLen);
+    set->setValue("general/search_limit", _searchLimit);
+    set->setValue("general/search_bookmarks", _searchBookmarks);
+    set->setValue("general/search_dictionaries", _searchDicts);
+}
+
+
+
+void Backbone::saveDefaultPrefs(QSettings *set) {
+    if(dryRun)
+        return;
+    set->setValue("general/plugin_path", _defaultPluginPath);
+    set->setValue("general/history_size", _defaultHistoryLen);
+    set->setValue("general/search_limit", _defaultSearchLimit);
+}
+
+
+
+void Backbone::loadDicts(QString fileName, bool _default) {
+    if(dryRun)
+        return;
+    QFileInfo file(QDir::toNativeSeparators(fileName));
+    QDir confDir(file.dir());
+    if(!confDir.exists()){
+        qDebug() << "Configuration file dosn't exists ("
+                << file.filePath() << ")";
+        Q_EMIT notify(Notify::Warning,
+                QString("%1 configurationfile dosen't exists.")
+                .arg(file.filePath()));
+        return;
+    }
+
+    QSettings set(file.filePath(), QSettings::IniFormat);
+    QStringList dicts = set.childGroups();
+    foreach(QString dict, dicts) {
+        if(!dict.contains("dictionary_"))
+            continue;
+        CommonDictInterface* plug = plugin
+                                    (set.value(dict + "/type", "").toString());
+        if(!plug) {
+            qDebug() << "Config file error: "
+                    << set.value(dict + "/type", "").toString()
+                    << " dosen't exists";
+            Q_EMIT notify(Notify::Warning,
+                    QString("Configuration file error. %2 plugin dosen't exists.")
+                    .arg(set.value(dict + "/type", "").toString()));
+            continue;
+        }
+        Settings* plugSet = new Settings();
+        set.beginGroup(dict);
+        QStringList items = set.childKeys();
+        foreach(QString item, items) {
+            plugSet->setValue(item, set.value(item, "").toString());
+        }
+        bool active = set.value("active",1).toBool();
+
+        if(_default)
+            plugSet->setValue("_default_", "true");
+
+        set.endGroup();
+        addInternalDictionary(plug->getNew(plugSet), active);
+    }
+}
+
+
+
+void Backbone::dictUpdated() {
+    if(dryRun)
+        return;
+    _history->setMaxSize(_historyLen);
+    QFileInfo file(QDir::toNativeSeparators(_configPath));
+    QDir confDir(file.dir());
+    if(!confDir.exists())
+        confDir.mkpath(file.dir().path());
+    QSettings set(file.filePath(), QSettings::IniFormat);
+    set.clear();
+
+    QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
+    QDir defConfDir(defFile.dir());
+    if(!defConfDir.exists())
+        defConfDir.mkpath(defFile.dir().path());
+    QSettings defSet(defFile.filePath(), QSettings::IniFormat);
+    defSet.clear();
+    savePrefs(&set);
+    saveDefaultPrefs(&defSet);
+
+    foreach(CommonDictInterface* dict, _dicts.keys()){
+        if(!dict || !dict->settings())
+            continue;
+        if(!dict->settings()->keys().contains("_default_"))
+            saveState(&set, dict->settings(), _dicts[dict], dict->hash());
+        else
+            saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
+    }
+}
+
+
+
+void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
+                         , uint hash) {
+    if(dryRun)
+        return;
+    if(!set || !plugSet)
+        return;
+    QString section;
+    section.append(QString("dictionary_%1").arg(hash));
+    QList<QString> keys = plugSet->keys();
+    foreach(QString key, keys)
+        set->setValue(section + "/" + key, plugSet->value(key));
+    set->setValue(section + "/active", active);
+}
+
+
+
+QStringList Backbone::htmls() {
+    return _htmlResult;
+}
+
+
+
+void Backbone::searchHtml(QList<Translation *> translations) {
+    _htmlResult.clear();
+
+    QList<TranslationPtr> dummy;
+    stopped = false;
+    foreach(Translation* tr, translations) {
+        if(containsDict(tr->dict()) || !tr->dict())
+            dummy.append(TranslationPtr(tr));
+  }
+
+   _innerHtmlResult = QtConcurrent::mapped(dummy,
+                                            &TranslationPtr::toHtml);
+   _htmlResultWatcher.setFuture(_innerHtmlResult);
+}
+
+void Backbone::htmlTranslationReady() {
+
+    QFutureIterator<QString> it(_innerHtmlResult);
+    while(it.hasNext())
+       _htmlResult.append(it.next());
+
+    if(!stopped)
+        Q_EMIT htmlReady();
+
+}
+
+
+QList<CommonDictInterface*> Backbone::activeDicts() {
+    QList<CommonDictInterface*>res;
+    foreach(CommonDictInterface* dict, _dicts.keys())
+        if(_dicts[dict])
+            res.append(dict);
+    return res;
+
+}
+
+
+
+void Backbone::bookmarksListReady() {
+   _bookmarksResult = _innerBookmarks.result();
+   Q_EMIT bookmarksReady();
+}
+
+
+
+
+void Backbone::setSettings(Settings *settings) {
+    _historyLen = settings->value("history_size").toInt();
+    _searchLimit = settings->value("search_limit").toInt();
+    if(settings->value("search_dictionaries") == "true")
+        _searchDicts = 1;
+    else
+        _searchDicts = 0;
+    if(settings->value("search_bookmarks") == "true")
+        _searchBookmarks = 1;
+    else
+        _searchBookmarks = 0;
+    dictUpdated();
+}
+
+
+
+
+Settings* Backbone::settings() {
+    Settings * settings = new Settings();
+    settings->setValue("history_size", QString("%1").arg(_historyLen));
+    settings->setValue("search_limit", QString("%1").arg(_searchLimit));
+    if(_searchBookmarks)
+        settings->setValue("search_bookmarks", "true");
+    else
+        settings->setValue("search_bookmarks", "false");
+
+    if(_searchDicts)
+        settings->setValue("search_dictionaries", "true");
+    else
+        settings->setValue("search_dictionaries", "false");
+    return settings;
+}
+
+
+bool Backbone::containsDict(uint hash) const {
+    QHashIterator<CommonDictInterface*, bool> it(_dicts);
+    if (!hash)
+        return false;
+    while(it.hasNext())
+        if(it.next().key()->hash() == hash)
+            return true;
+    return false;
+}