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/plugins";
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) {
269 qDebug()<<"delete empty dict";
277 void Backbone::quit() {
284 void Backbone::translationReady() {
285 bool changed = 0; // prevents doubling ready() signal, when both if's are
286 // executed in one translationReady() call then second
287 // translationReady() call doubles ready*() emit
289 if(!dictFin && _innerResult.isFinished()) {
292 QFutureIterator<QList<Translation*> > it(_innerResult);
294 while(it.hasNext()) {
295 QList<Translation* > list = it.next();
296 foreach(Translation* trans, list) {
299 if(!_searchBookmarks)
300 trans->setBookmark(_bookmarks.
301 inBookmarks(trans->key()));
302 _result.insert(trans->key().toLower(), trans);
307 if(!bookmarkFin && _innerBookmarks.isFinished()) {
310 QList<Translation*> list = _innerBookmarks.result();
312 foreach(Translation* trans, list)
313 _result.insert(trans->key().toLower(), trans);
316 if(!stopped && bookmarkFin && dictFin && changed) {
324 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
325 QDir plug(QDir::toNativeSeparators(dir));
327 qDebug() << plug.absolutePath() << " folder doesn't exist";
328 Q_EMIT notify(Notify::Warning,
329 QString("%1 folder doesn't exist.").arg(plug.path()));
330 return QStringList();
332 plug.setFilter(QDir::Files);
333 QStringList list = plug.entryList(nameFilter);
335 for(int i = 0; i < list.size(); i++)
336 list[i] = plug.absoluteFilePath(list.at(i));
341 void Backbone::loadPlugins() {
344 QStringList nameFilter;
345 nameFilter << "*.so" << "*.so.*";
346 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
348 foreach(QString file, files) {
349 QPluginLoader loader(file);
351 Q_EMIT notify(Notify::Error,
352 QString("%1 plugin cannot be loaded: %2.")
353 .arg(file).arg(loader.errorString()));
356 QObject *pl = loader.instance();
359 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
360 foreach(CommonDictInterface* pl, _plugins)
361 if(pl->type() == plugin->type()) {
366 _plugins.append(plugin);
372 CommonDictInterface* Backbone::plugin(QString type) {
373 foreach(CommonDictInterface* plugin, _plugins)
374 if(plugin->type() == type)
381 void Backbone::loadPrefs(QString fileName) {
384 QFileInfo file(QDir::toNativeSeparators(fileName));
385 QDir confDir(file.dir());
386 if(!confDir.exists()){
387 qDebug() << "Configuration file doesn't exist ("
388 << file.filePath() << ")";
389 Q_EMIT notify(Notify::Warning,
390 QString("%1 configuration file doesn't exist.")
391 .arg(file.filePath()));
394 QSettings set(file.filePath(), QSettings::IniFormat);
395 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
396 _historyLen = set.value("general/history_size", 10).toInt();
397 _searchLimit = set.value("general/search_limit", 15).toInt();
398 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
399 _searchDicts = set.value("general/search_dictionaries",1).toBool();
400 _zoom = set.value("general/zoom", 1.0).toReal();
405 void Backbone::savePrefs(QSettings *set) {
408 set->setValue("general/plugin_path", _pluginPath);
409 set->setValue("general/history_size", _historyLen);
410 set->setValue("general/search_limit", _searchLimit);
411 set->setValue("general/search_bookmarks", _searchBookmarks);
412 set->setValue("general/search_dictionaries", _searchDicts);
413 set->setValue("general/zoom", _zoom);
418 void Backbone::loadDicts(QString fileName) {
422 QFileInfo file(QDir::toNativeSeparators(fileName));
423 QDir confDir(file.dir());
424 if(!confDir.exists()){
425 qDebug() << "Configuration file doesn't exist ("
426 << file.filePath() << ")";
427 Q_EMIT notify(Notify::Warning,
428 QString("%1 configurationfile doesn't exist.")
429 .arg(file.filePath()));
433 QSettings set(file.filePath(), QSettings::IniFormat);
434 QStringList dicts = set.childGroups();
435 foreach(QString dict, dicts) {
436 if(!dict.contains("dictionary_"))
438 CommonDictInterface* plug = plugin
439 (set.value(dict + "/type", "").toString());
441 qDebug() << "Config file error: "
442 << set.value(dict + "/type", "").toString()
444 Q_EMIT notify(Notify::Warning,
445 QString("Configuration file error. %2 plugin doesn't exist.")
446 .arg(set.value(dict + "/type", "").toString()));
449 Settings* plugSet = new Settings();
450 set.beginGroup(dict);
451 QStringList items = set.childKeys();
452 foreach(QString item, items) {
453 plugSet->setValue(item, set.value(item, "").toString());
455 bool active = set.value("active",1).toBool();
458 addInternalDictionary(plug->getNew(plugSet), active);
465 void Backbone::dictUpdated() {
469 // For convienence this function is called for each change in dictionaries
470 // and each call dumps configuration for all dictionaries into file.
471 // Maybe better way would be to store new/changed configuration but
472 // parsing settings file and figuring out what was changed, in my opinion,
473 // would take more time
474 _history->setMaxSize(_historyLen);
475 QFileInfo file(QDir::toNativeSeparators(_configPath));
476 QDir confDir(file.dir());
477 if(!confDir.exists())
478 confDir.mkpath(file.dir().path());
479 QSettings set(file.filePath(), QSettings::IniFormat);
484 foreach(CommonDictInterface* dict, _dicts.keys()){
485 if(!dict || !dict->settings())
487 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
493 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
501 section.append(QString("dictionary_%1").arg(hash));
502 QList<QString> keys = plugSet->keys();
503 foreach(QString key, keys)
504 set->setValue(section + "/" + key, plugSet->value(key));
505 set->setValue(section + "/active", active);
510 QStringList Backbone::htmls() {
516 void Backbone::searchHtml(QList<Translation *> translations) {
519 QList<TranslationPtr> dummy;
521 foreach(Translation* tr, translations) {
522 if(containsDict(tr->dict()) || !tr->dict())
523 dummy.append(TranslationPtr(tr));
524 /* foreach(CommonDictInterface* dict, activeDicts()) {
525 Translation* trans = dict->getTranslationFor(tr->key());
527 dummy.append(TranslationPtr(trans));
530 if(translations.size()>0) {
531 Translation *tr = translations.at(0);
532 foreach(CommonDictInterface* dict, activeDicts()) {
533 Translation* trans = dict->getTranslationFor(tr->key());
535 dummy.append(TranslationPtr(trans));
539 _innerHtmlResult = QtConcurrent::mapped(dummy,
540 &TranslationPtr::toHtml);
541 _htmlResultWatcher.setFuture(_innerHtmlResult);
546 void Backbone::htmlTranslationReady() {
548 QFutureIterator<QString> it(_innerHtmlResult);
551 uniqe.insert(it.next());
553 _htmlResult = uniqe.toList();
561 QList<CommonDictInterface*> Backbone::activeDicts() {
562 QList<CommonDictInterface*>res;
563 foreach(CommonDictInterface* dict, _dicts.keys())
572 void Backbone::bookmarksListReady() {
573 _bookmarksResult = _innerBookmarks.result();
574 Q_EMIT bookmarksReady();
580 void Backbone::setSettings(Settings *settings) {
581 _historyLen = settings->value("history_size").toInt();
582 _searchLimit = settings->value("search_limit").toInt();
583 if(settings->value("search_dictionaries") == "true")
587 if(settings->value("search_bookmarks") == "true")
588 _searchBookmarks = 1;
590 _searchBookmarks = 0;
591 _zoom = settings->value("zoom").toFloat();
603 Settings* Backbone::settings() {
604 Settings * settings = new Settings();
605 settings->setValue("history_size", QString("%1").arg(_historyLen));
606 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
607 settings->setValue("zoom", QString("%1").arg(_zoom));
609 settings->setValue("search_bookmarks", "true");
611 settings->setValue("search_bookmarks", "false");
614 settings->setValue("search_dictionaries", "true");
616 settings->setValue("search_dictionaries", "false");
621 bool Backbone::containsDict(uint hash) const {
622 QHashIterator<CommonDictInterface*, bool> it(_dicts);
626 if(it.next().key()->hash() == hash)