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