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" << "*.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();
350 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
351 foreach(CommonDictInterface* pl, _plugins)
352 if(pl->type() == plugin->type()) {
357 _plugins.append(plugin);
363 CommonDictInterface* Backbone::plugin(QString type) {
364 foreach(CommonDictInterface* plugin, _plugins)
365 if(plugin->type() == type)
372 void Backbone::loadPrefs(QString fileName) {
375 QFileInfo file(QDir::toNativeSeparators(fileName));
376 QDir confDir(file.dir());
377 if(!confDir.exists()){
378 qDebug() << "Configuration file doesn't exist ("
379 << file.filePath() << ")";
380 Q_EMIT notify(Notify::Warning,
381 QString("%1 configuration file doesn't exist.")
382 .arg(file.filePath()));
385 QSettings set(file.filePath(), QSettings::IniFormat);
386 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
387 _historyLen = set.value("general/history_size", 10).toInt();
388 _searchLimit = set.value("general/search_limit", 15).toInt();
389 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
390 _searchDicts = set.value("general/search_dictionaries",1).toBool();
395 void Backbone::savePrefs(QSettings *set) {
398 set->setValue("general/plugin_path", _pluginPath);
399 set->setValue("general/history_size", _historyLen);
400 set->setValue("general/search_limit", _searchLimit);
401 set->setValue("general/search_bookmarks", _searchBookmarks);
402 set->setValue("general/search_dictionaries", _searchDicts);
407 void Backbone::saveDefaultPrefs(QSettings *set) {
410 set->setValue("general/plugin_path", _defaultPluginPath);
411 set->setValue("general/history_size", _defaultHistoryLen);
412 set->setValue("general/search_limit", _defaultSearchLimit);
417 void Backbone::loadDicts(QString fileName, bool _default) {
421 QFileInfo file(QDir::toNativeSeparators(fileName));
422 QDir confDir(file.dir());
423 if(!confDir.exists()){
424 qDebug() << "Configuration file doesn't exist ("
425 << file.filePath() << ")";
426 Q_EMIT notify(Notify::Warning,
427 QString("%1 configurationfile doesn't exist.")
428 .arg(file.filePath()));
432 QSettings set(file.filePath(), QSettings::IniFormat);
433 QStringList dicts = set.childGroups();
434 foreach(QString dict, dicts) {
435 if(!dict.contains("dictionary_"))
437 CommonDictInterface* plug = plugin
438 (set.value(dict + "/type", "").toString());
440 qDebug() << "Config file error: "
441 << set.value(dict + "/type", "").toString()
443 Q_EMIT notify(Notify::Warning,
444 QString("Configuration file error. %2 plugin doesn't exist.")
445 .arg(set.value(dict + "/type", "").toString()));
448 Settings* plugSet = new Settings();
449 set.beginGroup(dict);
450 QStringList items = set.childKeys();
451 foreach(QString item, items) {
452 plugSet->setValue(item, set.value(item, "").toString());
454 bool active = set.value("active",1).toBool();
457 plugSet->setValue("_default_", "true");
460 addInternalDictionary(plug->getNew(plugSet), active);
466 void Backbone::dictUpdated() {
470 // For convienence this function is called for each change in dictionaries
471 // and each call dumps configuration for all dictionaries into file.
472 // Maybe better way would be to store new/changed configuration but
473 // parsing settings file and figuring out what was changed, in my opinion,
474 // would take more time
475 _history->setMaxSize(_historyLen);
476 QFileInfo file(QDir::toNativeSeparators(_configPath));
477 QDir confDir(file.dir());
478 if(!confDir.exists())
479 confDir.mkpath(file.dir().path());
480 QSettings set(file.filePath(), QSettings::IniFormat);
483 QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
484 QDir defConfDir(defFile.dir());
485 if(!defConfDir.exists())
486 defConfDir.mkpath(defFile.dir().path());
487 QSettings defSet(defFile.filePath(), QSettings::IniFormat);
490 saveDefaultPrefs(&defSet);
492 foreach(CommonDictInterface* dict, _dicts.keys()){
493 if(!dict || !dict->settings())
495 if(!dict->settings()->keys().contains("_default_"))
496 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
498 saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
504 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
512 section.append(QString("dictionary_%1").arg(hash));
513 QList<QString> keys = plugSet->keys();
514 foreach(QString key, keys)
515 set->setValue(section + "/" + key, plugSet->value(key));
516 set->setValue(section + "/active", active);
521 QStringList Backbone::htmls() {
527 void Backbone::searchHtml(QList<Translation *> translations) {
530 QList<TranslationPtr> dummy;
532 foreach(Translation* tr, translations) {
533 if(containsDict(tr->dict()) || !tr->dict())
534 dummy.append(TranslationPtr(tr));
535 /* foreach(CommonDictInterface* dict, activeDicts()) {
536 Translation* trans = dict->getTranslationFor(tr->key());
538 dummy.append(TranslationPtr(trans));
541 if(translations.size()>0) {
542 Translation *tr = translations.at(0);
543 foreach(CommonDictInterface* dict, activeDicts()) {
544 Translation* trans = dict->getTranslationFor(tr->key());
546 dummy.append(TranslationPtr(trans));
550 _innerHtmlResult = QtConcurrent::mapped(dummy,
551 &TranslationPtr::toHtml);
552 _htmlResultWatcher.setFuture(_innerHtmlResult);
557 void Backbone::htmlTranslationReady() {
559 QFutureIterator<QString> it(_innerHtmlResult);
562 uniqe.insert(it.next());
564 _htmlResult = uniqe.toList();
572 QList<CommonDictInterface*> Backbone::activeDicts() {
573 QList<CommonDictInterface*>res;
574 foreach(CommonDictInterface* dict, _dicts.keys())
583 void Backbone::bookmarksListReady() {
584 _bookmarksResult = _innerBookmarks.result();
585 Q_EMIT bookmarksReady();
591 void Backbone::setSettings(Settings *settings) {
592 _historyLen = settings->value("history_size").toInt();
593 _searchLimit = settings->value("search_limit").toInt();
594 if(settings->value("search_dictionaries") == "true")
598 if(settings->value("search_bookmarks") == "true")
599 _searchBookmarks = 1;
601 _searchBookmarks = 0;
610 Settings* Backbone::settings() {
611 Settings * settings = new Settings();
612 settings->setValue("history_size", QString("%1").arg(_historyLen));
613 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
615 settings->setValue("search_bookmarks", "true");
617 settings->setValue("search_bookmarks", "false");
620 settings->setValue("search_dictionaries", "true");
622 settings->setValue("search_dictionaries", "false");
627 bool Backbone::containsDict(uint hash) const {
628 QHashIterator<CommonDictInterface*, bool> it(_dicts);
632 if(it.next().key()->hash() == hash)