]> git.lyx.org Git - features.git/blob - src/Thesaurus.cpp
* Spellchecker dialog:
[features.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 "LyXRC.h"
17
18 #include "support/FileNameList.h"
19 #include "support/debug.h"
20 #include "support/filetools.h"
21 #include "support/gettext.h"
22 #include "support/lstrings.h"
23 #include "support/os.h"
24 #include "support/unicode.h"
25
26 #include "support/mythes/mythes.hxx"
27
28 #include "frontends/alert.h"
29
30 #include <algorithm>
31 #include <cstring>
32
33 using namespace std;
34 using namespace lyx::support;
35 using namespace lyx::support::os;
36
37 namespace lyx {
38
39 namespace {
40
41 string const to_iconv_encoding(docstring const & s, string const & encoding)
42 {
43         std::vector<char> const encoded =
44                 ucs4_to_eightbit(s.data(), s.length(), encoding);
45         return string(encoded.begin(), encoded.end());
46 }
47
48
49 docstring const from_iconv_encoding(string const & s, string const & encoding)
50 {
51         std::vector<char_type> const ucs4 =
52                 eightbit_to_ucs4(s.data(), s.length(), encoding);
53         return docstring(ucs4.begin(), ucs4.end());
54 }
55
56 typedef std::map<docstring, MyThes *> Thesauri;
57
58 } // namespace anon
59
60
61 struct Thesaurus::Private
62 {
63         ~Private()
64         {
65                 for (Thesauri::iterator it = thes_.begin();
66                      it != thes_.end(); ++it) {
67                         delete it->second;
68                 }
69         }
70         ///
71         bool thesaurusAvailable(docstring const & lang) const
72         {
73                 for (Thesauri::const_iterator it = thes_.begin();
74                         it != thes_.end(); ++it) {
75                                 if (it->first == lang)
76                                         if (it->second)
77                                                 return true;
78                 }
79                 return false;
80         }
81
82         ///
83         typedef std::pair<std::string, std::string> ThesFiles;
84         ///
85         ThesFiles getThesaurus(docstring const & lang);
86         /// add a thesaurus to the list
87         bool addThesaurus(docstring const & lang);
88
89         /// the thesauri
90         Thesauri thes_;
91 };
92
93
94 pair<string, string> Thesaurus::Private::getThesaurus(docstring const & lang)
95 {
96         string const thes_path = external_path(lyxrc.thesaurusdir_path);
97         LYXERR(Debug::FILES, "thesaurus path: " << thes_path);
98         if (thes_path.empty())
99                 return make_pair(string(), string());
100
101         if (thesaurusAvailable(lang))
102                 return make_pair(string(), string());
103
104         FileNameList const idx_files = FileName(thes_path).dirList("idx");
105         FileNameList const data_files = FileName(thes_path).dirList("dat");
106         string idx;
107         string data;
108
109         for (FileNameList::const_iterator it = idx_files.begin();
110              it != idx_files.end(); ++it) {
111                 LYXERR(Debug::FILES, "found thesaurus idx file: " << it->onlyFileName());
112                 if (contains(it->onlyFileName(), to_ascii(lang))) {
113                         idx = it->absFilename();
114                         LYXERR(Debug::FILES, "selected thesaurus idx file: " << idx);
115                         break;
116                         }
117                 }
118
119         for (support::FileNameList::const_iterator it = data_files.begin();
120              it != data_files.end(); ++it) {
121                 LYXERR(Debug::FILES, "found thesaurus data file: " << it->onlyFileName());
122                 if (contains(it->onlyFileName(), to_ascii(lang))) {
123                         data = it->absFilename();
124                         LYXERR(Debug::FILES, "selected thesaurus data file: " << data);
125                         break;
126                         }
127                 }
128
129         return make_pair(idx, data);
130 }
131
132
133 bool Thesaurus::Private::addThesaurus(docstring const & lang)
134 {
135         ThesFiles files = getThesaurus(lang);
136         string const idx = files.first;
137         string const data = files.second;
138
139         if (idx.empty() || data.empty())
140                 return false;
141
142         char const * af = idx.c_str();
143         char const * df = data.c_str();
144         thes_[lang] = new MyThes(af, df);
145         return true;
146 }
147
148
149 bool Thesaurus::thesaurusAvailable(docstring const & lang) const
150 {
151         return d->thesaurusAvailable(lang);
152 }
153
154
155 bool Thesaurus::thesaurusInstalled(docstring const & lang) const
156 {
157         pair<string, string> files = d->getThesaurus(lang);
158         return (!files.first.empty() && !files.second.empty());
159 }
160
161
162 Thesaurus::Meanings Thesaurus::lookup(docstring const & t, docstring const & lang)
163 {
164         Meanings meanings;
165         MyThes * mythes = 0;
166
167         if (!d->addThesaurus(lang))
168                 return meanings;
169
170         for (Thesauri::const_iterator it = d->thes_.begin();
171              it != d->thes_.end(); ++it) {
172                 if (it->first == lang) {
173                         mythes = it->second;
174                         break;
175                 }
176         }
177
178         if (!mythes)
179                 return meanings;
180
181         string const encoding = mythes->get_th_encoding();
182         
183         mentry * pmean;
184         string const text = to_iconv_encoding(support::lowercase(t), encoding);
185         int len = strlen(text.c_str());
186         int count = mythes->Lookup(text.c_str(), len, &pmean);
187         if (!count)
188                 return meanings;
189
190         // don't change value of pmean or count
191         // they are needed for the CleanUpAfterLookup routine
192         mentry * pm = pmean;
193         docstring meaning;
194         docstring ret;
195         for (int i = 0; i < count; i++) {
196                 meaning = from_iconv_encoding(string(pm->defn), encoding);
197                 // remove silly item
198                 if (support::prefixIs(meaning, '-'))
199                         meaning = support::ltrim(meaning, "- ");
200                 for (int j = 0; j < pm->count; j++) {
201                         ret = from_iconv_encoding(string(pm->psyns[j]), encoding);
202                 }
203         meanings[meaning].push_back(ret);
204         pm++;
205         }
206         // now clean up all allocated memory
207         mythes->CleanUpAfterLookup(&pmean, count);
208
209         for (Meanings::iterator it = meanings.begin();
210              it != meanings.end(); ++it)
211                 sort(it->second.begin(), it->second.end());
212
213         return meanings;
214 }
215
216
217 Thesaurus::Thesaurus() : d(new Thesaurus::Private)
218 {
219 }
220
221
222 Thesaurus::~Thesaurus()
223 {
224         delete d;
225 }
226
227 // Global instance
228 Thesaurus thesaurus;
229
230
231 } // namespace lyx