Added notification iterface
[mdictionary] / trunk / src / base / backbone / backbone.cpp
1 /*******************************************************************************
2
3     This file is part of mDictionary.
4
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.
9
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.
14
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/>.
17
18     Copyright 2010 Comarch S.A.
19
20 *******************************************************************************/
21 /*! /file backbone.cpp
22 \brief Backbone/core main file \see Backbone
23
24
25 \author Bartosz Szatkowski <bulislaw@linux.com>
26 */
27
28 #include "backbone.h"
29 #include <QDebug>
30
31 QString mappedSearch;
32 QList<Translation*> mapSearch(CommonDictInterface *dict) {
33     qDebug() << "ooooooooooooooooooooooo" << unsigned(dict);
34     if(dict)
35         return dict->searchWordList(mappedSearch, 15);
36     return QList<Translation*>();
37 }
38
39 class TranslationPtr {
40     Translation* _tr;
41 public:
42     TranslationPtr(Translation* tr) :_tr(tr) {}
43     QString toHtml() const {
44         QString trans;
45         trans = _tr->toHtml();
46         return trans;
47
48     }
49 };
50
51 void Backbone::init() {
52
53    if(!_configPath.size())
54        _configPath = QDir::homePath() + "/.mdictionary/mdictionary.config";
55    if(!_defaultConfigPath.size())
56        _defaultConfigPath = QDir::homePath() + "/.mdictionary/mdictionary.defaults";
57    if(!_pluginPath.size())
58        _pluginPath = "/usr/lib/mdictionary";
59    _historyLen = 10;
60    _searchLimit = 15;
61
62    loadPrefs(_defaultConfigPath);
63    _defaultPluginPath = _pluginPath;
64    _defaultHistoryLen = _historyLen;
65    _defaultSearchLimit = _searchLimit;
66    loadPrefs(_configPath);
67
68    loadPlugins();
69
70    loadDicts(_defaultConfigPath, true);
71    loadDicts(_configPath);
72
73    connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
74    connect(&_htmlResultWatcher, SIGNAL(finished()), this,
75            SLOT(htmlTranslationReady()));
76    connect(&_bookmarkWatcher, SIGNAL(finished()), this,
77            SLOT(bookmarksListReady()));
78    connect(&_bookmarkSearchWatcher, SIGNAL(finished()), this,
79            SLOT(translationReady()));
80
81    QThreadPool::globalInstance()->setMaxThreadCount(
82            QThreadPool::globalInstance()->maxThreadCount()+1);
83
84    _history = new History(5, this);
85 }
86
87
88
89 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
90                    QObject *parent)
91     : QObject(parent)
92 {
93     _pluginPath = pluginPath;
94     _configPath = configPath;
95     _defaultConfigPath = configPath;
96     dryRun = false;
97     if(dry)
98         dryRun = true;
99     init();
100 }
101
102
103
104 Backbone::~Backbone()
105 {
106     QListIterator<CommonDictInterface*> it(_dicts.keys());
107
108     while(it.hasNext())
109         delete it.next();
110
111     it = QListIterator<CommonDictInterface*>(_plugins);
112     while(it.hasNext())
113         delete it.next();
114
115     QHashIterator<QString, Translation*> it2(_result);
116     while(it2.hasNext())
117         delete it2.next().value();
118
119 }
120
121
122
123
124 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
125     _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
126     _plugins = QList<CommonDictInterface* > (b._plugins);
127     _result = QHash<QString, Translation* > (b._result);
128     _searchLimit = b.searchLimit();
129 }
130
131
132
133
134 int Backbone::searchLimit() const {
135     return _searchLimit;
136 }
137
138
139
140 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
141     return _dicts;
142 }
143
144
145
146 QList<CommonDictInterface* > Backbone::getPlugins() {
147     return _plugins;
148 }
149
150
151
152 History* Backbone::history() {
153     return _history;
154 }
155
156
157
158 QMultiHash<QString, Translation*> Backbone::result() {
159     return _result;
160 }
161
162
163
164 void Backbone::stopSearching() {
165     if(stopped)
166         return;
167
168     foreach(CommonDictInterface* dict, _dicts.keys())
169         dict->stop();
170     stopped = true;
171     _innerHtmlResult.cancel();
172     _innerResult.cancel();
173     Q_EMIT searchCanceled();
174 }
175
176
177
178 void Backbone::search(QString word){
179     _result.clear();
180     mappedSearch = word.toLower();
181
182     stopped = false;
183     dictFin = !_searchDicts;
184     bookmarkFin = !_searchBookmarks;
185
186     if (_searchDicts) {
187         _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
188         _resultWatcher.setFuture(_innerResult);
189     }
190
191     if(_searchBookmarks) {
192         _innerBookmarks = QtConcurrent::run(_bookmarks,
193                 &Bookmarks::searchWordList, word);
194         _bookmarkSearchWatcher.setFuture(_innerBookmarks);
195     }
196 }
197
198
199
200 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
201     foreach(CommonDictInterface* dict, _dicts.keys())
202         if(activeDicts.contains(dict))
203             _dicts[dict] = 1;
204         else
205             _dicts[dict] = 0;
206     dictUpdated();
207  }
208
209
210
211 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
212     addInternalDictionary(dict,active);
213     dictUpdated();
214 }
215
216
217
218  void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
219      dict->setHash(_dicts.size()+1);
220      _dicts[dict] = active;
221      connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
222      connect(dict, SIGNAL(notify(Notify::NotifyType,QString)), this,
223              SIGNAL(notify(Notify::NotifyType,QString)));
224  }
225
226  void Backbone::removeDictionary(CommonDictInterface *dict) {
227      _dicts.remove(dict);
228      dictUpdated();
229
230  }
231
232
233
234  void Backbone::quit() {
235     stopSearching();
236     Q_EMIT closeOk();
237 }
238
239
240
241 void Backbone::translationReady() {
242     if(!dictFin && _innerResult.isFinished()) {
243         dictFin = 1;
244         QFutureIterator<QList<Translation*> > it(_innerResult);
245
246         while(it.hasNext()) {
247             QList<Translation* > list = it.next();
248             foreach(Translation* trans, list)
249                 _result.insert(trans->key().toLower(), trans);
250         }
251     }
252
253     if(!bookmarkFin && _innerBookmarks.isFinished()) {
254         bookmarkFin = 1;
255         QList<Translation*> list = _innerBookmarks.result();
256
257         foreach(Translation* trans, list)
258                 _result.insert(trans->key().toLower(), trans);
259     }
260
261     if(!stopped && bookmarkFin && dictFin)
262         Q_EMIT ready();
263 }
264
265 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
266     QDir plug(QDir::toNativeSeparators(dir));
267     if(!plug.exists()) {
268         qDebug() << plug.absolutePath() << " folder dosen't exists";
269         Q_EMIT notify(Notify::Warning,
270                 QString("%1 folder dosen't exists.").arg(plug.path()));
271         return QStringList();
272     }
273     plug.setFilter(QDir::Files);
274     QStringList list = plug.entryList(nameFilter);
275
276     for(int i = 0; i < list.size(); i++)
277         list[i] = plug.absoluteFilePath(list.at(i));
278     return list;
279 }
280
281
282 void Backbone::loadPlugins() {
283     if(dryRun)
284         return;
285     QStringList nameFilter;
286     nameFilter << "*.so";
287     QStringList files = getFilesFromDir(_pluginPath, nameFilter);
288
289     foreach(QString file, files) {
290         QPluginLoader loader(file);
291         if(!loader.load()) {
292             Q_EMIT notify(Notify::Error,
293                     QString("%1 plugin cannot be loaded: %2.")
294                     .arg(file).arg(loader.errorString()));
295             qDebug()<< file << " " << loader.errorString();
296             continue;
297         }
298         QObject *pl = loader.instance();
299
300         CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
301         _plugins.append(plugin);
302     }
303 }
304
305
306
307 CommonDictInterface* Backbone::plugin(QString type) {
308     foreach(CommonDictInterface* plugin, _plugins)
309         if(plugin->type() == type)
310             return plugin;
311     return 0;
312 }
313
314
315
316 void Backbone::loadPrefs(QString fileName) {
317     if(dryRun)
318         return;
319     QFileInfo file(QDir::toNativeSeparators(fileName));
320     QDir confDir(file.dir());
321     if(!confDir.exists()){
322         qDebug() << "Configuration file dosn't exists ("
323                 << file.filePath() << ")";
324         Q_EMIT notify(Notify::Warning,
325                 QString("%1 configurationfile dosen't exists.")
326                 .arg(file.filePath()));
327         return;
328     }
329     QSettings set(file.filePath(), QSettings::IniFormat);
330     _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
331     _historyLen = set.value("general/history_size", 10).toInt();
332     _searchLimit = set.value("general/search_limit", 15).toInt();
333     _searchBookmarks = set.value("general/search_bookmarks",1).toBool();
334     _searchDicts = set.value("general/search_dictionaries",1).toBool();
335 }
336
337
338
339 void Backbone::savePrefs(QSettings *set) {
340     if(dryRun)
341         return;
342     set->setValue("general/plugin_path", _pluginPath);
343     set->setValue("general/history_size", _historyLen);
344     set->setValue("general/search_limit", _searchLimit);
345     set->setValue("general/search_bookmarks", _searchBookmarks);
346     set->setValue("general/search_dictionaries", _searchDicts);
347 }
348
349
350
351 void Backbone::saveDefaultPrefs(QSettings *set) {
352     if(dryRun)
353         return;
354     set->setValue("general/plugin_path", _defaultPluginPath);
355     set->setValue("general/history_size", _defaultHistoryLen);
356     set->setValue("general/search_limit", _defaultSearchLimit);
357 }
358
359
360
361 void Backbone::loadDicts(QString fileName, bool _default) {
362     if(dryRun)
363         return;
364     QFileInfo file(QDir::toNativeSeparators(fileName));
365     QDir confDir(file.dir());
366     if(!confDir.exists()){
367         qDebug() << "Configuration file dosn't exists ("
368                 << file.filePath() << ")";
369         Q_EMIT notify(Notify::Warning,
370                 QString("%1 configurationfile dosen't exists.")
371                 .arg(file.filePath()));
372         return;
373     }
374
375     QSettings set(file.filePath(), QSettings::IniFormat);
376     QStringList dicts = set.childGroups();
377     foreach(QString dict, dicts) {
378         if(!dict.contains("dictionary_"))
379             continue;
380         CommonDictInterface* plug = plugin
381                                     (set.value(dict + "/type", "").toString());
382         if(!plug) {
383             qDebug() << "Config file error: "
384                     << set.value(dict + "/type", "").toString()
385                     << " dosen't exists";
386             Q_EMIT notify(Notify::Warning,
387                     QString("Configuration file error. %2 plugin dosen't exists.")
388                     .arg(set.value(dict + "/type", "").toString()));
389             continue;
390         }
391         Settings* plugSet = new Settings();
392         set.beginGroup(dict);
393         QStringList items = set.childKeys();
394         foreach(QString item, items) {
395             plugSet->setValue(item, set.value(item, "").toString());
396         }
397         bool active = set.value("active",1).toBool();
398
399         if(_default)
400             plugSet->setValue("_default_", "true");
401
402         set.endGroup();
403         addInternalDictionary(plug->getNew(plugSet), active);
404     }
405 }
406
407
408
409 void Backbone::dictUpdated() {
410     if(dryRun)
411         return;
412     _history->setMaxSize(_historyLen);
413     QFileInfo file(QDir::toNativeSeparators(_configPath));
414     QDir confDir(file.dir());
415     if(!confDir.exists())
416         confDir.mkpath(file.dir().path());
417     QSettings set(file.filePath(), QSettings::IniFormat);
418     set.clear();
419
420     QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
421     QDir defConfDir(defFile.dir());
422     if(!defConfDir.exists())
423         defConfDir.mkpath(defFile.dir().path());
424     QSettings defSet(defFile.filePath(), QSettings::IniFormat);
425     defSet.clear();
426     savePrefs(&set);
427     saveDefaultPrefs(&defSet);
428
429     foreach(CommonDictInterface* dict, _dicts.keys()){
430         if(!dict || !dict->settings())
431             continue;
432         if(!dict->settings()->keys().contains("_default_"))
433             saveState(&set, dict->settings(), _dicts[dict], dict->hash());
434         else
435             saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
436     }
437 }
438
439
440
441 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
442                          , uint hash) {
443     if(dryRun)
444         return;
445     if(!set || !plugSet)
446         return;
447     QString section;
448     section.append(QString("dictionary_%1").arg(hash));
449     QList<QString> keys = plugSet->keys();
450     foreach(QString key, keys)
451         set->setValue(section + "/" + key, plugSet->value(key));
452     set->setValue(section + "/active", active);
453 }
454
455
456
457 QStringList Backbone::htmls() {
458     return _htmlResult;
459 }
460
461
462
463 void Backbone::searchHtml(QList<Translation *> translations) {
464     _htmlResult.clear();
465
466     QList<TranslationPtr> dummy;
467     stopped = false;
468     foreach(Translation* tr, translations)
469         dummy.append(TranslationPtr(tr));
470
471    _innerHtmlResult = QtConcurrent::mapped(dummy,
472                                             &TranslationPtr::toHtml);
473    _htmlResultWatcher.setFuture(_innerHtmlResult);
474 }
475
476 void Backbone::htmlTranslationReady() {
477
478     QFutureIterator<QString> it(_innerHtmlResult);
479     while(it.hasNext())
480        _htmlResult.append(it.next());
481
482     if(!stopped)
483         Q_EMIT htmlReady();
484
485 }
486
487
488 QList<CommonDictInterface*> Backbone::activeDicts() {
489     QList<CommonDictInterface*>res;
490     foreach(CommonDictInterface* dict, _dicts.keys())
491         if(_dicts[dict])
492             res.append(dict);
493     return res;
494
495 }
496
497
498
499 void Backbone::bookmarksListReady() {
500    _bookmarksResult = _innerBookmarks.result();
501    Q_EMIT bookmarksReady();
502 }
503
504
505
506
507 void Backbone::setSettings(Settings *settings) {
508     _historyLen = settings->value("history_size").toInt();
509     _searchLimit = settings->value("search_limit").toInt();
510     if(settings->value("search_dictionaries") == "true")
511         _searchDicts = 1;
512     else
513         _searchDicts = 0;
514     if(settings->value("search_bookmarks") == "true")
515         _searchBookmarks = 1;
516     else
517         _searchBookmarks = 0;
518     dictUpdated();
519 }
520
521
522
523
524 Settings* Backbone::settings() {
525     Settings * settings = new Settings();
526     settings->setValue("history_size", QString("%1").arg(_historyLen));
527     settings->setValue("search_limit", QString("%1").arg(_searchLimit));
528     if(_searchBookmarks)
529         settings->setValue("search_bookmarks", "true");
530     else
531         settings->setValue("search_bookmarks", "false");
532
533     if(_searchDicts)
534         settings->setValue("search_dictionaries", "true");
535     else
536         settings->setValue("search_dictionaries", "false");
537     return settings;
538 }