Changed timer based result fatching for signal based
[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     if(dict)
34         return dict->searchWordList(mappedSearch, 15);
35     return QList<Translation*>();
36 }
37
38 class TranslationPtr {
39     Translation* _tr;
40 public:
41     TranslationPtr(Translation* tr) :_tr(tr) {}
42     QString toHtml() const {
43         QString trans;
44         trans = _tr->toHtml();
45         return trans;
46
47     }
48 };
49
50 void Backbone::init() {
51
52    if(!_configPath.size())
53        _configPath = QDir::homePath() + "/.mdictionary/mdictionary.config";
54    if(!_defaultConfigPath.size())
55        _defaultConfigPath = QDir::homePath() + "/.mdictionary/mdictionary.defaults";
56    if(!_pluginPath.size())
57        _pluginPath = "/usr/lib/mdictionary";
58    _historyLen = 10;
59    _searchLimit = 15;
60
61    loadPrefs(_defaultConfigPath);
62    _defaultPluginPath = _pluginPath;
63    _defaultHistoryLen = _historyLen;
64    _defaultSearchLimit = _searchLimit;
65    loadPrefs(_configPath);
66
67    loadPlugins();
68
69    loadDicts(_defaultConfigPath, true);
70    loadDicts(_configPath);
71
72    connect(&_resultWatcher, SIGNAL(finished()), this, SLOT(translationReady()));
73    connect(&_htmlResultWatcher, SIGNAL(finished()), this,
74            SLOT(htmlTranslationReady()));
75
76    QThreadPool::globalInstance()->setMaxThreadCount(
77            QThreadPool::globalInstance()->maxThreadCount()+1);
78
79    _history = new History(5, this);
80 }
81
82 Backbone::Backbone(QString pluginPath, QString configPath, bool dry,
83                    QObject *parent)
84     : QObject(parent)
85 {
86     _pluginPath = pluginPath;
87     _configPath = configPath;
88     _defaultConfigPath = configPath;
89     dryRun = false;
90     if(dry)
91         dryRun = true;
92     init();
93 }
94
95
96
97 Backbone::~Backbone()
98 {
99     QListIterator<CommonDictInterface*> it(_dicts.keys());
100
101     while(it.hasNext())
102         delete it.next();
103
104     it = QListIterator<CommonDictInterface*>(_plugins);
105     while(it.hasNext())
106         delete it.next();
107
108     QHashIterator<QString, Translation*> it2(_result);
109     while(it2.hasNext())
110         delete it2.next().value();
111
112 }
113
114
115
116
117 Backbone::Backbone(const Backbone &b) :QObject(b.parent()) {
118    // init();
119     _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
120     _plugins = QList<CommonDictInterface* > (b._plugins);
121     _result = QHash<QString, Translation* > (b._result);
122     _searchLimit = b.searchLimit();
123 }
124
125
126
127
128 int Backbone::searchLimit() const {
129     return _searchLimit;
130 }
131
132
133
134
135 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
136     return _dicts;
137 }
138
139
140
141
142 QList<CommonDictInterface* > Backbone::getPlugins() {
143     return _plugins;
144 }
145
146
147
148
149 History* Backbone::history() {
150     return _history;
151 }
152
153
154
155
156 QMultiHash<QString, Translation*> Backbone::result() {
157     return _result;
158 }
159
160
161
162
163 void Backbone::stopSearching() {
164     foreach(CommonDictInterface* dict, _dicts.keys())
165         dict->stop();
166     _innerHtmlResult.cancel();
167     _innerResult.cancel();
168 }
169
170
171
172
173
174
175 void Backbone::search(QString word) {
176     _result.clear();
177     mappedSearch = word.toLower();
178     //_time.restart();
179
180     _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
181     _resultWatcher.setFuture(_innerResult);
182
183
184
185     //foreach(CommonDictInterface* dict, _dicts.keys())
186     //   if(_dicts[dict] == 1) {
187     //        QFuture<QList<Translation*> > tr =
188     //                QtConcurrent::run(dict,
189     //                              &CommonDictInterface::searchWordList,word,
190     //                                                     searchLimit());
191     //        _innerResult.append(tr);
192     //    }
193
194 }
195
196
197
198
199 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
200     foreach(CommonDictInterface* dict, _dicts.keys())
201         if(activeDicts.contains(dict))
202             _dicts[dict] = 1;
203         else
204             _dicts[dict] = 0;
205     dictUpdated();
206  }
207
208
209
210 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
211     addInternalDictionary(dict,active);
212     dictUpdated();
213 }
214
215
216
217  void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
218      dict->setHash(_dicts.size()+1);
219      _dicts[dict] = active;
220      connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
221  }
222
223  void Backbone::removeDictionary(CommonDictInterface *dict) {
224      _dicts.remove(dict);
225      dictUpdated();
226
227  }
228
229
230
231  void Backbone::quit() {
232     stopSearching();
233     Q_EMIT closeOk();
234 }
235
236
237
238
239
240
241 void Backbone::translationReady() {
242     //if(!_innerResult.isFinished())
243      //   return;
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     //qDebug () << "time " << _time.elapsed();
253     Q_EMIT ready();
254 }
255
256 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
257     QDir plug(QDir::toNativeSeparators(dir));
258     if(!plug.exists()) {
259         qDebug() << plug.absolutePath() << " folder dosen't exists";
260         return QStringList();
261     }
262     plug.setFilter(QDir::Files);
263     QStringList list = plug.entryList(nameFilter);
264
265     for(int i = 0; i < list.size(); i++)
266         list[i] = plug.absoluteFilePath(list.at(i));
267     return list;
268 }
269
270
271 void Backbone::loadPlugins() {
272     if(dryRun)
273         return;
274     QStringList nameFilter;
275     nameFilter << "*.so";
276     QStringList files = getFilesFromDir(_pluginPath, nameFilter);
277
278     foreach(QString file, files) {
279         QPluginLoader loader(file);
280         if(!loader.load()) {
281             qDebug()<< file << " " << loader.errorString();
282             continue;
283         }
284         QObject *pl = loader.instance();
285
286         CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
287         _plugins.append(plugin);
288     }
289 }
290
291
292
293 CommonDictInterface* Backbone::plugin(QString type) {
294     foreach(CommonDictInterface* plugin, _plugins)
295         if(plugin->type() == type)
296             return plugin;
297     return 0;
298 }
299
300
301
302 void Backbone::loadPrefs(QString fileName) {
303     if(dryRun)
304         return;
305     QFileInfo file(QDir::toNativeSeparators(fileName));
306     QDir confDir(file.dir());
307     if(!confDir.exists()){
308         qDebug() << "Configuration file dosn't exists ("
309                 << file.filePath() << ")";
310         return;
311     }
312     QSettings set(file.filePath(), QSettings::IniFormat);
313     _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
314     _historyLen = set.value("general/history_length", 10).toInt();
315     _searchLimit = set.value("general/search_limit", 15).toInt();
316 }
317
318
319
320 void Backbone::savePrefs(QSettings *set) {
321     if(dryRun)
322         return;
323     set->setValue("general/plugin_path", _pluginPath);
324     set->setValue("general/history_length", _historyLen);
325     set->setValue("general/search_limit", _searchLimit);
326 }
327
328
329
330 void Backbone::saveDefaultPrefs(QSettings *set) {
331     if(dryRun)
332         return;
333     set->setValue("general/plugin_path", _defaultPluginPath);
334     set->setValue("general/history_length", _defaultHistoryLen);
335     set->setValue("general/search_limit", _defaultSearchLimit);
336 }
337
338
339
340 void Backbone::loadDicts(QString fileName, bool _default) {
341     if(dryRun)
342         return;
343     QFileInfo file(QDir::toNativeSeparators(fileName));
344     QDir confDir(file.dir());
345     if(!confDir.exists()){
346         qDebug() << "Configuration file dosn't exists ("
347                 << file.filePath() << ")";
348         return;
349     }
350
351     QSettings set(file.filePath(), QSettings::IniFormat);
352     QStringList dicts = set.childGroups();
353     foreach(QString dict, dicts) {
354         if(!dict.contains("dictionary_"))
355             continue;
356         CommonDictInterface* plug = plugin
357                                     (set.value(dict + "/type", "").toString());
358         if(!plug) {
359             qDebug() << "Config file error: "
360                     << set.value(dict + "/type", "").toString()
361                     << " dosen't exists";
362             continue;
363         }
364         Settings* plugSet = new Settings();
365         set.beginGroup(dict);
366         QStringList items = set.childKeys();
367         foreach(QString item, items) {
368             plugSet->setValue(item, set.value(item, "").toString());
369         }
370         bool active = set.value("active",1).toBool();
371
372         if(_default)
373             plugSet->setValue("_default_", "true");
374
375         set.endGroup();
376         addInternalDictionary(plug->getNew(plugSet), active);
377     }
378 }
379
380
381
382 void Backbone::dictUpdated() {
383     if(dryRun)
384         return;
385     QFileInfo file(QDir::toNativeSeparators(_configPath));
386     QDir confDir(file.dir());
387     if(!confDir.exists())
388         confDir.mkpath(file.dir().path());
389     QSettings set(file.filePath(), QSettings::IniFormat);
390     set.clear();
391
392     QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
393     QDir defConfDir(defFile.dir());
394     if(!defConfDir.exists())
395         defConfDir.mkpath(defFile.dir().path());
396     QSettings defSet(defFile.filePath(), QSettings::IniFormat);
397     defSet.clear();
398     savePrefs(&set);
399     saveDefaultPrefs(&defSet);
400
401     foreach(CommonDictInterface* dict, _dicts.keys()){
402         if(!dict || !dict->settings())
403             continue;
404         if(!dict->settings()->keys().contains("_default_"))
405             saveState(&set, dict->settings(), _dicts[dict], dict->hash());
406         else
407             saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
408     }
409 }
410
411
412
413 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
414                          , uint hash) {
415     if(dryRun)
416         return;
417     if(!set || !plugSet)
418         return;
419     QString section;
420     section.append(QString("dictionary_%1").arg(hash));
421     QList<QString> keys = plugSet->keys();
422     foreach(QString key, keys)
423         set->setValue(section + "/" + key, plugSet->value(key));
424     set->setValue(section + "/active", active);
425 }
426
427
428
429 QStringList Backbone::htmls() {
430     return _htmlResult;
431 }
432
433
434
435 void Backbone::searchHtml(QList<Translation *> translations) {
436     _htmlResult.clear();
437     QList<TranslationPtr> dummy;
438     //_time.restart();
439     foreach(Translation* tr, translations)
440         dummy.append(TranslationPtr(tr));
441
442    _innerHtmlResult = QtConcurrent::mapped(dummy,
443                                             &TranslationPtr::toHtml);
444    _htmlResultWatcher.setFuture(_innerHtmlResult);
445 }
446
447 void Backbone::htmlTranslationReady() {
448     //if(!_innerHtmlResult.isFinished())
449         //return;
450
451     QFutureIterator<QString> it(_innerHtmlResult);
452     while(it.hasNext())
453        _htmlResult.append(it.next());
454
455     //qDebug() << "time " << _time.elapsed();
456     Q_EMIT htmlReady();
457
458 }
459
460
461 QList<CommonDictInterface*> Backbone::activeDicts() {
462     QList<CommonDictInterface*>res;
463     foreach(CommonDictInterface* dict, _dicts.keys())
464         if(_dicts[dict])
465             res.append(dict);
466     return res;
467
468 }