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 dosent 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() {
- _searchLimit = 10;
- _interval = 250; //msec
- _pluginPath = "../lib/mdictionary/";
+
+ 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);
+
loadPlugins();
- if(!connect(&_timer, SIGNAL(timeout()), this, SLOT(translation())))
- qDebug() << "Timer signal not connected";
+ 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(QObject *parent)
+
+
+Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
+ QObject *parent)
: QObject(parent)
{
+ _pluginPath = pluginPath;
+ _configPath = configPath;
+ _defaultConfigPath = configPath;
+ dryRun = false;
+ if(dry)
+ dryRun = true;
init();
}
Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
- init();
_dicts = QHash<CommonDictInterface*, bool > (b._dicts);
_plugins = QList<CommonDictInterface* > (b._plugins);
_result = QHash<QString, Translation* > (b._result);
-
QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
return _dicts;
}
-
QList<CommonDictInterface* > Backbone::getPlugins() {
return _plugins;
}
-
-QList<QString> Backbone::getHistory() {
- //TODO code needed
+History* Backbone::history() {
+ return _history;
}
-
QMultiHash<QString, Translation*> Backbone::result() {
return _result;
}
-
void Backbone::stopSearching() {
- _timer.stop();
- _innerResult.clear();
+ if(stopped)
+ return;
+
foreach(CommonDictInterface* dict, _dicts.keys())
dict->stop();
+ stopped = true;
+ _innerHtmlResult.cancel();
+ _innerResult.cancel();
+ Q_EMIT searchCanceled();
}
-
-void Backbone::search(QString word) {
- _timer.stop();
+void Backbone::search(QString word){
_result.clear();
- _innerResult.clear();
+ mappedSearch = word.toLower();
- _timer.start(_interval);
- foreach(CommonDictInterface* dict, _dicts.keys())
- if(_dicts[dict] == 1) {
- QFuture<QList<Translation*> > tr =
- QtConcurrent::run(dict,
- &CommonDictInterface::searchWordList,word,
- searchLimit());
- _innerResult.append(tr);
- }
+ 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, bool active) {
+ addInternalDictionary(dict,active);
+ dictUpdated();
+}
+
- void Backbone::addDictionary(CommonDictInterface* dict) {
- dict->setHash(_dicts.size()+1);
- _dicts[dict] = 1;
+
+ 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();
}
-int Backbone::activeSearches() const {
- return _innerResult.size();
-}
+void Backbone::translationReady() {
+ bool changed = 0; // prevents doubling ready() signal, when both if are
+ // executed in one translationReady() call then second
+ // call doubles ready*() emit without any new data
+ if(!dictFin && _innerResult.isFinished()) {
+ changed = 1;
+ 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()) {
+ changed = 1;
+ bookmarkFin = 1;
+ QList<Translation*> list = _innerBookmarks.result();
-void Backbone::translation() {
- foreach(QFuture<QList<Translation*> > trans, _innerResult) {
- if(!trans.isFinished())
- continue;
- QList<Translation*> tList = trans.result();
- foreach(Translation* t, tList)
- _result.insert(t->key(), t);
- _innerResult.removeOne(trans);
+ foreach(Translation* trans, list)
+ _result.insert(trans->key().toLower(), trans);
}
- if(!_innerResult.size()) {
- _timer.stop();
+
+ if(!stopped && bookmarkFin && dictFin && changed) {
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() {
- QDir plug(QDir::toNativeSeparators(_pluginPath));
- if(!plug.exists()) {
- qDebug() << plug.absolutePath() << " folder dosen't exists";
+ if(dryRun)
return;
- }
QStringList nameFilter;
nameFilter << "*.so";
- plug.setFilter(QDir::Files);
- QStringList files = plug.entryList(nameFilter);
- qDebug() << files;
-
+ QStringList files = getFilesFromDir(_pluginPath, nameFilter);
foreach(QString file, files) {
- QPluginLoader loader(plug.absoluteFilePath(file));
+ 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;
}
CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
_plugins.append(plugin);
- addDictionary(plugin->getNew(0)); //TODO change 0 to real settings
}
}
+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;
+}