]> git.lyx.org Git - features.git/blob - src/insets/InsetIndex.cpp
Fix bug 6179 ("|" in index entries not embraced in ERT by lyx2lyx).
[features.git] / src / insets / InsetIndex.cpp
1 /**
2  * \file InsetIndex.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 #include <config.h>
12
13 #include "InsetIndex.h"
14
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "BufferView.h"
18 #include "ColorSet.h"
19 #include "DispatchResult.h"
20 #include "Encoding.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
23 #include "IndicesList.h"
24 #include "LaTeXFeatures.h"
25 #include "Lexer.h"
26 #include "MetricsInfo.h"
27 #include "sgml.h"
28 #include "TocBackend.h"
29
30 #include "support/debug.h"
31 #include "support/docstream.h"
32 #include "support/gettext.h"
33 #include "support/lstrings.h"
34
35 #include "frontends/alert.h"
36
37 #include <ostream>
38
39 using namespace std;
40 using namespace lyx::support;
41
42 namespace lyx {
43
44 /////////////////////////////////////////////////////////////////////
45 //
46 // InsetIndex
47 //
48 ///////////////////////////////////////////////////////////////////////
49
50
51 InsetIndex::InsetIndex(Buffer const & buf, InsetIndexParams const & params)
52         : InsetCollapsable(buf), params_(params)
53 {}
54
55
56 int InsetIndex::latex(odocstream & os,
57                       OutputParams const & runparams_in) const
58 {
59         OutputParams runparams(runparams_in);
60         runparams.inIndexEntry = true;
61
62         if (buffer().masterBuffer()->params().use_indices && !params_.index.empty()
63             && params_.index != "idx") {
64                 os << "\\sindex[";
65                 os << params_.index;
66                 os << "]{";
67         } else {
68                 os << "\\index";
69                 os << '{';
70         }
71         int i = 0;
72
73         // get contents of InsetText as LaTeX and plaintext
74         odocstringstream ourlatex;
75         InsetText::latex(ourlatex, runparams);
76         odocstringstream ourplain;
77         InsetText::plaintext(ourplain, runparams);
78         docstring latexstr = ourlatex.str();
79         docstring plainstr = ourplain.str();
80
81         // this will get what follows | if anything does
82         docstring cmd;
83
84         // check for the | separator
85         // FIXME This would go wrong on an escaped "|", but
86         // how far do we want to go here?
87         size_t pos = latexstr.find(from_ascii("|"));
88         if (pos != docstring::npos) {
89                 // put the bit after "|" into cmd...
90                 cmd = latexstr.substr(pos + 1);
91                 // ...and erase that stuff from latexstr
92                 latexstr = latexstr.erase(pos);
93                 // ...and similarly from plainstr
94                 size_t ppos = plainstr.find(from_ascii("|"));
95                 if (ppos < plainstr.size())
96                         plainstr.erase(ppos);
97                 else
98                         LYXERR0("The `|' separator was not found in the plaintext version!");
99         }
100
101         // Separate the entires and subentries, i.e., split on "!"
102         // FIXME This would do the wrong thing with escaped ! characters
103         std::vector<docstring> const levels =
104                 getVectorFromString(latexstr, from_ascii("!"), true);
105         std::vector<docstring> const levels_plain =
106                 getVectorFromString(plainstr, from_ascii("!"), true);
107
108         vector<docstring>::const_iterator it = levels.begin();
109         vector<docstring>::const_iterator end = levels.end();
110         vector<docstring>::const_iterator it2 = levels_plain.begin();
111         bool first = true;
112         for (; it != end; ++it) {
113                 // write the separator except the first time
114                 if (!first)
115                         os << '!';
116                 else
117                         first = false;
118
119                 // correctly sort macros and formatted strings
120                 // if we do find a command, prepend a plain text
121                 // version of the content to get sorting right,
122                 // e.g. \index{LyX@\LyX}, \index{text@\textbf{text}}
123                 // Don't do that if the user entered '@' himself, though.
124                 if (contains(*it, '\\') && !contains(*it, '@')) {
125                         // Plaintext might return nothing (e.g. for ERTs)
126                         docstring const spart = 
127                                 (it2 < levels_plain.end() && !(*it2).empty())
128                                 ? *it2 : *it;
129                         // Now we need to validate that all characters in
130                         // the sorting part are representable in the current
131                         // encoding. If not try the LaTeX macro which might
132                         // or might not be a good choice, and issue a warning.
133                         docstring spart2;
134                         for (size_t n = 0; n < spart.size(); ++n) {
135                                 try {
136                                         spart2 += runparams.encoding->latexChar(spart[n]);
137                                 } catch (EncodingException & /* e */) {
138                                         LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
139                                 }
140                         }
141                         if (spart != spart2 && !runparams.dryrun) {
142                                 // FIXME: warning should be passed to the error dialog
143                                 frontend::Alert::warning(_("Index sorting failed"),
144                                 bformat(_("LyX's automatic index sorting algorithm faced\n"
145                                   "problems with the entry '%1$s'.\n"
146                                   "Please specify the sorting of this entry manually, as\n"
147                                   "explained in the User Guide."), spart));
148                         }
149                         // remove remaining \'s for the sorting part
150                         docstring const ppart =
151                                 subst(spart2, from_ascii("\\"), docstring());
152                         os << ppart;
153                         os << '@';
154                 }
155                 docstring const tpart = *it;
156                 os << tpart;
157                 if (it2 < levels_plain.end())
158                         ++it2;
159         }
160         // write the bit that followed "|"
161         if (!cmd.empty())
162                 os << "|" << cmd;
163         os << '}';
164         return i;
165 }
166
167
168 int InsetIndex::docbook(odocstream & os, OutputParams const & runparams) const
169 {
170         os << "<indexterm><primary>";
171         int const i = InsetText::docbook(os, runparams);
172         os << "</primary></indexterm>";
173         return i;
174 }
175
176
177 docstring InsetIndex::xhtml(odocstream &, OutputParams const &) const
178 {
179         return docstring();
180 }
181
182
183 bool InsetIndex::showInsetDialog(BufferView * bv) const
184 {
185         bv->showDialog("index", params2string(params_),
186                         const_cast<InsetIndex *>(this));
187         return true;
188 }
189
190
191 void InsetIndex::doDispatch(Cursor & cur, FuncRequest & cmd)
192 {
193         switch (cmd.action) {
194
195         case LFUN_INSET_MODIFY: {
196                 if (cmd.getArg(0) == "changetype") {
197                         params_.index = from_utf8(cmd.getArg(1));
198                         break;
199                 }
200                 InsetIndexParams params;
201                 InsetIndex::string2params(to_utf8(cmd.argument()), params);
202                 params_.index = params.index;
203                 break;
204         }
205
206         case LFUN_INSET_DIALOG_UPDATE:
207                 cur.bv().updateDialog("index", params2string(params_));
208                 break;
209
210         default:
211                 InsetCollapsable::doDispatch(cur, cmd);
212                 break;
213         }
214 }
215
216
217 bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
218                 FuncStatus & flag) const
219 {
220         switch (cmd.action) {
221
222         case LFUN_INSET_MODIFY:
223                 if (cmd.getArg(0) == "changetype") {
224                         docstring const newtype = from_utf8(cmd.getArg(1));
225                         Buffer const & realbuffer = *buffer().masterBuffer();
226                         IndicesList const & indiceslist = realbuffer.params().indiceslist();
227                         Index const * index = indiceslist.findShortcut(newtype);
228                         flag.setEnabled(index != 0);
229                         flag.setOnOff(
230                                 from_utf8(cmd.getArg(1)) == params_.index);
231                         return true;
232                 }
233                 flag.setEnabled(true);
234                 return true;
235
236         case LFUN_INSET_DIALOG_UPDATE: {
237                 Buffer const & realbuffer = *buffer().masterBuffer();
238                 flag.setEnabled(realbuffer.params().use_indices);
239                 return true;
240         }
241
242         default:
243                 return InsetCollapsable::getStatus(cur, cmd, flag);
244         }
245 }
246
247
248 docstring const InsetIndex::buttonLabel(BufferView const & bv) const
249 {
250         docstring s = _("Idx");
251         if (decoration() == InsetLayout::CLASSIC)
252                 return isOpen(bv) ? s : getNewLabel(s);
253         else
254                 return getNewLabel(s);
255 }
256
257
258 ColorCode InsetIndex::labelColor() const
259 {
260         if (params_.index.empty() || params_.index == from_ascii("idx"))
261                 return InsetCollapsable::labelColor();
262         // FIXME UNICODE
263         ColorCode c = lcolor.getFromLyXName(to_utf8(params_.index));
264         if (c == Color_none)
265                 c = InsetCollapsable::labelColor();
266         return c;
267 }
268
269
270 docstring InsetIndex::toolTip(BufferView const &, int, int) const
271 {
272         docstring tip = _("Index Entry");
273         if (buffer().params().use_indices && !params_.index.empty()) {
274                 Buffer const & realbuffer = *buffer().masterBuffer();
275                 IndicesList const & indiceslist = realbuffer.params().indiceslist();
276                 tip += " (";
277                 Index const * index = indiceslist.findShortcut(params_.index);
278                 if (!index)
279                         tip += _("unknown type!");
280                 else
281                         tip += index->index();
282                 tip += ")";
283         }
284         tip += ": ";
285         OutputParams rp(&buffer().params().encoding());
286         odocstringstream ods;
287         InsetText::plaintext(ods, rp);
288         tip += ods.str();
289         return wrapParas(tip);
290 }
291
292
293 void InsetIndex::write(ostream & os) const
294 {
295         os << to_utf8(name());
296         params_.write(os);
297         InsetCollapsable::write(os);
298 }
299
300
301 void InsetIndex::read(Lexer & lex)
302 {
303         params_.read(lex);
304         InsetCollapsable::read(lex);
305 }
306
307
308 string InsetIndex::params2string(InsetIndexParams const & params)
309 {
310         ostringstream data;
311         data << "index";
312         params.write(data);
313         return data.str();
314 }
315
316
317 void InsetIndex::string2params(string const & in, InsetIndexParams & params)
318 {
319         params = InsetIndexParams();
320         if (in.empty())
321                 return;
322
323         istringstream data(in);
324         Lexer lex;
325         lex.setStream(data);
326         lex.setContext("InsetIndex::string2params");
327         lex >> "index";
328         params.read(lex);
329 }
330
331
332 void InsetIndex::addToToc(DocIterator const & cpit)
333 {
334         DocIterator pit = cpit;
335         pit.push_back(CursorSlice(*this));
336         docstring const item = text().asString(0, 1, AS_STR_LABEL | AS_STR_INSETS);
337         buffer().tocBackend().toc("index").push_back(TocItem(pit, 0, item));
338         // Proceed with the rest of the inset.
339         InsetCollapsable::addToToc(cpit);
340 }
341
342
343 void InsetIndex::validate(LaTeXFeatures & features) const
344 {
345         if (buffer().masterBuffer()->params().use_indices
346             && !params_.index.empty()
347             && params_.index != "idx")
348                 features.require("splitidx");
349 }
350
351
352 docstring InsetIndex::contextMenu(BufferView const &, int, int) const
353 {
354         return from_ascii("context-index");
355 }
356
357
358 bool InsetIndex::hasSettings() const
359 {
360         return buffer().masterBuffer()->params().use_indices;
361 }
362
363
364
365
366 /////////////////////////////////////////////////////////////////////
367 //
368 // InsetIndexParams
369 //
370 ///////////////////////////////////////////////////////////////////////
371
372
373 void InsetIndexParams::write(ostream & os) const
374 {
375         os << ' ';
376         if (!index.empty())
377                 os << to_utf8(index);
378         else
379                 os << "idx";
380         os << '\n';
381 }
382
383
384 void InsetIndexParams::read(Lexer & lex)
385 {
386         if (lex.eatLine())
387                 index = lex.getDocString();
388         else
389                 index = from_ascii("idx");
390 }
391
392
393 /////////////////////////////////////////////////////////////////////
394 //
395 // InsetPrintIndex
396 //
397 ///////////////////////////////////////////////////////////////////////
398
399 InsetPrintIndex::InsetPrintIndex(InsetCommandParams const & p)
400         : InsetCommand(p, "index_print")
401 {}
402
403
404 ParamInfo const & InsetPrintIndex::findInfo(string const & /* cmdName */)
405 {
406         static ParamInfo param_info_;
407         if (param_info_.empty()) {
408                 param_info_.add("type", ParamInfo::LATEX_OPTIONAL);
409                 param_info_.add("name", ParamInfo::LATEX_REQUIRED);
410         }
411         return param_info_;
412 }
413
414
415 docstring InsetPrintIndex::screenLabel() const
416 {
417         bool const printall = suffixIs(getCmdName(), '*');
418         bool const multind = buffer().masterBuffer()->params().use_indices;
419         if ((!multind
420              && getParam("type") == from_ascii("idx"))
421             || (getParam("type").empty() && !printall))
422                 return _("Index");
423         Buffer const & realbuffer = *buffer().masterBuffer();
424         IndicesList const & indiceslist = realbuffer.params().indiceslist();
425         Index const * index = indiceslist.findShortcut(getParam("type"));
426         if (!index && !printall)
427                 return _("Unknown index type!");
428         docstring res = printall ? _("All indices") : index->index();
429         if (!multind)
430                 res += " (" + _("non-active") + ")";
431         else if (contains(getCmdName(), "printsubindex"))
432                 res += " (" + _("subindex") + ")";
433         return res;
434 }
435
436
437 bool InsetPrintIndex::isCompatibleCommand(string const & s)
438 {
439         return s == "printindex" || s == "printsubindex"
440                 || s == "printindex*" || s == "printsubindex*";
441 }
442
443
444 void InsetPrintIndex::doDispatch(Cursor & cur, FuncRequest & cmd)
445 {
446         switch (cmd.action) {
447
448         case LFUN_INSET_MODIFY: {
449                 if (cmd.argument() == from_ascii("toggle-subindex")) {
450                         string cmd = getCmdName();
451                         if (contains(cmd, "printindex"))
452                                 cmd = subst(cmd, "printindex", "printsubindex");
453                         else
454                                 cmd = subst(cmd, "printsubindex", "printindex");
455                         setCmdName(cmd);
456                         break;
457                 } else if (cmd.argument() == from_ascii("check-printindex*")) {
458                         string cmd = getCmdName();
459                         if (suffixIs(cmd, '*'))
460                                 break;
461                         cmd += '*';
462                         setParam("type", docstring());
463                         setCmdName(cmd);
464                         break;
465                 }
466                 InsetCommandParams p(INDEX_PRINT_CODE);
467                 // FIXME UNICODE
468                 InsetCommand::string2params("index_print",
469                         to_utf8(cmd.argument()), p);
470                 if (p.getCmdName().empty()) {
471                         cur.noUpdate();
472                         break;
473                 }
474                 setParams(p);
475                 break;
476         }
477
478         default:
479                 InsetCommand::doDispatch(cur, cmd);
480                 break;
481         }
482 }
483
484
485 bool InsetPrintIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
486         FuncStatus & status) const
487 {
488         switch (cmd.action) {
489
490         case LFUN_INSET_MODIFY: {
491                 if (cmd.argument() == from_ascii("toggle-subindex")) {
492                         status.setEnabled(buffer().masterBuffer()->params().use_indices);
493                         status.setOnOff(contains(getCmdName(), "printsubindex"));
494                         return true;
495                 } else if (cmd.argument() == from_ascii("check-printindex*")) {
496                         status.setEnabled(buffer().masterBuffer()->params().use_indices);
497                         status.setOnOff(suffixIs(getCmdName(), '*'));
498                         return true;
499                 } if (cmd.getArg(0) == "index_print"
500                     && cmd.getArg(1) == "CommandInset") {
501                         InsetCommandParams p(INDEX_PRINT_CODE);
502                         InsetCommand::string2params("index_print",
503                                 to_utf8(cmd.argument()), p);
504                         if (suffixIs(p.getCmdName(), '*')) {
505                                 status.setEnabled(true);
506                                 status.setOnOff(false);
507                                 return true;
508                         }
509                         Buffer const & realbuffer = *buffer().masterBuffer();
510                         IndicesList const & indiceslist =
511                                 realbuffer.params().indiceslist();
512                         Index const * index = indiceslist.findShortcut(p["type"]);
513                         status.setEnabled(index != 0);
514                         status.setOnOff(p["type"] == getParam("type"));
515                         return true;
516                 } else
517                         return InsetCommand::getStatus(cur, cmd, status);
518         }
519         
520         case LFUN_INSET_DIALOG_UPDATE: {
521                 status.setEnabled(buffer().masterBuffer()->params().use_indices);
522                 return true;
523         }
524
525         default:
526                 return InsetCommand::getStatus(cur, cmd, status);
527         }
528 }
529
530
531 int InsetPrintIndex::latex(odocstream & os, OutputParams const &) const
532 {
533         if (!buffer().masterBuffer()->params().use_indices) {
534                 if (getParam("type") == from_ascii("idx"))
535                         os << "\\printindex{}";
536                 return 0;
537         }
538         os << getCommand();
539         return 0;
540 }
541
542
543 void InsetPrintIndex::validate(LaTeXFeatures & features) const
544 {
545         features.require("makeidx");
546         if (buffer().masterBuffer()->params().use_indices)
547                 features.require("splitidx");
548 }
549
550
551 docstring InsetPrintIndex::contextMenu(BufferView const &, int, int) const
552 {
553         return buffer().masterBuffer()->params().use_indices ?
554                 from_ascii("context-indexprint") : docstring();
555 }
556
557
558 bool InsetPrintIndex::hasSettings() const
559 {
560         return buffer().masterBuffer()->params().use_indices;
561 }
562
563 docstring InsetPrintIndex::xhtml(odocstream &, OutputParams const &) const
564 {
565         return docstring();
566 }
567
568 } // namespace lyx