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() {
75 _dir = QDir::homePath() + "/.mdictionary/";
76 if(!_configPath.size())
77 _configPath = _dir + "mdictionary.config";
78 if(!_pluginPath.size())
79 _pluginPath = "/usr/lib/mdictionary";
81 //Install default config files
82 ConfigGenerator confGen;
83 confGen.generateCss(_dir + "style.css");
84 confGen.generateDefaultConfig(_configPath);
86 loadPrefs(_configPath);
90 loadDicts(_configPath);
92 connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
93 connect(&_htmlResultWatcher, SIGNAL(finished()), this,
94 SLOT(htmlTranslationReady()));
95 connect(&_bookmarkWatcher, SIGNAL(finished()), this,
96 SLOT(bookmarksListReady()));
97 connect(&_bookmarkSearchWatcher, SIGNAL(finished()), this,
98 SLOT(translationReady()));
100 // In common opinion perfect thread count is cores_number+1 (in qt perfect
101 // thread count is set to cores number
102 QThreadPool::globalInstance()->setMaxThreadCount(
103 QThreadPool::globalInstance()->maxThreadCount()+1);
105 _history = new History(_historyLen, this);
110 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
114 _pluginPath = pluginPath;
115 _configPath = configPath;
125 Backbone::~Backbone()
127 QListIterator<CommonDictInterface*> it(_dicts.keys());
132 it = QListIterator<CommonDictInterface*>(_plugins);
136 QHashIterator<QString, Translation*> it2(_result);
138 delete it2.next().value();
145 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
147 _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
148 _plugins = QList<CommonDictInterface* > (b._plugins);
149 _result = QHash<QString, Translation* > (b._result);
150 _searchLimit = b.searchLimit();
156 int Backbone::searchLimit() const {
162 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
168 QList<CommonDictInterface* > Backbone::getPlugins() {
174 History* Backbone::history() {
180 QMultiHash<QString, Translation*> Backbone::result() {
186 void Backbone::stopSearching() {
190 foreach(CommonDictInterface* dict, _dicts.keys())
193 _innerHtmlResult.cancel();
194 _innerResult.cancel();
195 Q_EMIT searchCanceled();
200 void Backbone::search(QString word){
202 mappedSearch = word.toLower();
206 // When dictFin and bookmarkFin is set to true then translationReady()
207 // signal is emited see translationReady(),
208 // so when searching only in one of them, coresponding *Fin is set to false
209 // and other to true so program is waiting only for one translation
210 dictFin = !_searchDicts;
211 bookmarkFin = !_searchBookmarks;
214 _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
215 _resultWatcher.setFuture(_innerResult);
218 if(_searchBookmarks) {
219 _innerBookmarks = QtConcurrent::run(_bookmarks,
220 &Bookmarks::searchWordList, word);
221 _bookmarkSearchWatcher.setFuture(_innerBookmarks);
227 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
228 foreach(CommonDictInterface* dict, _dicts.keys())
229 if(activeDicts.contains(dict))
238 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
239 addInternalDictionary(dict,active);
245 void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
246 dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
247 _dicts[dict] = active;
249 connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
250 connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
251 SIGNAL(notify(Notify::NotifyType,QString)));
256 void Backbone::removeDictionary(CommonDictInterface *dict) {
265 void Backbone::quit() {
272 void Backbone::translationReady() {
273 bool changed = 0; // prevents doubling ready() signal, when both if's are
274 // executed in one translationReady() call then second
275 // translationReady() call doubles ready*() emit
277 if(!dictFin && _innerResult.isFinished()) {
280 QFutureIterator<QList<Translation*> > it(_innerResult);
282 while(it.hasNext()) {
283 QList<Translation* > list = it.next();
284 foreach(Translation* trans, list)
285 _result.insert(trans->key().toLower(), trans);
289 if(!bookmarkFin && _innerBookmarks.isFinished()) {
292 QList<Translation*> list = _innerBookmarks.result();
294 foreach(Translation* trans, list)
295 _result.insert(trans->key().toLower(), trans);
298 if(!stopped && bookmarkFin && dictFin && changed) {
306 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
307 QDir plug(QDir::toNativeSeparators(dir));
309 qDebug() << plug.absolutePath() << " folder doesn't exist";
310 Q_EMIT notify(Notify::Warning,
311 QString("%1 folder doesn't exist.").arg(plug.path()));
312 return QStringList();
314 plug.setFilter(QDir::Files);
315 QStringList list = plug.entryList(nameFilter);
317 for(int i = 0; i < list.size(); i++)
318 list[i] = plug.absoluteFilePath(list.at(i));
323 void Backbone::loadPlugins() {
326 QStringList nameFilter;
327 nameFilter << "*.so" << "*.so.*";
328 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
330 foreach(QString file, files) {
331 QPluginLoader loader(file);
333 Q_EMIT notify(Notify::Error,
334 QString("%1 plugin cannot be loaded: %2.")
335 .arg(file).arg(loader.errorString()));
338 QObject *pl = loader.instance();
341 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
342 foreach(CommonDictInterface* pl, _plugins)
343 if(pl->type() == plugin->type()) {
348 _plugins.append(plugin);
354 CommonDictInterface* Backbone::plugin(QString type) {
355 foreach(CommonDictInterface* plugin, _plugins)
356 if(plugin->type() == type)
363 void Backbone::loadPrefs(QString fileName) {
366 QFileInfo file(QDir::toNativeSeparators(fileName));
367 QDir confDir(file.dir());
368 if(!confDir.exists()){
369 qDebug() << "Configuration file doesn't exist ("
370 << file.filePath() << ")";
371 Q_EMIT notify(Notify::Warning,
372 QString("%1 configuration file doesn't exist.")
373 .arg(file.filePath()));
376 QSettings set(file.filePath(), QSettings::IniFormat);
377 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
378 _historyLen = set.value("general/history_size", 10).toInt();
379 _searchLimit = set.value("general/search_limit", 15).toInt();
380 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
381 _searchDicts = set.value("general/search_dictionaries",1).toBool();
386 void Backbone::savePrefs(QSettings *set) {
389 set->setValue("general/plugin_path", _pluginPath);
390 set->setValue("general/history_size", _historyLen);
391 set->setValue("general/search_limit", _searchLimit);
392 set->setValue("general/search_bookmarks", _searchBookmarks);
393 set->setValue("general/search_dictionaries", _searchDicts);
398 void Backbone::loadDicts(QString fileName) {
402 QFileInfo file(QDir::toNativeSeparators(fileName));
403 QDir confDir(file.dir());
404 if(!confDir.exists()){
405 qDebug() << "Configuration file doesn't exist ("
406 << file.filePath() << ")";
407 Q_EMIT notify(Notify::Warning,
408 QString("%1 configurationfile doesn't exist.")
409 .arg(file.filePath()));
413 QSettings set(file.filePath(), QSettings::IniFormat);
414 QStringList dicts = set.childGroups();
415 foreach(QString dict, dicts) {
416 if(!dict.contains("dictionary_"))
418 CommonDictInterface* plug = plugin
419 (set.value(dict + "/type", "").toString());
421 qDebug() << "Config file error: "
422 << set.value(dict + "/type", "").toString()
424 Q_EMIT notify(Notify::Warning,
425 QString("Configuration file error. %2 plugin doesn't exist.")
426 .arg(set.value(dict + "/type", "").toString()));
429 Settings* plugSet = new Settings();
430 set.beginGroup(dict);
431 QStringList items = set.childKeys();
432 foreach(QString item, items) {
433 plugSet->setValue(item, set.value(item, "").toString());
435 bool active = set.value("active",1).toBool();
438 addInternalDictionary(plug->getNew(plugSet), active);
444 void Backbone::dictUpdated() {
448 // For convienence this function is called for each change in dictionaries
449 // and each call dumps configuration for all dictionaries into file.
450 // Maybe better way would be to store new/changed configuration but
451 // parsing settings file and figuring out what was changed, in my opinion,
452 // would take more time
453 _history->setMaxSize(_historyLen);
454 QFileInfo file(QDir::toNativeSeparators(_configPath));
455 QDir confDir(file.dir());
456 if(!confDir.exists())
457 confDir.mkpath(file.dir().path());
458 QSettings set(file.filePath(), QSettings::IniFormat);
463 foreach(CommonDictInterface* dict, _dicts.keys()){
464 if(!dict || !dict->settings())
466 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
472 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
480 section.append(QString("dictionary_%1").arg(hash));
481 QList<QString> keys = plugSet->keys();
482 foreach(QString key, keys)
483 set->setValue(section + "/" + key, plugSet->value(key));
484 set->setValue(section + "/active", active);
489 QStringList Backbone::htmls() {
495 void Backbone::searchHtml(QList<Translation *> translations) {
498 QList<TranslationPtr> dummy;
500 foreach(Translation* tr, translations) {
501 if(containsDict(tr->dict()) || !tr->dict())
502 dummy.append(TranslationPtr(tr));
503 /* foreach(CommonDictInterface* dict, activeDicts()) {
504 Translation* trans = dict->getTranslationFor(tr->key());
506 dummy.append(TranslationPtr(trans));
509 if(translations.size()>0) {
510 Translation *tr = translations.at(0);
511 foreach(CommonDictInterface* dict, activeDicts()) {
512 Translation* trans = dict->getTranslationFor(tr->key());
514 dummy.append(TranslationPtr(trans));
518 _innerHtmlResult = QtConcurrent::mapped(dummy,
519 &TranslationPtr::toHtml);
520 _htmlResultWatcher.setFuture(_innerHtmlResult);
525 void Backbone::htmlTranslationReady() {
527 QFutureIterator<QString> it(_innerHtmlResult);
530 uniqe.insert(it.next());
532 _htmlResult = uniqe.toList();
540 QList<CommonDictInterface*> Backbone::activeDicts() {
541 QList<CommonDictInterface*>res;
542 foreach(CommonDictInterface* dict, _dicts.keys())
551 void Backbone::bookmarksListReady() {
552 _bookmarksResult = _innerBookmarks.result();
553 Q_EMIT bookmarksReady();
559 void Backbone::setSettings(Settings *settings) {
560 _historyLen = settings->value("history_size").toInt();
561 _searchLimit = settings->value("search_limit").toInt();
562 if(settings->value("search_dictionaries") == "true")
566 if(settings->value("search_bookmarks") == "true")
567 _searchBookmarks = 1;
569 _searchBookmarks = 0;
578 Settings* Backbone::settings() {
579 Settings * settings = new Settings();
580 settings->setValue("history_size", QString("%1").arg(_historyLen));
581 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
583 settings->setValue("search_bookmarks", "true");
585 settings->setValue("search_bookmarks", "false");
588 settings->setValue("search_dictionaries", "true");
590 settings->setValue("search_dictionaries", "false");
595 bool Backbone::containsDict(uint hash) const {
596 QHashIterator<CommonDictInterface*, bool> it(_dicts);
600 if(it.next().key()->hash() == hash)