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>
31 int Backbone::_searchLimit;
33 // Sadly QtConcurent::mapped dosent let me use something like calling method of
34 // some class with supplied argument; so i have to sin against art and put
35 // global function and variable so i could supply function with some parametr
37 QList<Translation*> mapSearch(CommonDictInterface *dict) {
39 return dict->searchWordList(mappedSearch, Backbone::_searchLimit);
40 return QList<Translation*>();
45 /*! Smart pointer (kind of) for translation object
47 QtConcurent::mapped use collection of data and one function, what i need is
48 to map signle data object to method calls for multiple objects. TranslationPtr
49 is try to store method call as a data -> moreover QtConcurent allow only for
50 methods without any parameters so TranslationPtr is created with Translation
51 object -> ready to call toHtml() for supplied Translation.
53 Another thing is that QtConcurent dont like pointers in data collection
54 so TranslationPtr is way to hide real translation object (pointer for object)
56 class TranslationPtr {
59 TranslationPtr(Translation* tr) :_tr(tr) {}
61 /*! \return translation text for corresponding Translation object */
62 QString toHtml() const {
64 trans = _tr->toHtml();
70 void Backbone::init() {
72 _dir = QDir::homePath() + "/.mdictionary/";
73 if(!_configPath.size())
74 _configPath = _dir + "mdictionary.config";
75 if(!_defaultConfigPath.size())
76 _defaultConfigPath = _dir + "mdictionary.defaults";
77 if(!_pluginPath.size())
78 _pluginPath = "/usr/lib/mdictionary";
82 //Install default config files
83 ConfigGenerator().generateCss(_dir + "style.css");
87 loadPrefs(_defaultConfigPath);
89 // Default configuration are stored in separate config file and we dont want
91 _defaultPluginPath = _pluginPath;
92 _defaultHistoryLen = _historyLen;
93 _defaultSearchLimit = _searchLimit;
94 loadPrefs(_configPath);
98 loadDicts(_defaultConfigPath, true);
99 loadDicts(_configPath);
101 connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
102 connect(&_htmlResultWatcher, SIGNAL(finished()), this,
103 SLOT(htmlTranslationReady()));
104 connect(&_bookmarkWatcher, SIGNAL(finished()), this,
105 SLOT(bookmarksListReady()));
106 connect(&_bookmarkSearchWatcher, SIGNAL(finished()), this,
107 SLOT(translationReady()));
109 // In common opinion perfect thread count is cores_number+1 (in qt perfect
110 // thread count is set to cores number
111 QThreadPool::globalInstance()->setMaxThreadCount(
112 QThreadPool::globalInstance()->maxThreadCount()+1);
114 _history = new History(5, this);
120 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
124 _pluginPath = pluginPath;
125 _configPath = configPath;
127 _defaultConfigPath = configPath;
136 Backbone::~Backbone()
138 QListIterator<CommonDictInterface*> it(_dicts.keys());
143 it = QListIterator<CommonDictInterface*>(_plugins);
147 QHashIterator<QString, Translation*> it2(_result);
149 delete it2.next().value();
156 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
157 _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
158 _plugins = QList<CommonDictInterface* > (b._plugins);
159 _result = QHash<QString, Translation* > (b._result);
160 _searchLimit = b.searchLimit();
166 int Backbone::searchLimit() const {
172 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
178 QList<CommonDictInterface* > Backbone::getPlugins() {
184 History* Backbone::history() {
190 QMultiHash<QString, Translation*> Backbone::result() {
196 void Backbone::stopSearching() {
200 foreach(CommonDictInterface* dict, _dicts.keys())
203 _innerHtmlResult.cancel();
204 _innerResult.cancel();
205 Q_EMIT searchCanceled();
210 void Backbone::search(QString word){
212 mappedSearch = word.toLower();
216 // When dictFin and bookmarkFin is set to true then translationReady()
217 // signal is emited see translationReady(),
218 // so when searching only in one of them, coresponding *Fin is set to false
219 // and other to true so program is waiting only for one translation
220 dictFin = !_searchDicts;
221 bookmarkFin = !_searchBookmarks;
224 _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
225 _resultWatcher.setFuture(_innerResult);
228 if(_searchBookmarks) {
229 _innerBookmarks = QtConcurrent::run(_bookmarks,
230 &Bookmarks::searchWordList, word);
231 _bookmarkSearchWatcher.setFuture(_innerBookmarks);
237 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
238 foreach(CommonDictInterface* dict, _dicts.keys())
239 if(activeDicts.contains(dict))
248 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
249 addInternalDictionary(dict,active);
255 void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
256 dict->setHash(++_dictNum); // Hash must be uniqe in every session but not between
257 _dicts[dict] = active;
259 connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
260 connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
261 SIGNAL(notify(Notify::NotifyType,QString)));
264 void Backbone::removeDictionary(CommonDictInterface *dict) {
273 void Backbone::quit() {
280 void Backbone::translationReady() {
281 bool changed = 0; // prevents doubling ready() signal, when both if's are
282 // executed in one translationReady() call then second
283 // translationReady() call doubles ready*() emit
285 if(!dictFin && _innerResult.isFinished()) {
288 QFutureIterator<QList<Translation*> > it(_innerResult);
290 while(it.hasNext()) {
291 QList<Translation* > list = it.next();
292 foreach(Translation* trans, list)
293 _result.insert(trans->key().toLower(), trans);
297 if(!bookmarkFin && _innerBookmarks.isFinished()) {
300 QList<Translation*> list = _innerBookmarks.result();
302 foreach(Translation* trans, list)
303 _result.insert(trans->key().toLower(), trans);
306 if(!stopped && bookmarkFin && dictFin && changed) {
311 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
312 QDir plug(QDir::toNativeSeparators(dir));
314 qDebug() << plug.absolutePath() << " folder doesn't exist";
315 Q_EMIT notify(Notify::Warning,
316 QString("%1 folder doesn't exist.").arg(plug.path()));
317 return QStringList();
319 plug.setFilter(QDir::Files);
320 QStringList list = plug.entryList(nameFilter);
322 for(int i = 0; i < list.size(); i++)
323 list[i] = plug.absoluteFilePath(list.at(i));
328 void Backbone::loadPlugins() {
331 QStringList nameFilter;
332 nameFilter << "*.so";
333 QStringList files = getFilesFromDir(_pluginPath, nameFilter);
335 foreach(QString file, files) {
336 QPluginLoader loader(file);
338 Q_EMIT notify(Notify::Error,
339 QString("%1 plugin cannot be loaded: %2.")
340 .arg(file).arg(loader.errorString()));
341 qDebug()<< file << " " << loader.errorString();
344 QObject *pl = loader.instance();
346 CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
347 _plugins.append(plugin);
353 CommonDictInterface* Backbone::plugin(QString type) {
354 foreach(CommonDictInterface* plugin, _plugins)
355 if(plugin->type() == type)
362 void Backbone::loadPrefs(QString fileName) {
365 QFileInfo file(QDir::toNativeSeparators(fileName));
366 QDir confDir(file.dir());
367 if(!confDir.exists()){
368 qDebug() << "Configuration file doesn't exist ("
369 << file.filePath() << ")";
370 Q_EMIT notify(Notify::Warning,
371 QString("%1 configuration file doesn't exist.")
372 .arg(file.filePath()));
375 QSettings set(file.filePath(), QSettings::IniFormat);
376 _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
377 _historyLen = set.value("general/history_size", 10).toInt();
378 _searchLimit = set.value("general/search_limit", 15).toInt();
379 _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
380 _searchDicts = set.value("general/search_dictionaries",1).toBool();
385 void Backbone::savePrefs(QSettings *set) {
388 set->setValue("general/plugin_path", _pluginPath);
389 set->setValue("general/history_size", _historyLen);
390 set->setValue("general/search_limit", _searchLimit);
391 set->setValue("general/search_bookmarks", _searchBookmarks);
392 set->setValue("general/search_dictionaries", _searchDicts);
397 void Backbone::saveDefaultPrefs(QSettings *set) {
400 set->setValue("general/plugin_path", _defaultPluginPath);
401 set->setValue("general/history_size", _defaultHistoryLen);
402 set->setValue("general/search_limit", _defaultSearchLimit);
407 void Backbone::loadDicts(QString fileName, bool _default) {
411 QFileInfo file(QDir::toNativeSeparators(fileName));
412 QDir confDir(file.dir());
413 if(!confDir.exists()){
414 qDebug() << "Configuration file doesn't exist ("
415 << file.filePath() << ")";
416 Q_EMIT notify(Notify::Warning,
417 QString("%1 configurationfile doesn't exist.")
418 .arg(file.filePath()));
422 QSettings set(file.filePath(), QSettings::IniFormat);
423 QStringList dicts = set.childGroups();
424 foreach(QString dict, dicts) {
425 if(!dict.contains("dictionary_"))
427 CommonDictInterface* plug = plugin
428 (set.value(dict + "/type", "").toString());
430 qDebug() << "Config file error: "
431 << set.value(dict + "/type", "").toString()
433 Q_EMIT notify(Notify::Warning,
434 QString("Configuration file error. %2 plugin doesn't exist.")
435 .arg(set.value(dict + "/type", "").toString()));
438 Settings* plugSet = new Settings();
439 set.beginGroup(dict);
440 QStringList items = set.childKeys();
441 foreach(QString item, items) {
442 plugSet->setValue(item, set.value(item, "").toString());
444 bool active = set.value("active",1).toBool();
447 plugSet->setValue("_default_", "true");
450 addInternalDictionary(plug->getNew(plugSet), active);
456 void Backbone::dictUpdated() {
460 // For convienence this function is called for each change in dictionaries
461 // and each call dumps configuration for all dictionaries into file.
462 // Maybe better way would be to store new/changed configuration but
463 // parsing settings file and figuring out what was changed, in my opinion,
464 // would take more time
465 _history->setMaxSize(_historyLen);
466 QFileInfo file(QDir::toNativeSeparators(_configPath));
467 QDir confDir(file.dir());
468 if(!confDir.exists())
469 confDir.mkpath(file.dir().path());
470 QSettings set(file.filePath(), QSettings::IniFormat);
473 QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
474 QDir defConfDir(defFile.dir());
475 if(!defConfDir.exists())
476 defConfDir.mkpath(defFile.dir().path());
477 QSettings defSet(defFile.filePath(), QSettings::IniFormat);
480 saveDefaultPrefs(&defSet);
482 foreach(CommonDictInterface* dict, _dicts.keys()){
483 if(!dict || !dict->settings())
485 if(!dict->settings()->keys().contains("_default_"))
486 saveState(&set, dict->settings(), _dicts[dict], dict->hash());
488 saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
494 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
502 section.append(QString("dictionary_%1").arg(hash));
503 QList<QString> keys = plugSet->keys();
504 foreach(QString key, keys)
505 set->setValue(section + "/" + key, plugSet->value(key));
506 set->setValue(section + "/active", active);
511 QStringList Backbone::htmls() {
517 void Backbone::searchHtml(QList<Translation *> translations) {
520 QList<TranslationPtr> dummy;
522 foreach(Translation* tr, translations) {
523 if(containsDict(tr->dict()) || !tr->dict())
524 dummy.append(TranslationPtr(tr));
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;
592 Settings* Backbone::settings() {
593 Settings * settings = new Settings();
594 settings->setValue("history_size", QString("%1").arg(_historyLen));
595 settings->setValue("search_limit", QString("%1").arg(_searchLimit));
597 settings->setValue("search_bookmarks", "true");
599 settings->setValue("search_bookmarks", "false");
602 settings->setValue("search_dictionaries", "true");
604 settings->setValue("search_dictionaries", "false");
609 bool Backbone::containsDict(uint hash) const {
610 QHashIterator<CommonDictInterface*, bool> it(_dicts);
614 if(it.next().key()->hash() == hash)