]> git.lyx.org Git - lyx.git/blob - src/insets/InsetIndexMacro.cpp
Merge branch 'features/indexmacros'
[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 void InsetIndexMacro::doDispatch(Cursor & cur, FuncRequest & cmd)
209 {
210         switch (cmd.action()) {
211
212         case LFUN_INSET_MODIFY: {
213                 if (cmd.getArg(0) == "changetype") {
214                         cur.recordUndoInset(this);
215                         params_.type = insetindexmacrotranslator().find(cmd.getArg(1));
216                         break;
217                 }
218                 InsetCollapsible::doDispatch(cur, cmd);
219                 break;
220         }
221
222         default:
223                 InsetCollapsible::doDispatch(cur, cmd);
224                 break;
225         }
226 }
227
228
229 bool InsetIndexMacro::getStatus(Cursor & cur, FuncRequest const & cmd,
230                                 FuncStatus & flag) const
231 {
232         switch (cmd.action()) {
233
234         case LFUN_INSET_MODIFY:
235                 if (cmd.getArg(0) == "changetype") {
236                         docstring const newtype = from_utf8(cmd.getArg(1));
237                         bool const enabled = (params_.type == InsetIndexMacroParams::See
238                                               || params_.type == InsetIndexMacroParams::Seealso)
239                                             && (newtype == "see" || newtype == "seealso");
240                         flag.setEnabled(enabled);
241                         flag.setOnOff(
242                                 newtype == from_ascii(insetindexmacrotranslator().find(params_.type)));
243                         return true;
244                 }
245                 return InsetCollapsible::getStatus(cur, cmd, flag);
246
247         default:
248                 return InsetCollapsible::getStatus(cur, cmd, flag);
249         }
250 }
251
252
253 void InsetIndexMacro::processLatexSorting(otexstream & os, OutputParams const & runparams,
254                                 docstring const latex, docstring const plain) const
255 {
256         if (contains(latex, '\\') && !contains(latex, '@')) {
257                 // Plaintext might return nothing (e.g. for ERTs).
258                 // In that case, we use LaTeX.
259                 docstring const spart = (plain.empty()) ? latex : plain;
260                 // Now we need to validate that all characters in
261                 // the sorting part are representable in the current
262                 // encoding. If not try the LaTeX macro which might
263                 // or might not be a good choice, and issue a warning.
264                 pair<docstring, docstring> spart_latexed =
265                                 runparams.encoding->latexString(spart, runparams.dryrun);
266                 if (!spart_latexed.second.empty())
267                         LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
268                 if (spart != spart_latexed.first && !runparams.dryrun) {
269                         TeXErrors terr;
270                         ErrorList & errorList = buffer().errorList("Export");
271                         docstring const s = bformat(_("LyX's automatic index sorting algorithm faced "
272                                                       "problems with the sub-entry '%1$s'.\n"
273                                                       "Please specify the sorting of this entry manually, as "
274                                                       "explained in the User Guide."), spart);
275                         Paragraph const & par = buffer().paragraphs().front();
276                         errorList.push_back(ErrorItem(_("Index sorting failed"), s,
277                                                       {par.id(), 0}, {par.id(), -1}));
278                         buffer().bufferErrors(terr, errorList);
279                 }
280                 // Remove remaining \'s from the sort key
281                 docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
282                 // Plain quotes need to be escaped, however (#10649), as this
283                 // is the default escape character
284                 ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
285
286                 // Now insert the sortkey, separated by '@'.
287                 os << ppart;
288                 os << '@';
289         }
290         // Insert the actual level text
291         os << latex;
292 }
293
294
295 docstring InsetIndexMacro::toolTip(BufferView const &, int, int) const
296 {
297         return insetindexmacrotranslator_loc().find(params_.type);
298 }
299
300
301 string InsetIndexMacro::params2string(InsetIndexMacroParams const & params)
302 {
303         ostringstream data;
304         data << "IndexMacro" << ' ';
305         params.write(data);
306         return data.str();
307 }
308
309
310 void InsetIndexMacro::string2params(string const & in, InsetIndexMacroParams & params)
311 {
312         params = InsetIndexMacroParams();
313
314         if (in.empty())
315                 return;
316
317         istringstream data(in);
318         Lexer lex;
319         lex.setStream(data);
320         lex.setContext("InsetIndexMacro::string2params");
321         lex >> "IndexMacro" >> "see";
322
323         params.read(lex);
324 }
325
326
327 bool InsetIndexMacro::hasSortKey() const
328 {
329         Paragraph const & par = paragraphs().front();
330         InsetList::const_iterator it = par.insetList().begin();
331         for (; it != par.insetList().end(); ++it) {
332                 Inset & inset = *it->inset;
333                 if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
334                         return true;
335         }
336         return false;
337 }
338
339
340 void InsetIndexMacro::getSortkey(otexstream & os, OutputParams const & runparams) const
341 {
342         Paragraph const & par = paragraphs().front();
343         InsetList::const_iterator it = par.insetList().begin();
344         for (; it != par.insetList().end(); ++it) {
345                 Inset & inset = *it->inset;
346                 if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE) {
347                         InsetIndexMacro const & iim =
348                                 static_cast<InsetIndexMacro const &>(inset);
349                         iim.getLatex(os, runparams);
350                         return;
351                 }
352         }
353 }
354
355
356 string InsetIndexMacro::contextMenuName() const
357 {
358         return "context-indexmacro";
359 }
360
361
362 string InsetIndexMacro::contextMenu(BufferView const & bv, int x, int y) const
363 {
364         // We override the implementation of InsetCollapsible,
365         // because we have eytra entries.
366         string owncm = "context-edit-index;";
367         return owncm + InsetCollapsible::contextMenu(bv, x, y);
368 }
369
370
371 bool InsetIndexMacro::insetAllowed(InsetCode code) const
372 {
373         switch (code) {
374         case INDEX_CODE:
375                 return false;
376         case INDEXMACRO_SORTKEY_CODE:
377                 return (params_.type == InsetIndexMacroParams::Subentry
378                         && !hasSortKey());
379         default:
380                 return InsetCollapsible::insetAllowed(code);
381         }
382 }
383
384 } // namespace lyx