1 /***************************************************************************
2 * 2007-2021 by Peter Semiletov *
3 * peter.semiletov@gmail.com *
4 * *
5 * This program 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 * This program 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 this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21
22 #ifdef HUNSPELL_ENABLE
23
24 #ifdef H_DEPRECATED
25 #include <string>
26 #include <vector>
27 #endif
28
29 #include <hunspell/hunspell.hxx>
30
31 #endif
32
33
34 #include <QTextCodec>
35 #include <QDir>
36 #include <QMessageBox>
37 #include <QObject>
38
39 #include "spellchecker.h"
40 #include "utils.h"
41
42
43 #ifdef ASPELL_ENABLE
44
aspell_default_dict_path()45 QString aspell_default_dict_path()
46 {
47 QString r;
48 r = "/usr/lib/aspell-0.60";
49
50 #if defined(Q_OS_OS2)
51
52 r = qEnvironmentVariable ("unixroot", "c:") + "\\usr\\lib\\aspell-0.60";
53
54 #endif
55
56 #if defined(Q_OS_WIN)
57
58 r = "C:\\Program Files\\Aspell";
59
60 #endif
61
62 return r;
63 }
64
65
load_dict()66 void CAspellchecker::load_dict()
67 {
68 loaded = false;
69
70 if (! spell_config)
71 return;
72
73 if (speller)
74 {
75 delete_aspell_speller (speller);
76 speller = 0;
77 }
78
79 aspell_config_replace (spell_config, "lang", language.toUtf8().data());
80 aspell_config_replace (spell_config, "encoding", "UTF-8");
81
82 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
83
84 QString data_path = dir_dicts + "\\data";
85 QString dict_path = dir_dicts + "\\dict";
86
87 data_path = data_path.replace ("/", "\\");
88 dict_path = dict_path.replace ("/", "\\");
89
90 aspell_config_replace (spell_config, "data-dir", data_path.toLatin1().data());
91 aspell_config_replace (spell_config, "dict-dir", dict_path.toLatin1().data());
92
93 #endif
94
95 ret = new_aspell_speller (spell_config);
96 if (! ret)
97 return;
98
99 if (aspell_error (ret) != 0)
100 {
101 //g_print ("Error: %s\n", aspell_error_message (ret));
102 delete_aspell_can_have_error (ret);
103 return;
104 }
105
106 speller = to_aspell_speller (ret);
107
108 loaded = true;
109 }
110
111
CAspellchecker(const QString & lang,const QString & path,const QString & user_path)112 CAspellchecker::CAspellchecker (const QString &lang, const QString &path,
113 const QString &user_path):
114 CSpellchecker (lang, path, user_path)
115 {
116 ret = 0;
117 speller = 0;
118 spell_config = 0;
119
120 spell_config = new_aspell_config();
121 if (! spell_config)
122 return;
123 }
124
125
~CAspellchecker()126 CAspellchecker::~CAspellchecker()
127 {
128 if (speller)
129 delete_aspell_speller (speller);
130
131 if (spell_config)
132 delete_aspell_config (spell_config);
133 }
134
135
add_to_user_dict(const QString & word)136 void CAspellchecker::add_to_user_dict (const QString &word)
137 {
138 if (! loaded || ! speller)
139 return;
140
141 if (word.isEmpty())
142 return;
143
144 QByteArray bw = word.toUtf8();
145
146 aspell_speller_add_to_personal (speller, bw.data(), bw.size());
147 aspell_speller_save_all_word_lists (speller);
148 }
149
150
remove_from_user_dict(const QString & word)151 void CAspellchecker::remove_from_user_dict (const QString &word)
152 {
153 //not implemented at aspell
154 }
155
156
change_lang(const QString & lang)157 void CAspellchecker::change_lang (const QString &lang)
158 {
159 if (lang.isEmpty() || language == lang)
160 return;
161
162 language = lang;
163 }
164
165
get_speller_modules_list()166 void CAspellchecker::get_speller_modules_list()
167 {
168 modules_list.clear();
169
170 if (! spell_config)
171 return;
172
173 AspellDictInfoList *dlist;
174 AspellDictInfoEnumeration *dels;
175 const AspellDictInfo *entry;
176
177 dlist = get_aspell_dict_info_list (spell_config);
178 dels = aspell_dict_info_list_elements (dlist);
179
180 while ((entry = aspell_dict_info_enumeration_next (dels)) != 0)
181 {
182 // if (entry)
183 modules_list.prepend (entry->name);
184 }
185
186 delete_aspell_dict_info_enumeration (dels);
187 }
188
189
check(const QString & word)190 bool CAspellchecker::check (const QString &word)
191 {
192 if (modules_list.size() == 0)
193 {
194 QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page"));
195 return false;
196 }
197
198 if (word.isEmpty())
199 return false;
200
201 if (! loaded)
202 load_dict();
203
204 if (speller)
205 return aspell_speller_check (speller, word.toUtf8().data(), -1);
206 else
207 return false;
208 }
209
210
get_suggestions_list(const QString & word)211 QStringList CAspellchecker::get_suggestions_list (const QString &word)
212 {
213 QStringList l;
214
215 if (! loaded || word.isEmpty() || !speller)
216 return l;
217
218 const AspellWordList *suggestions = aspell_speller_suggest (speller, word.toUtf8().data(), -1);
219
220 if (! suggestions)
221 return l;
222
223 AspellStringEnumeration *elements = aspell_word_list_elements (suggestions);
224 const char *wrd;
225
226 while ((wrd = aspell_string_enumeration_next (elements)))
227 l.prepend (QString::fromUtf8 (wrd));
228
229 delete_aspell_string_enumeration (elements);
230
231 return l;
232 }
233
234 #endif
235
236
237 #ifdef HUNSPELL_ENABLE
238
239
save_user_dict()240 void CHunspellChecker::save_user_dict() //uses current language
241 {
242 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
243 QString filename = dir_user_dicts + QDir::separator() + language + ".dic";
244 filename = filename.replace ("/", "\\");
245 #else
246 QString filename = dir_user_dicts + QDir::separator() + language + ".dic";
247 #endif
248
249 if (user_words.size() > 0)
250 {
251 user_words.prepend (QString::number (user_words.size()));
252 #if ! defined (H_DEPRECATED)
253 qstring_save (filename, user_words.join ("\n"), encoding);
254 #else
255 qstring_save (filename, user_words.join ("\n"), str_encoding.data());
256 #endif
257 }
258 }
259
260
load_dict()261 void CHunspellChecker::load_dict()
262 {
263 loaded = false;
264
265
266 if (! dir_exists (dir_dicts) || language.isEmpty())
267 return;
268
269 if (speller)
270 delete speller;
271
272 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
273
274 QString fname_aff = dir_dicts + QDir::separator() + language + ".aff";
275 QString fname_dict = dir_dicts + QDir::separator() + language + ".dic";
276 QString fname_userdict = dir_user_dicts + QDir::separator() + language + ".dic";
277 QString fname_userdict_pure;
278
279 fname_aff = fname_aff.replace ("/", "\\");
280 fname_dict = fname_dict.replace ("/", "\\");
281 fname_userdict = fname_userdict.replace ("/", "\\");
282 fname_userdict_pure = fname_userdict;
283
284 #else
285
286 QString fname_aff = dir_dicts + QDir::separator() + language + ".aff";
287 QString fname_dict = dir_dicts + QDir::separator() + language + ".dic";
288 QString fname_userdict = dir_user_dicts + QDir::separator() + language + ".dic";
289
290 #endif
291
292 #if defined(Q_OS_WIN)
293
294 fname_aff = "\\\\?\\" + fname_aff;
295 fname_dict = "\\\\?\\" + fname_dict;
296 fname_userdict = "\\\\?\\" + fname_userdict;
297
298 #endif
299
300 speller = new Hunspell (fname_aff.toUtf8().data(), fname_dict.toUtf8().data());
301
302 #if !defined (H_DEPRECATED)
303 encoding = speller->get_dic_encoding();
304 #else
305 str_encoding = speller->get_dict_encoding();
306 #endif
307
308
309 #if defined(Q_OS_UNIX)
310
311 if (file_exists (fname_userdict))
312 {
313 speller->add_dic (fname_userdict.toUtf8().data());
314
315 #if ! defined (H_DEPRECATED)
316 user_words = qstring_load (fname_userdict, encoding).split ("\n");
317 #else
318 user_words = qstring_load (fname_userdict, str_encoding.data()).split ("\n");
319 #endif
320 user_words.removeFirst();
321 }
322 #else //! UNIX
323
324 if (file_exists (fname_userdict_pure))
325 {
326 speller->add_dic (fname_userdict.toUtf8().data());
327 #if ! defined (H_DEPRECATED)
328 user_words = qstring_load (fname_userdict, encoding).split ("\n");
329 #else
330 user_words = qstring_load (fname_userdict, str_encoding.data()).split ("\n");
331 #endif
332 user_words.removeFirst();
333 }
334
335 #endif
336
337 loaded = true;
338
339 }
340
341
CHunspellChecker(const QString & lang,const QString & dir_path,const QString & dir_user)342 CHunspellChecker::CHunspellChecker (const QString &lang, const QString &dir_path, const QString &dir_user): CSpellchecker (lang, dir_path, dir_user)
343 {
344 speller = 0;
345 encoding = 0;
346 }
347
348
~CHunspellChecker()349 CHunspellChecker::~CHunspellChecker()
350 {
351 delete speller;
352 }
353
354
change_lang(const QString & lang)355 void CHunspellChecker::change_lang (const QString &lang)
356 {
357 if (language == lang)
358 return;
359
360 language = lang;
361 save_user_dict();
362 user_words.clear();
363 }
364
365
add_to_user_dict(const QString & word)366 void CHunspellChecker::add_to_user_dict (const QString &word)
367 {
368 if (! loaded || word.isEmpty())
369 return;
370
371 #if !defined (H_DEPRECATED)
372 QTextCodec *codec = QTextCodec::codecForName (encoding);
373 #else
374 QTextCodec *codec = QTextCodec::codecForName (str_encoding.data());
375 #endif
376
377 QByteArray es = codec->fromUnicode (word);
378 speller->add (es.data());
379 user_words.append (word);
380 save_user_dict();
381 }
382
383
check(const QString & word)384 bool CHunspellChecker::check (const QString &word)
385 {
386 if (modules_list.size() == 0)
387 {
388 QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page"));
389 return false;
390 }
391
392 if (! loaded)
393 load_dict();
394
395 #if ! defined (H_DEPRECATED)
396 QTextCodec *codec = QTextCodec::codecForName (encoding);
397 #else
398 QTextCodec *codec = QTextCodec::codecForName (str_encoding.data());
399 #endif
400
401 QByteArray es = codec->fromUnicode (word);
402
403 #ifndef H_DEPRECATED
404 return speller->spell (es.constData());
405 //return speller->spell (es.data()); //old way
406 #else
407 return speller->spell (QString(es).toStdString());
408 #endif
409 }
410
411
remove_from_user_dict(const QString & word)412 void CHunspellChecker::remove_from_user_dict (const QString &word)
413 {
414 if (! loaded || word.isEmpty())
415 return;
416
417 #ifndef H_DEPRECATED
418 QTextCodec *codec = QTextCodec::codecForName (encoding);
419 #else
420 QTextCodec *codec = QTextCodec::codecForName (str_encoding.data());
421 #endif
422
423 QByteArray es = codec->fromUnicode (word);
424 speller->remove (es.data());
425 int i = user_words.indexOf (word);
426 if (i != -1)
427 {
428 user_words.removeAt (i);
429 save_user_dict();
430 }
431 }
432
433
get_speller_modules_list()434 void CHunspellChecker::get_speller_modules_list()
435 {
436 modules_list.clear();
437
438 QDir dir (dir_dicts);
439 if (! dir.exists())
440 return;
441
442 QStringList filters;
443
444 filters << "*.dic";
445
446 dir.setSorting (QDir::Name);
447 QFileInfoList fil = dir.entryInfoList (filters);
448
449 for (int i = 0; i < fil.size(); i++)
450 {
451 modules_list.append (fil[i].baseName());
452 }
453 }
454
455
get_suggestions_list(const QString & word)456 QStringList CHunspellChecker::get_suggestions_list (const QString &word)
457 {
458 QStringList sl;
459
460 if (! loaded || word.isEmpty())
461 return sl;
462
463 #if !defined (H_DEPRECATED)
464 QTextCodec *codec = QTextCodec::codecForName (encoding);
465 #else
466 QTextCodec *codec = QTextCodec::codecForName (str_encoding.data());
467 #endif
468
469 QByteArray es = codec->fromUnicode (word);
470
471 #ifndef H_DEPRECATED
472 char **slst;
473
474 int size = speller->suggest (&slst, es.data());
475
476 for (int i = 0; i < size; i++)
477 sl.append (codec->toUnicode (slst[i]));
478
479 speller->free_list (&slst, size);
480
481 #else
482
483 std::vector<std::string> suglist = speller->suggest (QString(es).toStdString());
484
485 sl.reserve (suglist.size());
486 for (size_t i = 0, sz = suglist.size(); i < sz; ++i)
487 sl.append (QString::fromStdString (suglist[i]));
488
489 #endif
490
491 return sl;
492 }
493
494
hunspell_default_dict_path()495 QString hunspell_default_dict_path()
496 {
497 QString r;
498 r = "/usr/share/hunspell";
499
500 #if defined(Q_OS_OS2)
501 r = qEnvironmentVariable ("unixroot", "c:") + "\\usr\\share\\myspell";
502 #endif
503
504 #if defined(Q_OS_WIN)
505 #endif
506
507 return r;
508 }
509
510 #endif
511