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