Minor behavioral fixes
[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     _dicts = QHash<CommonDictInterface*, bool > (b._dicts);
119     _plugins = QList<CommonDictInterface* > (b._plugins);
120     _result = QHash<QString, Translation* > (b._result);
121     _searchLimit = b.searchLimit();
122 }
123
124
125
126
127 int Backbone::searchLimit() const {
128     return _searchLimit;
129 }
130
131
132
133
134 QHash<CommonDictInterface*, bool > Backbone::getDictionaries() {
135     return _dicts;
136 }
137
138
139
140
141 QList<CommonDictInterface* > Backbone::getPlugins() {
142     return _plugins;
143 }
144
145
146
147
148 History* Backbone::history() {
149     return _history;
150 }
151
152
153
154
155 QMultiHash<QString, Translation*> Backbone::result() {
156     return _result;
157 }
158
159
160
161
162 void Backbone::stopSearching() {
163     foreach(CommonDictInterface* dict, _dicts.keys())
164         dict->stop();
165     _innerHtmlResult.cancel();
166     _innerResult.cancel();
167 }
168
169
170
171
172
173
174 void Backbone::search(QString word) {
175     _result.clear();
176     mappedSearch = word.toLower();
177     //_time.restart();
178
179     _innerResult = QtConcurrent::mapped(activeDicts(), mapSearch);
180     _resultWatcher.setFuture(_innerResult);
181 }
182
183
184
185
186 void Backbone::selectedDictionaries(QList<CommonDictInterface* > activeDicts) {
187     foreach(CommonDictInterface* dict, _dicts.keys())
188         if(activeDicts.contains(dict))
189             _dicts[dict] = 1;
190         else
191             _dicts[dict] = 0;
192     dictUpdated();
193  }
194
195
196
197 void Backbone::addDictionary(CommonDictInterface *dict, bool active) {
198     addInternalDictionary(dict,active);
199     dictUpdated();
200 }
201
202
203
204  void Backbone::addInternalDictionary(CommonDictInterface* dict, bool active) {
205      dict->setHash(_dicts.size()+1);
206      _dicts[dict] = active;
207      connect(dict, SIGNAL(settingsChanged()), this, SLOT(dictUpdated()));
208  }
209
210  void Backbone::removeDictionary(CommonDictInterface *dict) {
211      _dicts.remove(dict);
212      dictUpdated();
213
214  }
215
216
217
218  void Backbone::quit() {
219     stopSearching();
220     Q_EMIT closeOk();
221 }
222
223
224
225
226
227
228 void Backbone::translationReady() {
229     //if(!_innerResult.isFinished())
230      //   return;
231     QFutureIterator<QList<Translation*> > it(_innerResult);
232
233     while(it.hasNext()) {
234         QList<Translation* > list = it.next();
235         foreach(Translation* trans, list)
236             _result.insert(trans->key().toLower(), trans);
237     }
238
239     //qDebug () << "time " << _time.elapsed();
240     Q_EMIT ready();
241 }
242
243 QStringList Backbone::getFilesFromDir(QString dir, QStringList nameFilter) {
244     QDir plug(QDir::toNativeSeparators(dir));
245     if(!plug.exists()) {
246         qDebug() << plug.absolutePath() << " folder dosen't exists";
247         return QStringList();
248     }
249     plug.setFilter(QDir::Files);
250     QStringList list = plug.entryList(nameFilter);
251
252     for(int i = 0; i < list.size(); i++)
253         list[i] = plug.absoluteFilePath(list.at(i));
254     return list;
255 }
256
257
258 void Backbone::loadPlugins() {
259     if(dryRun)
260         return;
261     QStringList nameFilter;
262     nameFilter << "*.so";
263     QStringList files = getFilesFromDir(_pluginPath, nameFilter);
264
265     foreach(QString file, files) {
266         QPluginLoader loader(file);
267         if(!loader.load()) {
268             qDebug()<< file << " " << loader.errorString();
269             continue;
270         }
271         QObject *pl = loader.instance();
272
273         CommonDictInterface *plugin = qobject_cast<CommonDictInterface*>(pl);
274         _plugins.append(plugin);
275     }
276 }
277
278
279
280 CommonDictInterface* Backbone::plugin(QString type) {
281     foreach(CommonDictInterface* plugin, _plugins)
282         if(plugin->type() == type)
283             return plugin;
284     return 0;
285 }
286
287
288
289 void Backbone::loadPrefs(QString fileName) {
290     if(dryRun)
291         return;
292     QFileInfo file(QDir::toNativeSeparators(fileName));
293     QDir confDir(file.dir());
294     if(!confDir.exists()){
295         qDebug() << "Configuration file dosn't exists ("
296                 << file.filePath() << ")";
297         return;
298     }
299     QSettings set(file.filePath(), QSettings::IniFormat);
300     _pluginPath = set.value("general/plugin_path", _pluginPath).toString();
301     _historyLen = set.value("general/history_length", 10).toInt();
302     _searchLimit = set.value("general/search_limit", 15).toInt();
303 }
304
305
306
307 void Backbone::savePrefs(QSettings *set) {
308     if(dryRun)
309         return;
310     set->setValue("general/plugin_path", _pluginPath);
311     set->setValue("general/history_length", _historyLen);
312     set->setValue("general/search_limit", _searchLimit);
313 }
314
315
316
317 void Backbone::saveDefaultPrefs(QSettings *set) {
318     if(dryRun)
319         return;
320     set->setValue("general/plugin_path", _defaultPluginPath);
321     set->setValue("general/history_length", _defaultHistoryLen);
322     set->setValue("general/search_limit", _defaultSearchLimit);
323 }
324
325
326
327 void Backbone::loadDicts(QString fileName, bool _default) {
328     if(dryRun)
329         return;
330     QFileInfo file(QDir::toNativeSeparators(fileName));
331     QDir confDir(file.dir());
332     if(!confDir.exists()){
333         qDebug() << "Configuration file dosn't exists ("
334                 << file.filePath() << ")";
335         return;
336     }
337
338     QSettings set(file.filePath(), QSettings::IniFormat);
339     QStringList dicts = set.childGroups();
340     foreach(QString dict, dicts) {
341         if(!dict.contains("dictionary_"))
342             continue;
343         CommonDictInterface* plug = plugin
344                                     (set.value(dict + "/type", "").toString());
345         if(!plug) {
346             qDebug() << "Config file error: "
347                     << set.value(dict + "/type", "").toString()
348                     << " dosen't exists";
349             continue;
350         }
351         Settings* plugSet = new Settings();
352         set.beginGroup(dict);
353         QStringList items = set.childKeys();
354         foreach(QString item, items) {
355             plugSet->setValue(item, set.value(item, "").toString());
356         }
357         bool active = set.value("active",1).toBool();
358
359         if(_default)
360             plugSet->setValue("_default_", "true");
361
362         set.endGroup();
363         addInternalDictionary(plug->getNew(plugSet), active);
364     }
365 }
366
367
368
369 void Backbone::dictUpdated() {
370     if(dryRun)
371         return;
372     QFileInfo file(QDir::toNativeSeparators(_configPath));
373     QDir confDir(file.dir());
374     if(!confDir.exists())
375         confDir.mkpath(file.dir().path());
376     QSettings set(file.filePath(), QSettings::IniFormat);
377     set.clear();
378
379     QFileInfo defFile(QDir::toNativeSeparators(_defaultConfigPath));
380     QDir defConfDir(defFile.dir());
381     if(!defConfDir.exists())
382         defConfDir.mkpath(defFile.dir().path());
383     QSettings defSet(defFile.filePath(), QSettings::IniFormat);
384     defSet.clear();
385     savePrefs(&set);
386     saveDefaultPrefs(&defSet);
387
388     foreach(CommonDictInterface* dict, _dicts.keys()){
389         if(!dict || !dict->settings())
390             continue;
391         if(!dict->settings()->keys().contains("_default_"))
392             saveState(&set, dict->settings(), _dicts[dict], dict->hash());
393         else
394             saveState(&defSet, dict->settings(), _dicts[dict], dict->hash());
395     }
396 }
397
398
399
400 void Backbone::saveState(QSettings* set, Settings* plugSet, bool active
401                          , uint hash) {
402     if(dryRun)
403         return;
404     if(!set || !plugSet)
405         return;
406     QString section;
407     section.append(QString("dictionary_%1").arg(hash));
408     QList<QString> keys = plugSet->keys();
409     foreach(QString key, keys)
410         set->setValue(section + "/" + key, plugSet->value(key));
411     set->setValue(section + "/active", active);
412 }
413
414
415
416 QStringList Backbone::htmls() {
417     return _htmlResult;
418 }
419
420
421
422 void Backbone::searchHtml(QList<Translation *> translations) {
423     _htmlResult.clear();
424     QList<TranslationPtr> dummy;
425     //_time.restart();
426     foreach(Translation* tr, translations)
427         dummy.append(TranslationPtr(tr));
428
429    _innerHtmlResult = QtConcurrent::mapped(dummy,
430                                             &TranslationPtr::toHtml);
431    _htmlResultWatcher.setFuture(_innerHtmlResult);
432 }
433
434 void Backbone::htmlTranslationReady() {
435     //if(!_innerHtmlResult.isFinished())
436         //return;
437
438     QFutureIterator<QString> it(_innerHtmlResult);
439     while(it.hasNext())
440        _htmlResult.append(it.next());
441
442     //qDebug() << "time " << _time.elapsed();
443     if(_htmlResult.size())
444         Q_EMIT htmlReady();
445
446 }
447
448
449 QList<CommonDictInterface*> Backbone::activeDicts() {
450     QList<CommonDictInterface*>res;
451     foreach(CommonDictInterface* dict, _dicts.keys())
452         if(_dicts[dict])
453             res.append(dict);
454     return res;
455
456 }