Copyright 2010 Comarch S.A.
*******************************************************************************/
-/*! /file backbone.cpp
+/*! \file backbone.cpp
\brief Backbone/core main file \see Backbone
*/
#include "backbone.h"
+#include "ConfigGenerator.h"
+class ConfigGenerator;
#include <QDebug>
+int Backbone::_searchLimit;
+
+// Sadly QtConcurent::mapped dosent let me use something like calling method of
+// some class with supplied argument; so i have to sin against art and put
+// global function and variable so i could supply function with some parametr
QString mappedSearch;
QList<Translation*> mapSearch(CommonDictInterface *dict) {
if(dict)
- return dict->searchWordList(mappedSearch, 15);
+ return dict->searchWordList(mappedSearch, Backbone::_searchLimit);
return QList<Translation*>();
}
+
+
+/*! Smart pointer (kind of) for translation object
+
+ QtConcurent::mapped use collection of data and one function, what i need is
+ to map signle data object to method calls for multiple objects. TranslationPtr
+ is try to store method call as a data -> moreover QtConcurent allow only for
+ methods without any parameters so TranslationPtr is created with Translation
+ object -> ready to call toHtml() for supplied Translation.
+
+ Another thing is that QtConcurent dont like pointers in data collection
+ so TranslationPtr is way to hide real translation object (pointer for object)
+ */
class TranslationPtr {
Translation* _tr;
public:
TranslationPtr(Translation* tr) :_tr(tr) {}
+
+ /*! \return translation text for corresponding Translation object */
QString toHtml() const {
QString trans;
trans = _tr->toHtml();
void Backbone::init() {
+ _dir = QDir::homePath() + "/.mdictionary/";
if(!_configPath.size())
- _configPath = QDir::homePath() + "/.mdictionary/mdictionary.config";
+ _configPath = _dir + "mdictionary.config";
if(!_defaultConfigPath.size())
- _defaultConfigPath = QDir::homePath() + "/.mdictionary/mdictionary.defaults";
+ _defaultConfigPath = _dir + "mdictionary.defaults";
if(!_pluginPath.size())
_pluginPath = "/usr/lib/mdictionary";
_historyLen = 10;
_searchLimit = 15;
+ //Install default config files
+ ConfigGenerator confGen;
+ confGen.generateCss(_dir + "style.css");
+
+
+
loadPrefs(_defaultConfigPath);
+
+ // Default configuration are stored in separate config file and we dont want
+ // to update it
_defaultPluginPath = _pluginPath;
_defaultHistoryLen = _historyLen;
_defaultSearchLimit = _searchLimit;
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()));
+ // In common opinion perfect thread count is cores_number+1 (in qt perfect
+ // thread count is set to cores number
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)
{
_pluginPath = pluginPath;
_configPath = configPath;
+
_defaultConfigPath = configPath;
dryRun = false;
if(dry)
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;
}
-
History* Backbone::history() {
return _history;
}
-
QMultiHash<QString, Translation*> Backbone::result() {
return _result;
}
-
void Backbone::stopSearching() {
+ if(stopped)
+ return;
+
foreach(CommonDictInterface* dict, _dicts.keys())
dict->stop();
+ stopped = true;
_innerHtmlResult.cancel();
_innerResult.cancel();
+ Q_EMIT searchCanceled();
}
-
-
-
-void Backbone::search(QString word) {
+void Backbone::search(QString word){
_result.clear();
mappedSearch = word.toLower();
- //_time.restart();
- _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
- _resultWatcher.setFuture(_innerResult);
+ stopped = false;
+ // When dictFin and bookmarkFin is set to true then translationReady()
+ // signal is emited see translationReady(),
+ // so when searching only in one of them, coresponding *Fin is set to false
+ // and other to true so program is waiting only for one translation
+ dictFin = !_searchDicts;
+ bookmarkFin = !_searchBookmarks;
+ if (_searchDicts) {
+ _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
+ _resultWatcher.setFuture(_innerResult);
+ }
- //foreach(CommonDictInterface* dict, _dicts.keys())
- // if(_dicts[dict] == 1) {
- // QFuture<QList<Translation*> > tr =
- // QtConcurrent::run(dict,
- // &CommonDictInterface::searchWordList,word,
- // searchLimit());
- // _innerResult.append(tr);
- // }
-
+ 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))
void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
- dict->setHash(_dicts.size()+1);
+ dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
_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::translationReady() {
- //if(!_innerResult.isFinished())
- // return;
- QFutureIterator<QList<Translation*> > it(_innerResult);
+ bool changed = 0; // prevents doubling ready() signal, when both if's are
+ // executed in one translationReady() call then second
+ // translationReady() call doubles ready*() emit
+
+ 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();
- while(it.hasNext()) {
- QList<Translation* > list = it.next();
foreach(Translation* trans, list)
- _result.insert(trans->key().toLower(), trans);
+ _result.insert(trans->key().toLower(), trans);
}
- //qDebug () << "time " << _time.elapsed();
- Q_EMIT ready();
+ 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";
+ qDebug() << plug.absolutePath() << " folder doesn't exist";
+ Q_EMIT notify(Notify::Warning,
+ QString("%1 folder doesn't exist.").arg(plug.path()));
return QStringList();
}
plug.setFilter(QDir::Files);
if(dryRun)
return;
QStringList nameFilter;
- nameFilter << "*.so";
+ nameFilter << "*.so" << "*.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();
+ bool exists = 0;
CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
- _plugins.append(plugin);
+ foreach(CommonDictInterface* pl, _plugins)
+ if(pl->type() == plugin->type()) {
+ exists = 1;
+ break;
+ }
+ if(!exists)
+ _plugins.append(plugin);
}
}
QFileInfo file(QDir::toNativeSeparators(fileName));
QDir confDir(file.dir());
if(!confDir.exists()){
- qDebug() << "Configuration file dosn't exists ("
+ qDebug() << "Configuration file doesn't exist ("
<< file.filePath() << ")";
+ Q_EMIT notify(Notify::Warning,
+ QString("%1 configuration file doesn't exist.")
+ .arg(file.filePath()));
return;
}
QSettings set(file.filePath(), QSettings::IniFormat);
_pluginPath = set.value("general/plugin_path", _pluginPath).toString();
- _historyLen = set.value("general/history_length", 10).toInt();
+ _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();
}
if(dryRun)
return;
set->setValue("general/plugin_path", _pluginPath);
- set->setValue("general/history_length", _historyLen);
+ set->setValue("general/history_size", _historyLen);
set->setValue("general/search_limit", _searchLimit);
+ set->setValue("general/search_bookmarks", _searchBookmarks);
+ set->setValue("general/search_dictionaries", _searchDicts);
}
if(dryRun)
return;
set->setValue("general/plugin_path", _defaultPluginPath);
- set->setValue("general/history_length", _defaultHistoryLen);
+ 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 ("
+ qDebug() << "Configuration file doesn't exist ("
<< file.filePath() << ")";
+ Q_EMIT notify(Notify::Warning,
+ QString("%1 configurationfile doesn't exist.")
+ .arg(file.filePath()));
return;
}
if(!plug) {
qDebug() << "Config file error: "
<< set.value(dict + "/type", "").toString()
- << " dosen't exists";
+ << " doesn't exist";
+ Q_EMIT notify(Notify::Warning,
+ QString("Configuration file error. %2 plugin doesn't exist.")
+ .arg(set.value(dict + "/type", "").toString()));
continue;
}
Settings* plugSet = new Settings();
void Backbone::dictUpdated() {
if(dryRun)
return;
+
+ // For convienence this function is called for each change in dictionaries
+ // and each call dumps configuration for all dictionaries into file.
+ // Maybe better way would be to store new/changed configuration but
+ // parsing settings file and figuring out what was changed, in my opinion,
+ // would take more time
+ _history->setMaxSize(_historyLen);
QFileInfo file(QDir::toNativeSeparators(_configPath));
QDir confDir(file.dir());
if(!confDir.exists())
return;
if(!set || !plugSet)
return;
+
QString section;
section.append(QString("dictionary_%1").arg(hash));
QList<QString> keys = plugSet->keys();
void Backbone::searchHtml(QList<Translation *> translations) {
_htmlResult.clear();
+
QList<TranslationPtr> dummy;
- //_time.restart();
- foreach(Translation* tr, translations)
- dummy.append(TranslationPtr(tr));
+ stopped = false;
+ foreach(Translation* tr, translations) {
+ if(containsDict(tr->dict()) || !tr->dict())
+ dummy.append(TranslationPtr(tr));
+ /* foreach(CommonDictInterface* dict, activeDicts()) {
+ Translation* trans = dict->getTranslationFor(tr->key());
+ if(trans)
+ dummy.append(TranslationPtr(trans));
+ } */
+ }
+ if(translations.size()>0) {
+ Translation *tr = translations.at(0);
+ foreach(CommonDictInterface* dict, activeDicts()) {
+ Translation* trans = dict->getTranslationFor(tr->key());
+ if(trans)
+ dummy.append(TranslationPtr(trans));
+ }
+ }
_innerHtmlResult = QtConcurrent::mapped(dummy,
&TranslationPtr::toHtml);
_htmlResultWatcher.setFuture(_innerHtmlResult);
}
+
+
void Backbone::htmlTranslationReady() {
- //if(!_innerHtmlResult.isFinished())
- //return;
QFutureIterator<QString> it(_innerHtmlResult);
+ QSet<QString> uniqe;
while(it.hasNext())
- _htmlResult.append(it.next());
+ uniqe.insert(it.next());
+ _htmlResult.clear();
+ _htmlResult = uniqe.toList();
- //qDebug() << "time " << _time.elapsed();
- Q_EMIT htmlReady();
+ if(!stopped)
+ Q_EMIT htmlReady();
}
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();
+ if(settings)
+ delete settings;
+}
+
+
+
+
+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;
+}