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(!QDir(_dir).exists())
79 if(!_configPath.size())
80 _configPath = _dir + "mdictionary.config";
81 if(!_pluginPath.size())
82 _pluginPath = "/usr/lib/mdictionary";
84 //Install default config files
85 ConfigGenerator confGen;
86 confGen.generateCss(_dir + "style.css");
87 confGen.generateDefaultConfig(_configPath);
89 loadPrefs(_configPath);
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(_historyLen, this);
113 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
117 _pluginPath = pluginPath;
118 _configPath = configPath;
128 Backbone::~Backbone()
130 QListIterator<CommonDictInterface*> it(_dicts.keys());
135 it = QListIterator<CommonDictInterface*>(_plugins);
139 QHashIterator<QString, Translation*> it2(_result);
141 delete it2.next().value();
148 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
150 _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
151 _plugins = QList<CommonDictInterface* > (b._plugins);
152 _result = QHash<QString, Translation* > (b._result);
153 _searchLimit = b.searchLimit();
159 int Backbone::searchLimit() const {
165 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
171 QList<CommonDictInterface* > Backbone::getPlugins() {
177 History* Backbone::history() {
183 QMultiHash<QString, Translation*> Backbone::result() {
189 void Backbone::stopSearching() {
193 foreach(CommonDictInterface* dict, _dicts.keys())
196 _innerHtmlResult.cancel();
197 _innerResult.cancel();
198 Q_EMIT searchCanceled();
203 void Backbone::search(QString word){
205 mappedSearch = word.toLower();
209 // When dictFin and bookmarkFin is set to true then translationReady()
210 // signal is emited see translationReady(),
211 // so when searching only in one of them, coresponding *Fin is set to false
212 // and other to true so program is waiting only for one translation
213 dictFin = !_searchDicts;
214 bookmarkFin = !_searchBookmarks;
216 if(!_searchDicts && !_searchBookmarks) {
218 Q_EMIT notify(Notify::Warning, tr("You have to specify where You want "
219 "to look for translations"));
223 _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
224 _resultWatcher.setFuture(_innerResult);
227 if(_searchBookmarks) {
228 _innerBookmarks = QtConcurrent::run(_bookmarks,
229 &Bookmarks::searchWordList, word);
230 _bookmarkSearchWatcher.setFuture(_innerBookmarks);
236 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
237 foreach(CommonDictInterface* dict, _dicts.keys())
238 if(activeDicts.contains(dict))
247 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
248 addInternalDictionary(dict,active);
254 void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
255 dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
256 _dicts[dict] = active;
257 connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
258 connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
259 SIGNAL(notify(Notify::NotifyType,QString)));
264 void Backbone::removeDictionary(CommonDictInterface *dict) {
274 void Backbone::quit() {
281 void Backbone::translationReady() {
282 bool changed = 0; // prevents doubling ready() signal, when both if's are
283 // executed in one translationReady() call then second
284 // translationReady() call doubles ready*() emit
286 if(!dictFin && _innerResult.isFinished()) {
289 QFutureIterator<QList<Translation*> > it(_innerResult);
291 while(it.hasNext()) {
292 QList<Translation* > list = it.next();
293 foreach(Translation* trans, list) {
296 if(!_searchBookmarks)
297 trans->setBookmark(_bookmarks.
298 inBookmarks(trans->key()));
299 _result.insert(trans->key().toLower(), trans);
304 if(!bookmarkFin && _innerBookmarks.isFinished()) {
307 QList<Translation*> list = _innerBookmarks.result();
309 foreach(Translation* trans, list)
310 _result.insert(trans->key().toLower(), trans);
313 if(!stopped && bookmarkFin && dictFin && changed) {
321 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
322 QDir plug(QDir::toNativeSeparators(dir));
324 qDebug() << plug.absolutePath() << " folder doesn't exist";
325 Q_EMIT notify(Notify::Warning,
326 QString("%1 folder doesn't exist.").arg(plug.path()));
327 return QStringList();
329 plug.setFilter(QDir::Files);
330 QStringList list = plug.entryList(nameFilter);
332 for(int i = 0; i < list.size(); i++)
333 list[i] = plug.absoluteFilePath(list.at(i));
338 void Backbone::loadPlugins() {
341 QStringList nameFilter;
342 nameFilter << "*.so" << "*.so.*";
343 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
345 foreach(QString file, files) {
346 QPluginLoader loader(file);
348 Q_EMIT notify(Notify::Error,
349 QString("%1 plugin cannot be loaded: %2.")
350 .arg(file).arg(loader.errorString()));
353 QObject *pl = loader.instance();
356 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
357 foreach(CommonDictInterface* pl, _plugins)
358 if(pl->type() == plugin->type()) {
363 _plugins.append(plugin);
369 CommonDictInterface* Backbone::plugin(QString type) {
370 foreach(CommonDictInterface* plugin, _plugins)
371 if(plugin->type() == type)
378 void Backbone::loadPrefs(QString fileName) {
381 QFileInfo file(QDir::toNativeSeparators(fileName));
382 QDir confDir(file.dir());
383 if(!confDir.exists()){
384 qDebug() << "Configuration file doesn't exist ("
385 << file.filePath() << ")";
386 Q_EMIT notify(Notify::Warning,
387 QString("%1 configuration file doesn't exist.")
388 .arg(file.filePath()));
391 QSettings set(file.filePath(), QSettings::IniFormat);
392 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
393 _historyLen = set.value("general/history_size", 10).toInt();
394 _searchLimit = set.value("general/search_limit", 15).toInt();
395 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
396 _searchDicts = set.value("general/search_dictionaries",1).toBool();
397 _zoom = set.value("general/zoom", 1.0).toReal();
402 void Backbone::savePrefs(QSettings *set) {
405 set->setValue("general/plugin_path", _pluginPath);
406 set->setValue("general/history_size", _historyLen);
407 set->setValue("general/search_limit", _searchLimit);
408 set->setValue("general/search_bookmarks", _searchBookmarks);
409 set->setValue("general/search_dictionaries", _searchDicts);
410 set->setValue("general/zoom", _zoom);
415 void Backbone::loadDicts(QString fileName) {
419 QFileInfo file(QDir::toNativeSeparators(fileName));
420 QDir confDir(file.dir());
421 if(!confDir.exists()){
422 qDebug() << "Configuration file doesn't exist ("
423 << file.filePath() << ")";
424 Q_EMIT notify(Notify::Warning,
425 QString("%1 configurationfile doesn't exist.")
426 .arg(file.filePath()));
430 QSettings set(file.filePath(), QSettings::IniFormat);
431 QStringList dicts = set.childGroups();
432 foreach(QString dict, dicts) {
433 if(!dict.contains("dictionary_"))
435 CommonDictInterface* plug = plugin
436 (set.value(dict + "/type", "").toString());
438 qDebug() << "Config file error: "
439 << set.value(dict + "/type", "").toString()
441 Q_EMIT notify(Notify::Warning,
442 QString("Configuration file error. %2 plugin doesn't exist.")
443 .arg(set.value(dict + "/type", "").toString()));
446 Settings* plugSet = new Settings();
447 set.beginGroup(dict);
448 QStringList items = set.childKeys();
449 foreach(QString item, items) {
450 plugSet->setValue(item, set.value(item, "").toString());
452 bool active = set.value("active",1).toBool();
455 addInternalDictionary(plug->getNew(plugSet), active);
461 void Backbone::dictUpdated() {
465 // For convienence this function is called for each change in dictionaries
466 // and each call dumps configuration for all dictionaries into file.
467 // Maybe better way would be to store new/changed configuration but
468 // parsing settings file and figuring out what was changed, in my opinion,
469 // would take more time
470 _history->setMaxSize(_historyLen);
471 QFileInfo file(QDir::toNativeSeparators(_configPath));
472 QDir confDir(file.dir());
473 if(!confDir.exists())
474 confDir.mkpath(file.dir().path());
475 QSettings set(file.filePath(), QSettings::IniFormat);
480 foreach(CommonDictInterface* dict, _dicts.keys()){
481 if(!dict || !dict->settings())
483 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
489 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
497 section.append(QString("dictionary_%1").arg(hash));
498 QList<QString> keys = plugSet->keys();
499 foreach(QString key, keys)
500 set->setValue(section + "/" + key, plugSet->value(key));
501 set->setValue(section + "/active", active);
506 QStringList Backbone::htmls() {
512 void Backbone::searchHtml(QList<Translation *> translations) {
515 QList<TranslationPtr> dummy;
517 foreach(Translation* tr, translations) {
518 if(containsDict(tr->dict()) || !tr->dict())
519 dummy.append(TranslationPtr(tr));
520 /* foreach(CommonDictInterface* dict, activeDicts()) {
521 Translation* trans = dict->getTranslationFor(tr->key());
523 dummy.append(TranslationPtr(trans));
526 if(translations.size()>0) {
527 Translation *tr = translations.at(0);
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;
587 _zoom = settings->value("zoom").toFloat();
599 Settings* Backbone::settings() {
600 Settings * settings = new Settings();
601 settings->setValue("history_size", QString("%1").arg(_historyLen));
602 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
603 settings->setValue("zoom", QString("%1").arg(_zoom));
605 settings->setValue("search_bookmarks", "true");
607 settings->setValue("search_bookmarks", "false");
610 settings->setValue("search_dictionaries", "true");
612 settings->setValue("search_dictionaries", "false");
617 bool Backbone::containsDict(uint hash) const {
618 QHashIterator<CommonDictInterface*, bool> it(_dicts);
622 if(it.next().key()->hash() == hash)