1 /*******************************************************************************
3 This file is part of mDictionary.
5 mDictionary is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 mDictionary is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mDictionary. If not, see <http://www.gnu.org/licenses/>.
18 Copyright 2010 Comarch S.A.
20 *******************************************************************************/
21 /*! \file backbone.cpp
22 \brief Backbone/core main file \see Backbone
25 \author Bartosz Szatkowski <bulislaw@linux.com>
31 int Backbone::_searchLimit;
33 // Sadly QtConcurent::mapped dosent let me use something like calling method of
34 // some class with supplied argument; so i have to sin against art and put
35 // global function and variable so i could supply function with some parametr
37 QList<Translation*> mapSearch(CommonDictInterface *dict) {
39 return dict->searchWordList(mappedSearch, Backbone::_searchLimit);
40 return QList<Translation*>();
45 /*! Smart pointer (kind of) for translation object
47 QtConcurent::mapped use collection of data and one function, what i need is
48 to map signle data object to method calls for multiple objects. TranslationPtr
49 is try to store method call as a data -> moreover QtConcurent allow only for
50 methods without any parameters so TranslationPtr is created with Translation
51 object -> ready to call toHtml() for supplied Translation.
53 Another thing is that QtConcurent dont like pointers in data collection
54 so TranslationPtr is way to hide real translation object (pointer for object)
56 class TranslationPtr {
59 TranslationPtr(Translation* tr) :_tr(tr) {}
61 /*! \return translation text for corresponding Translation object */
62 QString toHtml() const {
64 trans = _tr->toHtml();
70 void Backbone::init() {
72 if(!_configPath.size())
73 _configPath = QDir::homePath() + "/.mdictionary/mdictionary.config";
74 if(!_defaultConfigPath.size())
75 _defaultConfigPath = QDir::homePath() + "/.mdictionary/mdictionary.defaults";
76 if(!_pluginPath.size())
77 _pluginPath = "/usr/lib/mdictionary";
81 loadPrefs(_defaultConfigPath);
83 // Default configuration are stored in separate config file and we dont want
85 _defaultPluginPath = _pluginPath;
86 _defaultHistoryLen = _historyLen;
87 _defaultSearchLimit = _searchLimit;
88 loadPrefs(_configPath);
92 loadDicts(_defaultConfigPath, true);
93 loadDicts(_configPath);
95 connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
96 connect(&_htmlResultWatcher, SIGNAL(finished()), this,
97 SLOT(htmlTranslationReady()));
98 connect(&_bookmarkWatcher, SIGNAL(finished()), this,
99 SLOT(bookmarksListReady()));
100 connect(&_bookmarkSearchWatcher, SIGNAL(finished()), this,
101 SLOT(translationReady()));
103 // In common opinion perfect thread count is cores_number+1 (in qt perfect
104 // thread count is set to cores number
105 QThreadPool::globalInstance()->setMaxThreadCount(
106 QThreadPool::globalInstance()->maxThreadCount()+1);
108 _history = new History(5, this);
114 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
118 _pluginPath = pluginPath;
119 _configPath = configPath;
121 _defaultConfigPath = configPath;
130 Backbone::~Backbone()
132 QListIterator<CommonDictInterface*> it(_dicts.keys());
137 it = QListIterator<CommonDictInterface*>(_plugins);
141 QHashIterator<QString, Translation*> it2(_result);
143 delete it2.next().value();
150 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
151 _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
152 _plugins = QList<CommonDictInterface* > (b._plugins);
153 _result = QHash<QString, Translation* > (b._result);
154 _searchLimit = b.searchLimit();
160 int Backbone::searchLimit() const {
166 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
172 QList<CommonDictInterface* > Backbone::getPlugins() {
178 History* Backbone::history() {
184 QMultiHash<QString, Translation*> Backbone::result() {
190 void Backbone::stopSearching() {
194 foreach(CommonDictInterface* dict, _dicts.keys())
197 _innerHtmlResult.cancel();
198 _innerResult.cancel();
199 Q_EMIT searchCanceled();
204 void Backbone::search(QString word){
206 mappedSearch = word.toLower();
210 // When dictFin and bookmarkFin is set to true then translationReady()
211 // signal is emited see translationReady(),
212 // so when searching only in one of them, coresponding *Fin is set to false
213 // and other to true so program is waiting only for one translation
214 dictFin = !_searchDicts;
215 bookmarkFin = !_searchBookmarks;
218 _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
219 _resultWatcher.setFuture(_innerResult);
222 if(_searchBookmarks) {
223 _innerBookmarks = QtConcurrent::run(_bookmarks,
224 &Bookmarks::searchWordList, word);
225 _bookmarkSearchWatcher.setFuture(_innerBookmarks);
231 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
232 foreach(CommonDictInterface* dict, _dicts.keys())
233 if(activeDicts.contains(dict))
242 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
243 addInternalDictionary(dict,active);
249 void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
250 dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
251 _dicts[dict] = active;
253 connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
254 connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
255 SIGNAL(notify(Notify::NotifyType,QString)));
258 void Backbone::removeDictionary(CommonDictInterface *dict) {
267 void Backbone::quit() {
274 void Backbone::translationReady() {
275 bool changed = 0; // prevents doubling ready() signal, when both if's are
276 // executed in one translationReady() call then second
277 // translationReady() call doubles ready*() emit
279 if(!dictFin && _innerResult.isFinished()) {
282 QFutureIterator<QList<Translation*> > it(_innerResult);
284 while(it.hasNext()) {
285 QList<Translation* > list = it.next();
286 foreach(Translation* trans, list)
287 _result.insert(trans->key().toLower(), trans);
291 if(!bookmarkFin && _innerBookmarks.isFinished()) {
294 QList<Translation*> list = _innerBookmarks.result();
296 foreach(Translation* trans, list)
297 _result.insert(trans->key().toLower(), trans);
300 if(!stopped && bookmarkFin && dictFin && changed) {
305 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
306 QDir plug(QDir::toNativeSeparators(dir));
308 qDebug() << plug.absolutePath() << " folder dosen't exists";
309 Q_EMIT notify(Notify::Warning,
310 QString("%1 folder dosen't exists.").arg(plug.path()));
311 return QStringList();
313 plug.setFilter(QDir::Files);
314 QStringList list = plug.entryList(nameFilter);
316 for(int i = 0; i < list.size(); i++)
317 list[i] = plug.absoluteFilePath(list.at(i));
322 void Backbone::loadPlugins() {
325 QStringList nameFilter;
326 nameFilter << "*.so";
327 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
329 foreach(QString file, files) {
330 QPluginLoader loader(file);
332 Q_EMIT notify(Notify::Error,
333 QString("%1 plugin cannot be loaded: %2.")
334 .arg(file).arg(loader.errorString()));
335 qDebug()<< file << " " << loader.errorString();
338 QObject *pl = loader.instance();
340 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
341 _plugins.append(plugin);
347 CommonDictInterface* Backbone::plugin(QString type) {
348 foreach(CommonDictInterface* plugin, _plugins)
349 if(plugin->type() == type)
356 void Backbone::loadPrefs(QString fileName) {
359 QFileInfo file(QDir::toNativeSeparators(fileName));
360 QDir confDir(file.dir());
361 if(!confDir.exists()){
362 qDebug() << "Configuration file dosn't exists ("
363 << file.filePath() << ")";
364 Q_EMIT notify(Notify::Warning,
365 QString("%1 configurationfile dosen't exists.")
366 .arg(file.filePath()));
369 QSettings set(file.filePath(), QSettings::IniFormat);
370 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
371 _historyLen = set.value("general/history_size", 10).toInt();
372 _searchLimit = set.value("general/search_limit", 15).toInt();
373 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
374 _searchDicts = set.value("general/search_dictionaries",1).toBool();
379 void Backbone::savePrefs(QSettings *set) {
382 set->setValue("general/plugin_path", _pluginPath);
383 set->setValue("general/history_size", _historyLen);
384 set->setValue("general/search_limit", _searchLimit);
385 set->setValue("general/search_bookmarks", _searchBookmarks);
386 set->setValue("general/search_dictionaries", _searchDicts);
391 void Backbone::saveDefaultPrefs(QSettings *set) {
394 set->setValue("general/plugin_path", _defaultPluginPath);
395 set->setValue("general/history_size", _defaultHistoryLen);
396 set->setValue("general/search_limit", _defaultSearchLimit);
401 void Backbone::loadDicts(QString fileName, bool _default) {
405 QFileInfo file(QDir::toNativeSeparators(fileName));
406 QDir confDir(file.dir());
407 if(!confDir.exists()){
408 qDebug() << "Configuration file dosn't exists ("
409 << file.filePath() << ")";
410 Q_EMIT notify(Notify::Warning,
411 QString("%1 configurationfile dosen't exists.")
412 .arg(file.filePath()));
416 QSettings set(file.filePath(), QSettings::IniFormat);
417 QStringList dicts = set.childGroups();
418 foreach(QString dict, dicts) {
419 if(!dict.contains("dictionary_"))
421 CommonDictInterface* plug = plugin
422 (set.value(dict + "/type", "").toString());
424 qDebug() << "Config file error: "
425 << set.value(dict + "/type", "").toString()
426 << " dosen't exists";
427 Q_EMIT notify(Notify::Warning,
428 QString("Configuration file error. %2 plugin dosen't exists.")
429 .arg(set.value(dict + "/type", "").toString()));
432 Settings* plugSet = new Settings();
433 set.beginGroup(dict);
434 QStringList items = set.childKeys();
435 foreach(QString item, items) {
436 plugSet->setValue(item, set.value(item, "").toString());
438 bool active = set.value("active",1).toBool();
441 plugSet->setValue("_default_", "true");
444 addInternalDictionary(plug->getNew(plugSet), active);
450 void Backbone::dictUpdated() {
454 // For convienence this function is called for each change in dictionaries
455 // and each call dumps configuration for all dictionaries into file.
456 // Maybe better way would be to store new/changed configuration but
457 // parsing settings file and figuring out what was changed, in my opinion,
458 // would take more time
459 _history->setMaxSize(_historyLen);
460 QFileInfo file(QDir::toNativeSeparators(_configPath));
461 QDir confDir(file.dir());
462 if(!confDir.exists())
463 confDir.mkpath(file.dir().path());
464 QSettings set(file.filePath(), QSettings::IniFormat);
467 QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
468 QDir defConfDir(defFile.dir());
469 if(!defConfDir.exists())
470 defConfDir.mkpath(defFile.dir().path());
471 QSettings defSet(defFile.filePath(), QSettings::IniFormat);
474 saveDefaultPrefs(&defSet);
476 foreach(CommonDictInterface* dict, _dicts.keys()){
477 if(!dict || !dict->settings())
479 if(!dict->settings()->keys().contains("_default_"))
480 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
482 saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
488 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
496 section.append(QString("dictionary_%1").arg(hash));
497 QList<QString> keys = plugSet->keys();
498 foreach(QString key, keys)
499 set->setValue(section + "/" + key, plugSet->value(key));
500 set->setValue(section + "/active", active);
505 QStringList Backbone::htmls() {
511 void Backbone::searchHtml(QList<Translation *> translations) {
514 QList<TranslationPtr> dummy;
516 foreach(Translation* tr, translations) {
517 if(containsDict(tr->dict()) || !tr->dict())
518 dummy.append(TranslationPtr(tr));
521 _innerHtmlResult = QtConcurrent::mapped(dummy,
522 &TranslationPtr::toHtml);
523 _htmlResultWatcher.setFuture(_innerHtmlResult);
526 void Backbone::htmlTranslationReady() {
528 QFutureIterator<QString> it(_innerHtmlResult);
530 _htmlResult.append(it.next());
538 QList<CommonDictInterface*> Backbone::activeDicts() {
539 QList<CommonDictInterface*>res;
540 foreach(CommonDictInterface* dict, _dicts.keys())
549 void Backbone::bookmarksListReady() {
550 _bookmarksResult = _innerBookmarks.result();
551 Q_EMIT bookmarksReady();
557 void Backbone::setSettings(Settings *settings) {
558 _historyLen = settings->value("history_size").toInt();
559 _searchLimit = settings->value("search_limit").toInt();
560 if(settings->value("search_dictionaries") == "true")
564 if(settings->value("search_bookmarks") == "true")
565 _searchBookmarks = 1;
567 _searchBookmarks = 0;
576 Settings* Backbone::settings() {
577 Settings * settings = new Settings();
578 settings->setValue("history_size", QString("%1").arg(_historyLen));
579 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
581 settings->setValue("search_bookmarks", "true");
583 settings->setValue("search_bookmarks", "false");
586 settings->setValue("search_dictionaries", "true");
588 settings->setValue("search_dictionaries", "false");
593 bool Backbone::containsDict(uint hash) const {
594 QHashIterator<CommonDictInterface*, bool> it(_dicts);
598 if(it.next().key()->hash() == hash)