]> git.lyx.org Git - lyx.git/blob - src/insets/InsetIndexMacro.cpp
e4dc167ca338d536699d76b0558c6a73de740ec9
[lyx.git] / src / insets / InsetIndexMacro.cpp
1 /**
2  * \file InsetIndexMacro.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Spitzmüller
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetIndexMacro.h"
14
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "Cursor.h"
18 #include "Dimension.h"
19 #include "Encoding.h"
20 #include "ErrorList.h"
21 #include "FontInfo.h"
22 #include "FuncRequest.h"
23 #include "FuncStatus.h"
24 #include "InsetLayout.h"
25 #include "InsetList.h"
26 #include "LaTeX.h"
27 #include "LaTeXFeatures.h"
28 #include "Lexer.h"
29 #include "MetricsInfo.h"
30 #include "xml.h"
31 #include "texstream.h"
32
33 #include "frontends/alert.h"
34
35 #include "support/debug.h"
36 #include "support/docstream.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/Translator.h"
40
41 using namespace std;
42 using namespace lyx::support;
43
44 namespace lyx {
45
46 namespace {
47
48 typedef Translator<string, InsetIndexMacroParams::Type> InsetIndexMacroTranslator;
49 typedef Translator<docstring, InsetIndexMacroParams::Type> InsetIndexMacroTranslatorLoc;
50
51 InsetIndexMacroTranslator const init_insetindexmacrotranslator()
52 {
53         InsetIndexMacroTranslator translator("see", InsetIndexMacroParams::See);
54         translator.addPair("seealso", InsetIndexMacroParams::Seealso);
55         translator.addPair("subentry", InsetIndexMacroParams::Subentry);
56         translator.addPair("sortkey", InsetIndexMacroParams::Sortkey);
57         return translator;
58 }
59
60
61 InsetIndexMacroTranslatorLoc const init_insetindexmacrotranslator_loc()
62 {
63         InsetIndexMacroTranslatorLoc translator(_("See"), InsetIndexMacroParams::See);
64         translator.addPair(_("See also"), InsetIndexMacroParams::Seealso);
65         translator.addPair(_("Subentry"), InsetIndexMacroParams::Subentry);
66         translator.addPair(_("Sort as"), InsetIndexMacroParams::Sortkey);
67         return translator;
68 }
69
70
71 InsetIndexMacroTranslator const & insetindexmacrotranslator()
72 {
73         static InsetIndexMacroTranslator const macrotranslator =
74                         init_insetindexmacrotranslator();
75         return macrotranslator;
76 }
77
78
79 InsetIndexMacroTranslatorLoc const & insetindexmacrotranslator_loc()
80 {
81         static InsetIndexMacroTranslatorLoc const translator =
82                         init_insetindexmacrotranslator_loc();
83         return translator;
84 }
85
86 } // namespace
87
88
89 InsetIndexMacroParams::InsetIndexMacroParams()
90         : type(See)
91 {}
92
93
94 void InsetIndexMacroParams::write(ostream & os) const
95 {
96         string const label = insetindexmacrotranslator().find(type);
97         os << "IndexMacro " << label << "\n";
98 }
99
100
101 void InsetIndexMacroParams::read(Lexer & lex)
102 {
103         string label;
104         lex >> label;
105         if (lex)
106                 type = insetindexmacrotranslator().find(label);
107 }
108
109
110 /////////////////////////////////////////////////////////////////////
111 //
112 // InsetIndexMacro
113 //
114 /////////////////////////////////////////////////////////////////////
115
116 InsetIndexMacro::InsetIndexMacro(Buffer * buf, string const & label)
117         : InsetCollapsible(buf)
118 {
119         setDrawFrame(true);
120         setFrameColor(Color_insetframe);
121         params_.type = insetindexmacrotranslator().find(label);
122 }
123
124
125 InsetIndexMacro::~InsetIndexMacro()
126 {}
127
128
129 docstring InsetIndexMacro::layoutName() const
130 {
131         return from_ascii("IndexMacro:" + insetindexmacrotranslator().find(params_.type));
132 }
133
134 InsetCode InsetIndexMacro::lyxCode() const
135 {
136         return params_.type == InsetIndexMacroParams::Sortkey
137                         ? INDEXMACRO_SORTKEY_CODE
138                         : INDEXMACRO_CODE;
139 }
140
141
142 void InsetIndexMacro::write(ostream & os) const
143 {
144         params_.write(os);
145         InsetCollapsible::write(os);
146 }
147
148
149 void InsetIndexMacro::read(Lexer & lex)
150 {
151         params_.read(lex);
152         InsetCollapsible::read(lex);
153 }
154
155
156 void InsetIndexMacro::getLatex(otexstream & os, OutputParams const & runparams) const
157 {
158         if (params_.type == InsetIndexMacroParams::Subentry) {
159                 if (hasSortKey()) {
160                         getSortkey(os, runparams);
161                         os << "@";
162                 } else {
163                         odocstringstream ourlatex;
164                         otexstream ots(ourlatex);
165                         InsetText::latex(ots, runparams);
166                         odocstringstream ourplain;
167                         InsetText::plaintext(ourplain, runparams);
168                         // These are the LaTeX and plaintext representations
169                         docstring latexstr = ourlatex.str();
170                         docstring plainstr = ourplain.str();
171                         processLatexSorting(os, runparams, latexstr, plainstr);
172                 }
173                 return;
174         }
175
176         if (params_.type == InsetIndexMacroParams::See)
177                 os << "see{";
178         else if (params_.type == InsetIndexMacroParams::Seealso)
179                 os << "seealso{";
180
181         InsetCollapsible::latex(os, runparams);
182
183         if (params_.type == InsetIndexMacroParams::See
184              || params_.type == InsetIndexMacroParams::Seealso)
185                 os << "}";
186 }
187
188
189 int InsetIndexMacro::getPlaintext(odocstringstream & os,
190                             OutputParams const & runparams, size_t max_length) const
191 {
192         return InsetText::plaintext(os, runparams, max_length);
193 }
194
195
196 void InsetIndexMacro::getDocbook(XMLStream & xs, OutputParams const & runparams) const
197 {
198         InsetText::docbook(xs, runparams);
199 }
200
201
202 docstring InsetIndexMacro::getXhtml(XMLStream & xs, OutputParams const & runparams) const
203 {
204         return InsetText::xhtml(xs, runparams);
205 }
206
207
208 bool InsetIndexMacro::hasNoContent() const
209 {
210         return paragraphs().front().empty();
211 }
212
213
214 void InsetIndexMacro::doDispatch(Cursor & cur, FuncRequest & cmd)
215 {
216         switch (cmd.action()) {
217
218         case LFUN_INSET_MODIFY: {
219                 if (cmd.getArg(0) == "changetype") {
220                         cur.recordUndoInset(this);
221                         params_.type = insetindexmacrotranslator().find(cmd.getArg(1));
222                         break;
223                 }
224                 InsetCollapsible::doDispatch(cur, cmd);
225                 break;
226         }
227
228         default:
229                 InsetCollapsible::doDispatch(cur, cmd);
230                 break;
231         }
232 }
233
234
235 bool InsetIndexMacro::getStatus(Cursor & cur, FuncRequest const & cmd,
236                                 FuncStatus & flag) const
237 {
238         switch (cmd.action()) {
239
240         case LFUN_INSET_MODIFY:
241                 if (cmd.getArg(0) == "changetype") {
242                         docstring const newtype = from_utf8(cmd.getArg(1));
243                         bool const enabled = (params_.type == InsetIndexMacroParams::See
244                                               || params_.type == InsetIndexMacroParams::Seealso)
245                                             && (newtype == "see" || newtype == "seealso");
246                         flag.setEnabled(enabled);
247                         flag.setOnOff(
248                                 newtype == from_ascii(insetindexmacrotranslator().find(params_.type)));
249                         return true;
250                 }
251                 return InsetCollapsible::getStatus(cur, cmd, flag);
252
253         default:
254                 return InsetCollapsible::getStatus(cur, cmd, flag);
255         }
256 }
257
258
259 void InsetIndexMacro::processLatexSorting(otexstream & os, OutputParams const & runparams,
260                                 docstring const latex, docstring const plain) const
261 {
262         if (contains(latex, '\\') && !contains(latex, '@')) {
263                 // Plaintext might return nothing (e.g. for ERTs).
264                 // In that case, we use LaTeX.
265                 docstring const spart = (plain.empty()) ? latex : plain;
266                 // Now we need to validate that all characters in
267                 // the sorting part are representable in the current
268                 // encoding. If not try the LaTeX macro which might
269                 // or might not be a good choice, and issue a warning.
270                 pair<docstring, docstring> spart_latexed =
271                                 runparams.encoding->latexString(spart, runparams.dryrun);
272                 if (!spart_latexed.second.empty())
273                         LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
274                 if (spart != spart_latexed.first && !runparams.dryrun) {
275                         TeXErrors terr;
276                         ErrorList & errorList = buffer().errorList("Export");
277                         docstring const s = bformat(_("LyX's automatic index sorting algorithm faced "
278                                                       "problems with the sub-entry '%1$s'.\n"
279                                                       "Please specify the sorting of this entry manually, as "
280                                                       "explained in the User Guide."), spart);
281                         Paragraph const & par = buffer().paragraphs().front();
282                         errorList.push_back(ErrorItem(_("Index sorting failed"), s,
283                                                       {par.id(), 0}, {par.id(), -1}));
284                         buffer().bufferErrors(terr, errorList);
285                 }
286                 // Remove remaining \'s from the sort key
287                 docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
288                 // Plain quotes need to be escaped, however (#10649), as this
289                 // is the default escape character
290                 ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
291
292                 // Now insert the sortkey, separated by '@'.
293                 os << ppart;
294                 os << '@';
295         }
296         // Insert the actual level text
297         os << latex;
298 }
299
300
301 docstring InsetIndexMacro::toolTip(BufferView const &, int, int) const
302 {
303         return insetindexmacrotranslator_loc().find(params_.type);
304 }
305
306
307 string InsetIndexMacro::params2string(InsetIndexMacroParams const & params)
308 {
309         ostringstream data;
310         data << "IndexMacro" << ' ';
311         params.write(data);
312         return data.str();
313 }
314
315
316 void InsetIndexMacro::string2params(string const & in, InsetIndexMacroParams & params)
317 {
318         params = InsetIndexMacroParams();
319
320         if (in.empty())
321                 return;
322
323         istringstream data(in);
324         Lexer lex;
325         lex.setStream(data);
326         lex.setContext("InsetIndexMacro::string2params");
327         lex >> "IndexMacro" >> "see";
328
329         params.read(lex);
330 }
331
332
333 bool InsetIndexMacro::hasSortKey() const
334 {
335         Paragraph const & par = paragraphs().front();
336         InsetList::const_iterator it = par.insetList().begin();
337         for (; it != par.insetList().end(); ++it) {
338                 Inset & inset = *it->inset;
339                 if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
340                         return true;
341         }
342         return false;
343 }
344
345
346 void InsetIndexMacro::getSortkey(otexstream & os, OutputParams const & runparams) const
347 {
348         Paragraph const & par = paragraphs().front();
349         InsetList::const_iterator it = par.insetList().begin();
350         for (; it != par.insetList().end(); ++it) {
351                 Inset & inset = *it->inset;
352                 if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE) {
353                         InsetIndexMacro const & iim =
354                                 static_cast<InsetIndexMacro const &>(inset);
355                         iim.getLatex(os, runparams);
356                         return;
357                 }
358         }
359 }
360
361
362 string InsetIndexMacro::contextMenuName() const
363 {
364         return "context-indexmacro";
365 }
366
367
368 string InsetIndexMacro::contextMenu(BufferView const & bv, int x, int y) const
369 {
370         // We override the implementation of InsetCollapsible,
371         // because we have eytra entries.
372         string owncm = "context-edit-index;";
373         return owncm + InsetCollapsible::contextMenu(bv, x, y);
374 }
375
376
377 bool InsetIndexMacro::insetAllowed(InsetCode code) const
378 {
379         switch (code) {
380         case INDEX_CODE:
381                 return false;
382         case INDEXMACRO_SORTKEY_CODE:
383                 return (params_.type == InsetIndexMacroParams::Subentry
384                         && !hasSortKey());
385         default:
386                 return InsetCollapsible::insetAllowed(code);
387         }
388 }
389
390 } // namespace lyx