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;
213 if(!_searchDicts && !_searchBookmarks) {
214 Q_EMIT notify(Notify::Warning, tr("You have to specify where You want "
215 "to look for translations"));
220 _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
221 _resultWatcher.setFuture(_innerResult);
224 if(_searchBookmarks) {
225 _innerBookmarks = QtConcurrent::run(_bookmarks,
226 &Bookmarks::searchWordList, word);
227 _bookmarkSearchWatcher.setFuture(_innerBookmarks);
233 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
234 foreach(CommonDictInterface* dict, _dicts.keys())
235 if(activeDicts.contains(dict))
244 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
245 addInternalDictionary(dict,active);
251 void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
252 dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
253 _dicts[dict] = active;
254 connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
255 connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
256 SIGNAL(notify(Notify::NotifyType,QString)));
261 void Backbone::removeDictionary(CommonDictInterface *dict) {
271 void Backbone::quit() {
278 void Backbone::translationReady() {
279 bool changed = 0; // prevents doubling ready() signal, when both if's are
280 // executed in one translationReady() call then second
281 // translationReady() call doubles ready*() emit
283 if(!dictFin && _innerResult.isFinished()) {
286 QFutureIterator<QList<Translation*> > it(_innerResult);
288 while(it.hasNext()) {
289 QList<Translation* > list = it.next();
290 foreach(Translation* trans, list) {
293 if(!_searchBookmarks)
294 trans->setBookmark(_bookmarks.
295 inBookmarks(trans->key()));
296 _result.insert(trans->key().toLower(), trans);
301 if(!bookmarkFin && _innerBookmarks.isFinished()) {
304 QList<Translation*> list = _innerBookmarks.result();
306 foreach(Translation* trans, list)
307 _result.insert(trans->key().toLower(), trans);
310 if(!stopped && bookmarkFin && dictFin && changed) {
318 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
319 QDir plug(QDir::toNativeSeparators(dir));
321 qDebug() << plug.absolutePath() << " folder doesn't exist";
322 Q_EMIT notify(Notify::Warning,
323 QString("%1 folder doesn't exist.").arg(plug.path()));
324 return QStringList();
326 plug.setFilter(QDir::Files);
327 QStringList list = plug.entryList(nameFilter);
329 for(int i = 0; i < list.size(); i++)
330 list[i] = plug.absoluteFilePath(list.at(i));
335 void Backbone::loadPlugins() {
338 QStringList nameFilter;
339 nameFilter << "*.so" << "*.so.*";
340 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
342 foreach(QString file, files) {
343 QPluginLoader loader(file);
345 Q_EMIT notify(Notify::Error,
346 QString("%1 plugin cannot be loaded: %2.")
347 .arg(file).arg(loader.errorString()));
350 QObject *pl = loader.instance();
353 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
354 foreach(CommonDictInterface* pl, _plugins)
355 if(pl->type() == plugin->type()) {
360 _plugins.append(plugin);
366 CommonDictInterface* Backbone::plugin(QString type) {
367 foreach(CommonDictInterface* plugin, _plugins)
368 if(plugin->type() == type)
375 void Backbone::loadPrefs(QString fileName) {
378 QFileInfo file(QDir::toNativeSeparators(fileName));
379 QDir confDir(file.dir());
380 if(!confDir.exists()){
381 qDebug() << "Configuration file doesn't exist ("
382 << file.filePath() << ")";
383 Q_EMIT notify(Notify::Warning,
384 QString("%1 configuration file doesn't exist.")
385 .arg(file.filePath()));
388 QSettings set(file.filePath(), QSettings::IniFormat);
389 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
390 _historyLen = set.value("general/history_size", 10).toInt();
391 _searchLimit = set.value("general/search_limit", 15).toInt();
392 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
393 _searchDicts = set.value("general/search_dictionaries",1).toBool();
394 _zoom = set.value("general/zoom", 1.0).toReal();
399 void Backbone::savePrefs(QSettings *set) {
402 set->setValue("general/plugin_path", _pluginPath);
403 set->setValue("general/history_size", _historyLen);
404 set->setValue("general/search_limit", _searchLimit);
405 set->setValue("general/search_bookmarks", _searchBookmarks);
406 set->setValue("general/search_dictionaries", _searchDicts);
407 set->setValue("general/zoom", _zoom);
412 void Backbone::loadDicts(QString fileName) {
416 QFileInfo file(QDir::toNativeSeparators(fileName));
417 QDir confDir(file.dir());
418 if(!confDir.exists()){
419 qDebug() << "Configuration file doesn't exist ("
420 << file.filePath() << ")";
421 Q_EMIT notify(Notify::Warning,
422 QString("%1 configurationfile doesn't exist.")
423 .arg(file.filePath()));
427 QSettings set(file.filePath(), QSettings::IniFormat);
428 QStringList dicts = set.childGroups();
429 foreach(QString dict, dicts) {
430 if(!dict.contains("dictionary_"))
432 CommonDictInterface* plug = plugin
433 (set.value(dict + "/type", "").toString());
435 qDebug() << "Config file error: "
436 << set.value(dict + "/type", "").toString()
438 Q_EMIT notify(Notify::Warning,
439 QString("Configuration file error. %2 plugin doesn't exist.")
440 .arg(set.value(dict + "/type", "").toString()));
443 Settings* plugSet = new Settings();
444 set.beginGroup(dict);
445 QStringList items = set.childKeys();
446 foreach(QString item, items) {
447 plugSet->setValue(item, set.value(item, "").toString());
449 bool active = set.value("active",1).toBool();
452 addInternalDictionary(plug->getNew(plugSet), active);
458 void Backbone::dictUpdated() {
462 // For convienence this function is called for each change in dictionaries
463 // and each call dumps configuration for all dictionaries into file.
464 // Maybe better way would be to store new/changed configuration but
465 // parsing settings file and figuring out what was changed, in my opinion,
466 // would take more time
467 _history->setMaxSize(_historyLen);
468 QFileInfo file(QDir::toNativeSeparators(_configPath));
469 QDir confDir(file.dir());
470 if(!confDir.exists())
471 confDir.mkpath(file.dir().path());
472 QSettings set(file.filePath(), QSettings::IniFormat);
477 foreach(CommonDictInterface* dict, _dicts.keys()){
478 if(!dict || !dict->settings())
480 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
486 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
494 section.append(QString("dictionary_%1").arg(hash));
495 QList<QString> keys = plugSet->keys();
496 foreach(QString key, keys)
497 set->setValue(section + "/" + key, plugSet->value(key));
498 set->setValue(section + "/active", active);
503 QStringList Backbone::htmls() {
509 void Backbone::searchHtml(QList<Translation *> translations) {
512 QList<TranslationPtr> dummy;
514 foreach(Translation* tr, translations) {
515 if(containsDict(tr->dict()) || !tr->dict())
516 dummy.append(TranslationPtr(tr));
517 /* foreach(CommonDictInterface* dict, activeDicts()) {
518 Translation* trans = dict->getTranslationFor(tr->key());
520 dummy.append(TranslationPtr(trans));
523 if(translations.size()>0) {
524 Translation *tr = translations.at(0);
525 foreach(CommonDictInterface* dict, activeDicts()) {
526 Translation* trans = dict->getTranslationFor(tr->key());
528 dummy.append(TranslationPtr(trans));
532 _innerHtmlResult = QtConcurrent::mapped(dummy,
533 &TranslationPtr::toHtml);
534 _htmlResultWatcher.setFuture(_innerHtmlResult);
539 void Backbone::htmlTranslationReady() {
541 QFutureIterator<QString> it(_innerHtmlResult);
544 uniqe.insert(it.next());
546 _htmlResult = uniqe.toList();
554 QList<CommonDictInterface*> Backbone::activeDicts() {
555 QList<CommonDictInterface*>res;
556 foreach(CommonDictInterface* dict, _dicts.keys())
565 void Backbone::bookmarksListReady() {
566 _bookmarksResult = _innerBookmarks.result();
567 Q_EMIT bookmarksReady();
573 void Backbone::setSettings(Settings *settings) {
574 _historyLen = settings->value("history_size").toInt();
575 _searchLimit = settings->value("search_limit").toInt();
576 if(settings->value("search_dictionaries") == "true")
580 if(settings->value("search_bookmarks") == "true")
581 _searchBookmarks = 1;
583 _searchBookmarks = 0;
584 _zoom = settings->value("zoom").toFloat();
596 Settings* Backbone::settings() {
597 Settings * settings = new Settings();
598 settings->setValue("history_size", QString("%1").arg(_historyLen));
599 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
600 settings->setValue("zoom", QString("%1").arg(_zoom));
602 settings->setValue("search_bookmarks", "true");
604 settings->setValue("search_bookmarks", "false");
607 settings->setValue("search_dictionaries", "true");
609 settings->setValue("search_dictionaries", "false");
614 bool Backbone::containsDict(uint hash) const {
615 QHashIterator<CommonDictInterface*, bool> it(_dicts);
619 if(it.next().key()->hash() == hash)