]> git.lyx.org Git - lyx.git/blob - src/Thesaurus.cpp
adjust comment.
[lyx.git] / src / Thesaurus.cpp
1 /**
2  * \file Thesaurus.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "Thesaurus.h"
15
16 #include "support/debug.h"
17 #include "support/gettext.h"
18 #include "LyXRC.h"
19
20 #include "support/FileNameList.h"
21 #include "support/filetools.h"
22 #include "support/lstrings.h"
23 #include "support/os.h"
24 #include "support/unicode.h"
25
26 #include "frontends/alert.h"
27
28 #ifdef HAVE_LIBMYTHES
29 #include MYTHES_H_LOCATION
30 #else
31 #ifdef HAVE_LIBAIKSAURUS
32 #include AIKSAURUS_H_LOCATION
33 #endif // HAVE_LIBAIKSAURUS
34 #endif // !HAVE_LIBMYTHES
35
36 #include <algorithm>
37 #include <cstring>
38
39 using namespace std;
40 using namespace lyx::support;
41 using namespace lyx::support::os;
42
43 namespace lyx {
44
45 #ifdef HAVE_LIBMYTHES
46
47 namespace {
48
49 string const to_iconv_encoding(docstring const & s, string const & encoding)
50 {
51         std::vector<char> const encoded =
52                 ucs4_to_eightbit(s.data(), s.length(), encoding);
53         return string(encoded.begin(), encoded.end());
54 }
55
56
57 docstring const from_iconv_encoding(string const & s, string const & encoding)
58 {
59         std::vector<char_type> const ucs4 =
60                 eightbit_to_ucs4(s.data(), s.length(), encoding);
61         return docstring(ucs4.begin(), ucs4.end());
62 }
63
64 typedef std::map<docstring, MyThes *> Thesauri;
65
66 } // namespace anon
67
68
69 struct Thesaurus::Private
70 {
71         ~Private()
72         {
73                 for (Thesauri::iterator it = thes_.begin();
74                      it != thes_.end(); ++it) {
75                         delete it->second;
76                 }
77         }
78         ///
79         bool thesaurusAvailable(docstring const & lang) const
80         {
81                 for (Thesauri::const_iterator it = thes_.begin();
82                         it != thes_.end(); ++it) {
83                                 if (it->first == lang)
84                                         if (it->second)
85                                                 return true;
86                 }
87                 return false;
88         }
89
90         /// add a thesaurus to the list
91         bool addThesaurus(docstring const & lang);
92
93         /// the thesauri
94         Thesauri thes_;
95 };
96
97 bool Thesaurus::Private::addThesaurus(docstring const & lang)
98 {
99         string const thes_path = external_path(lyxrc.thesaurusdir_path);
100         LYXERR(Debug::FILES, "thesaurus path: " << thes_path);
101         if (thes_path.empty())
102                 return false;
103
104         if (thesaurusAvailable(lang))
105                 return true;
106
107         FileNameList const idx_files = FileName(thes_path).dirList("idx");
108         FileNameList const data_files = FileName(thes_path).dirList("dat");
109         string idx;
110         string data;
111
112         for (FileNameList::const_iterator it = idx_files.begin();
113              it != idx_files.end(); ++it) {
114                 LYXERR(Debug::FILES, "found thesaurus idx file: " << it->onlyFileName());
115                 if (contains(it->onlyFileName(), to_ascii(lang))) {
116                         idx = it->absFilename();
117                         LYXERR(Debug::FILES, "selected thesaurus idx file: " << idx);
118                         break;
119                         }
120                 }
121
122         for (support::FileNameList::const_iterator it = data_files.begin();
123              it != data_files.end(); ++it) {
124                 LYXERR(Debug::FILES, "found thesaurus data file: " << it->onlyFileName());
125                 if (contains(it->onlyFileName(), to_ascii(lang))) {
126                         data = it->absFilename();
127                         LYXERR(Debug::FILES, "selected thesaurus data file: " << data);
128                         break;
129                         }
130                 }
131
132         if (idx.empty() || data.empty())
133                 return false;
134
135         char const * af = idx.c_str();
136         char const * df = data.c_str();
137         thes_[lang] = new MyThes(af, df);
138         return true;
139 }
140
141
142 bool Thesaurus::thesaurusAvailable(docstring const & lang) const
143 {
144         return d->thesaurusAvailable(lang);
145 }
146
147
148 Thesaurus::Meanings Thesaurus::lookup(docstring const & t, docstring const & lang)
149 {
150         Meanings meanings;
151         MyThes * mythes = 0;
152
153         if (!d->addThesaurus(lang))
154                 return meanings;
155
156         for (Thesauri::const_iterator it = d->thes_.begin();
157              it != d->thes_.end(); ++it) {
158                 if (it->first == lang) {
159                         mythes = it->second;
160                         break;
161                 }
162         }
163
164         if (!mythes)
165                 return meanings;
166
167         string const encoding = mythes->get_th_encoding();
168         
169         mentry * pmean;
170         string const text = to_iconv_encoding(support::lowercase(t), encoding);
171         int len = strlen(text.c_str());
172         int count = mythes->Lookup(text.c_str(), len, &pmean);
173         if (!count)
174                 return meanings;
175
176         // don't change value of pmean or count
177         // they are needed for the CleanUpAfterLookup routine
178         mentry * pm = pmean;
179         docstring meaning;
180         docstring ret;
181         for (int i = 0; i < count; i++) {
182                 meaning = from_iconv_encoding(string(pm->defn), encoding);
183                 // remove silly item
184                 if (support::prefixIs(meaning, '-'))
185                         meaning = support::ltrim(meaning, "- ");
186                 for (int j = 0; j < pm->count; j++) {
187                         ret = from_iconv_encoding(string(pm->psyns[j]), encoding);
188                 }
189         meanings[meaning].push_back(ret);
190         pm++;
191         }
192         // now clean up all allocated memory
193         mythes->CleanUpAfterLookup(&pmean, count);
194
195         for (Meanings::iterator it = meanings.begin();
196              it != meanings.end(); ++it)
197                 sort(it->second.begin(), it->second.end());
198
199         return meanings;
200 }
201
202 #else // HAVE_LIBMYTHES
203 #ifdef HAVE_LIBAIKSAURUS
204
205 struct Thesaurus::Private
206 {
207         Private(): thes_(new Aiksaurus) {}
208         Aiksaurus * thes_;
209 };
210
211 Thesaurus::Meanings Thesaurus::lookup(docstring const & t, docstring const &)
212 {
213         Meanings meanings;
214
215         // aiksaurus is for english text only, therefore it does not work
216         // with non-ascii strings.
217         // The interface of the Thesaurus class uses docstring because a
218         // non-english thesaurus is possible in theory.
219         if (!support::isAscii(t))
220                 // to_ascii() would assert
221                 return meanings;
222
223         string const text = to_ascii(t);
224
225         docstring error = from_ascii(d->thes_->error());
226         if (!error.empty()) {
227                 static bool sent_error = false;
228                 if (!sent_error) {
229                         frontend::Alert::error(_("Thesaurus failure"),
230                                      bformat(_("Aiksaurus returned the following error:\n\n%1$s."),
231                                              error));
232                         sent_error = true;
233                 }
234                 return meanings;
235         }
236         if (!d->thes_->find(text.c_str()))
237                 return meanings;
238
239         // weird api, but ...
240
241         int prev_meaning = -1;
242         int cur_meaning;
243         docstring meaning;
244
245         // correct, returns "" at the end
246         string ret = d->thes_->next(cur_meaning);
247
248         while (!ret.empty()) {
249                 if (cur_meaning != prev_meaning) {
250                         meaning = from_ascii(ret);
251                         ret = d->thes_->next(cur_meaning);
252                         prev_meaning = cur_meaning;
253                 } else {
254                         if (ret != text)
255                                 meanings[meaning].push_back(from_ascii(ret));
256                 }
257
258                 ret = d->thes_->next(cur_meaning);
259         }
260
261         for (Meanings::iterator it = meanings.begin();
262              it != meanings.end(); ++it)
263                 sort(it->second.begin(), it->second.end());
264
265         return meanings;
266 }
267
268
269 bool Thesaurus::thesaurusAvailable(docstring const & lang) const
270 {
271         // we support English only
272         return prefixIs(lang, from_ascii("en_"));
273 }
274
275 #else // HAVE_LIBAIKSAURUS
276
277 struct Thesaurus::Private
278 {
279 };
280
281
282 Thesaurus::Meanings Thesaurus::lookup(docstring const &, docstring const &)
283 {
284         return Meanings();
285 }
286
287
288 bool Thesaurus::thesaurusAvailable(docstring const &) const
289 {
290         return false;
291 }
292
293 #endif // HAVE_LIBAIKSAURUS
294 #endif // HAVE_LIBMYTHES
295
296 Thesaurus::Thesaurus() : d(new Thesaurus::Private)
297 {
298 }
299
300
301 Thesaurus::~Thesaurus()
302 {
303         delete d;
304 }
305
306 // Global instance
307 Thesaurus thesaurus;
308
309
310 } // namespace lyx