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