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>
29 #include "ConfigGenerator.h"
30 class ConfigGenerator;
33 int Backbone::_searchLimit;
35 // Sadly QtConcurent::mapped dosent let me use something like calling method of
36 // some class with supplied argument; so i have to sin against art and put
37 // global function and variable so i could supply function with some parametr
39 QList<Translation*> mapSearch(CommonDictInterface *dict) {
41 return dict->searchWordList(mappedSearch, Backbone::_searchLimit);
42 return QList<Translation*>();
47 /*! Smart pointer (kind of) for translation object
49 QtConcurent::mapped use collection of data and one function, what i need is
50 to map signle data object to method calls for multiple objects. TranslationPtr
51 is try to store method call as a data -> moreover QtConcurent allow only for
52 methods without any parameters so TranslationPtr is created with Translation
53 object -> ready to call toHtml() for supplied Translation.
55 Another thing is that QtConcurent dont like pointers in data collection
56 so TranslationPtr is way to hide real translation object (pointer for object)
58 class TranslationPtr {
61 TranslationPtr(Translation* tr) :_tr(tr) {}
63 /*! \return translation text for corresponding Translation object */
64 QString toHtml() const {
66 trans = _tr->toHtml();
72 void Backbone::init() {
74 _dir = QDir::homePath() + "/.mdictionary/";
75 if(!_configPath.size())
76 _configPath = _dir + "mdictionary.config";
77 if(!_defaultConfigPath.size())
78 _defaultConfigPath = _dir + "mdictionary.defaults";
79 if(!_pluginPath.size())
80 _pluginPath = "/usr/lib/mdictionary";
84 //Install default config files
85 ConfigGenerator confGen;
86 confGen.generateCss(_dir + "style.css");
90 loadPrefs(_defaultConfigPath);
92 // Default configuration are stored in separate config file and we dont want
94 _defaultPluginPath = _pluginPath;
95 _defaultHistoryLen = _historyLen;
96 _defaultSearchLimit = _searchLimit;
97 loadPrefs(_configPath);
101 loadDicts(_defaultConfigPath, true);
102 loadDicts(_configPath);
104 connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
105 connect(&_htmlResultWatcher, SIGNAL(finished()), this,
106 SLOT(htmlTranslationReady()));
107 connect(&_bookmarkWatcher, SIGNAL(finished()), this,
108 SLOT(bookmarksListReady()));
109 connect(&_bookmarkSearchWatcher, SIGNAL(finished()), this,
110 SLOT(translationReady()));
112 // In common opinion perfect thread count is cores_number+1 (in qt perfect
113 // thread count is set to cores number
114 QThreadPool::globalInstance()->setMaxThreadCount(
115 QThreadPool::globalInstance()->maxThreadCount()+1);
117 _history = new History(5, this);
123 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
127 _pluginPath = pluginPath;
128 _configPath = configPath;
130 _defaultConfigPath = configPath;
139 Backbone::~Backbone()
141 QListIterator<CommonDictInterface*> it(_dicts.keys());
146 it = QListIterator<CommonDictInterface*>(_plugins);
150 QHashIterator<QString, Translation*> it2(_result);
152 delete it2.next().value();
159 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
160 _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
161 _plugins = QList<CommonDictInterface* > (b._plugins);
162 _result = QHash<QString, Translation* > (b._result);
163 _searchLimit = b.searchLimit();
169 int Backbone::searchLimit() const {
175 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
181 QList<CommonDictInterface* > Backbone::getPlugins() {
187 History* Backbone::history() {
193 QMultiHash<QString, Translation*> Backbone::result() {
199 void Backbone::stopSearching() {
203 foreach(CommonDictInterface* dict, _dicts.keys())
206 _innerHtmlResult.cancel();
207 _innerResult.cancel();
208 Q_EMIT searchCanceled();
213 void Backbone::search(QString word){
215 mappedSearch = word.toLower();
219 // When dictFin and bookmarkFin is set to true then translationReady()
220 // signal is emited see translationReady(),
221 // so when searching only in one of them, coresponding *Fin is set to false
222 // and other to true so program is waiting only for one translation
223 dictFin = !_searchDicts;
224 bookmarkFin = !_searchBookmarks;
227 _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
228 _resultWatcher.setFuture(_innerResult);
231 if(_searchBookmarks) {
232 _innerBookmarks = QtConcurrent::run(_bookmarks,
233 &Bookmarks::searchWordList, word);
234 _bookmarkSearchWatcher.setFuture(_innerBookmarks);
240 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
241 foreach(CommonDictInterface* dict, _dicts.keys())
242 if(activeDicts.contains(dict))
251 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
252 addInternalDictionary(dict,active);
258 void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
259 dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
260 _dicts[dict] = active;
262 connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
263 connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
264 SIGNAL(notify(Notify::NotifyType,QString)));
267 void Backbone::removeDictionary(CommonDictInterface *dict) {
276 void Backbone::quit() {
283 void Backbone::translationReady() {
284 bool changed = 0; // prevents doubling ready() signal, when both if's are
285 // executed in one translationReady() call then second
286 // translationReady() call doubles ready*() emit
288 if(!dictFin && _innerResult.isFinished()) {
291 QFutureIterator<QList<Translation*> > it(_innerResult);
293 while(it.hasNext()) {
294 QList<Translation* > list = it.next();
295 foreach(Translation* trans, list)
296 _result.insert(trans->key().toLower(), trans);
300 if(!bookmarkFin && _innerBookmarks.isFinished()) {
303 QList<Translation*> list = _innerBookmarks.result();
305 foreach(Translation* trans, list)
306 _result.insert(trans->key().toLower(), trans);
309 if(!stopped && bookmarkFin && dictFin && changed) {
314 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
315 QDir plug(QDir::toNativeSeparators(dir));
317 qDebug() << plug.absolutePath() << " folder doesn't exist";
318 Q_EMIT notify(Notify::Warning,
319 QString("%1 folder doesn't exist.").arg(plug.path()));
320 return QStringList();
322 plug.setFilter(QDir::Files);
323 QStringList list = plug.entryList(nameFilter);
325 for(int i = 0; i < list.size(); i++)
326 list[i] = plug.absoluteFilePath(list.at(i));
331 void Backbone::loadPlugins() {
334 QStringList nameFilter;
335 nameFilter << "*.so";
336 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
338 foreach(QString file, files) {
339 QPluginLoader loader(file);
341 Q_EMIT notify(Notify::Error,
342 QString("%1 plugin cannot be loaded: %2.")
343 .arg(file).arg(loader.errorString()));
344 qDebug()<< file << " " << loader.errorString();
347 QObject *pl = loader.instance();
349 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
350 _plugins.append(plugin);
356 CommonDictInterface* Backbone::plugin(QString type) {
357 foreach(CommonDictInterface* plugin, _plugins)
358 if(plugin->type() == type)
365 void Backbone::loadPrefs(QString fileName) {
368 QFileInfo file(QDir::toNativeSeparators(fileName));
369 QDir confDir(file.dir());
370 if(!confDir.exists()){
371 qDebug() << "Configuration file doesn't exist ("
372 << file.filePath() << ")";
373 Q_EMIT notify(Notify::Warning,
374 QString("%1 configuration file doesn't exist.")
375 .arg(file.filePath()));
378 QSettings set(file.filePath(), QSettings::IniFormat);
379 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
380 _historyLen = set.value("general/history_size", 10).toInt();
381 _searchLimit = set.value("general/search_limit", 15).toInt();
382 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
383 _searchDicts = set.value("general/search_dictionaries",1).toBool();
388 void Backbone::savePrefs(QSettings *set) {
391 set->setValue("general/plugin_path", _pluginPath);
392 set->setValue("general/history_size", _historyLen);
393 set->setValue("general/search_limit", _searchLimit);
394 set->setValue("general/search_bookmarks", _searchBookmarks);
395 set->setValue("general/search_dictionaries", _searchDicts);
400 void Backbone::saveDefaultPrefs(QSettings *set) {
403 set->setValue("general/plugin_path", _defaultPluginPath);
404 set->setValue("general/history_size", _defaultHistoryLen);
405 set->setValue("general/search_limit", _defaultSearchLimit);
410 void Backbone::loadDicts(QString fileName, bool _default) {
414 QFileInfo file(QDir::toNativeSeparators(fileName));
415 QDir confDir(file.dir());
416 if(!confDir.exists()){
417 qDebug() << "Configuration file doesn't exist ("
418 << file.filePath() << ")";
419 Q_EMIT notify(Notify::Warning,
420 QString("%1 configurationfile doesn't exist.")
421 .arg(file.filePath()));
425 QSettings set(file.filePath(), QSettings::IniFormat);
426 QStringList dicts = set.childGroups();
427 foreach(QString dict, dicts) {
428 if(!dict.contains("dictionary_"))
430 CommonDictInterface* plug = plugin
431 (set.value(dict + "/type", "").toString());
433 qDebug() << "Config file error: "
434 << set.value(dict + "/type", "").toString()
436 Q_EMIT notify(Notify::Warning,
437 QString("Configuration file error. %2 plugin doesn't exist.")
438 .arg(set.value(dict + "/type", "").toString()));
441 Settings* plugSet = new Settings();
442 set.beginGroup(dict);
443 QStringList items = set.childKeys();
444 foreach(QString item, items) {
445 plugSet->setValue(item, set.value(item, "").toString());
447 bool active = set.value("active",1).toBool();
450 plugSet->setValue("_default_", "true");
453 addInternalDictionary(plug->getNew(plugSet), active);
459 void Backbone::dictUpdated() {
463 // For convienence this function is called for each change in dictionaries
464 // and each call dumps configuration for all dictionaries into file.
465 // Maybe better way would be to store new/changed configuration but
466 // parsing settings file and figuring out what was changed, in my opinion,
467 // would take more time
468 _history->setMaxSize(_historyLen);
469 QFileInfo file(QDir::toNativeSeparators(_configPath));
470 QDir confDir(file.dir());
471 if(!confDir.exists())
472 confDir.mkpath(file.dir().path());
473 QSettings set(file.filePath(), QSettings::IniFormat);
476 QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
477 QDir defConfDir(defFile.dir());
478 if(!defConfDir.exists())
479 defConfDir.mkpath(defFile.dir().path());
480 QSettings defSet(defFile.filePath(), QSettings::IniFormat);
483 saveDefaultPrefs(&defSet);
485 foreach(CommonDictInterface* dict, _dicts.keys()){
486 if(!dict || !dict->settings())
488 if(!dict->settings()->keys().contains("_default_"))
489 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
491 saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
497 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
505 section.append(QString("dictionary_%1").arg(hash));
506 QList<QString> keys = plugSet->keys();
507 foreach(QString key, keys)
508 set->setValue(section + "/" + key, plugSet->value(key));
509 set->setValue(section + "/active", active);
514 QStringList Backbone::htmls() {
520 void Backbone::searchHtml(QList<Translation *> translations) {
523 QList<TranslationPtr> dummy;
525 foreach(Translation* tr, translations) {
526 if(containsDict(tr->dict()) || !tr->dict())
527 dummy.append(TranslationPtr(tr));
528 foreach(CommonDictInterface* dict, activeDicts()) {
529 Translation* trans = dict->getTranslationFor(tr->key());
531 dummy.append(TranslationPtr(trans));
535 _innerHtmlResult = QtConcurrent::mapped(dummy,
536 &TranslationPtr::toHtml);
537 _htmlResultWatcher.setFuture(_innerHtmlResult);
542 void Backbone::htmlTranslationReady() {
544 QFutureIterator<QString> it(_innerHtmlResult);
547 uniqe.insert(it.next());
549 _htmlResult = uniqe.toList();
557 QList<CommonDictInterface*> Backbone::activeDicts() {
558 QList<CommonDictInterface*>res;
559 foreach(CommonDictInterface* dict, _dicts.keys())
568 void Backbone::bookmarksListReady() {
569 _bookmarksResult = _innerBookmarks.result();
570 Q_EMIT bookmarksReady();
576 void Backbone::setSettings(Settings *settings) {
577 _historyLen = settings->value("history_size").toInt();
578 _searchLimit = settings->value("search_limit").toInt();
579 if(settings->value("search_dictionaries") == "true")
583 if(settings->value("search_bookmarks") == "true")
584 _searchBookmarks = 1;
586 _searchBookmarks = 0;
595 Settings* Backbone::settings() {
596 Settings * settings = new Settings();
597 settings->setValue("history_size", QString("%1").arg(_historyLen));
598 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
600 settings->setValue("search_bookmarks", "true");
602 settings->setValue("search_bookmarks", "false");
605 settings->setValue("search_dictionaries", "true");
607 settings->setValue("search_dictionaries", "false");
612 bool Backbone::containsDict(uint hash) const {
613 QHashIterator<CommonDictInterface*, bool> it(_dicts);
617 if(it.next().key()->hash() == hash)