]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
prepare Qt 5.6 builds
[lyx.git] / src / insets / InsetCommandParams.cpp
1 /**
2  * \file InsetCommandParams.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Georg Baum
8  * \author Richard Heck
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14 #include <algorithm>
15 #include <functional>
16
17
18 #include "InsetCommandParams.h"
19
20 #include "InsetBibitem.h"
21 #include "InsetBibtex.h"
22 #include "InsetCitation.h"
23 #include "InsetFloatList.h"
24 #include "InsetHyperlink.h"
25 #include "InsetInclude.h"
26 #include "InsetIndex.h"
27 #include "InsetLabel.h"
28 #include "InsetLine.h"
29 #include "InsetNomencl.h"
30 #include "InsetRef.h"
31 #include "InsetTOC.h"
32
33 #include "Buffer.h"
34 #include "Encoding.h"
35 #include "Lexer.h"
36 #include "OutputParams.h"
37
38 #include "frontends/alert.h"
39
40 #include "support/debug.h"
41 #include "support/docstream.h"
42 #include "support/ExceptionMessage.h"
43 #include "support/gettext.h"
44 #include "support/lstrings.h"
45
46 #include "support/lassert.h"
47
48 using namespace std;
49 using namespace lyx::support;
50
51
52 namespace lyx {
53
54 /// Get information for \p code and command \p cmdName.
55 /// Don't call this without first making sure the command name is
56 /// acceptable to the inset.
57 static ParamInfo const & findInfo(InsetCode code, string const & cmdName)
58 {
59         switch (code) {
60         case BIBITEM_CODE:
61                 return InsetBibitem::findInfo(cmdName);
62         case BIBTEX_CODE:
63                 return InsetBibtex::findInfo(cmdName);
64         case CITE_CODE:
65                 return InsetCitation::findInfo(cmdName);
66         case FLOAT_LIST_CODE:
67                 return InsetFloatList::findInfo(cmdName);
68         case HYPERLINK_CODE:
69                 return InsetHyperlink::findInfo(cmdName);
70         case INCLUDE_CODE:
71                 return InsetInclude::findInfo(cmdName);
72         case INDEX_PRINT_CODE:
73                 return InsetPrintIndex::findInfo(cmdName);
74         case LABEL_CODE:
75                 return InsetLabel::findInfo(cmdName);
76         case LINE_CODE:
77                 return InsetLine::findInfo(cmdName);
78         case NOMENCL_CODE:
79                 return InsetNomencl::findInfo(cmdName);
80         case NOMENCL_PRINT_CODE:
81                 return InsetPrintNomencl::findInfo(cmdName);
82         case REF_CODE:
83                 return InsetRef::findInfo(cmdName);
84         case TOC_CODE:
85                 return InsetTOC::findInfo(cmdName);
86         default:
87                 LATTEST(false);
88                 // fall through in release mode
89         }
90         static const ParamInfo pi;
91         return pi;
92 }
93
94
95 /////////////////////////////////////////////////////////////////////
96 //
97 // ParamInfo::ParamData
98 //
99 /////////////////////////////////////////////////////////////////////
100
101 ParamInfo::ParamData::ParamData(std::string const & s, ParamType t,
102                                 ParamHandling h)
103         : name_(s), type_(t), handling_(h)
104 {}
105
106
107 bool ParamInfo::ParamData::isOptional() const
108 {
109         return type_ == ParamInfo::LATEX_OPTIONAL;
110 }
111
112
113 bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const
114 {
115         return name() == rhs.name() && type() == rhs.type()
116                 && handling() == rhs.handling();
117 }
118
119
120 bool ParamInfo::hasParam(std::string const & name) const
121 {
122         const_iterator it = begin();
123         const_iterator last = end();
124         for (; it != last; ++it) {
125                 if (it->name() == name)
126                         return true;
127         }
128         return false;
129 }
130
131
132 void ParamInfo::add(std::string const & name, ParamType type,
133                     ParamHandling handling)
134
135         info_.push_back(ParamData(name, type, handling)); 
136 }
137
138
139 bool ParamInfo::operator==(ParamInfo const & rhs) const
140 {
141         if (size() != rhs.size())
142                 return false;
143         return equal(begin(), end(), rhs.begin());
144 }
145
146
147 ParamInfo::ParamData const & 
148         ParamInfo::operator[](std::string const & name) const
149 {
150         const_iterator it = begin();
151         const_iterator last = end();
152         for (; it != last; ++it) {
153                 if (it->name() == name)
154                         return *it;
155         }
156         LATTEST(false);
157         // we will try to continue in release mode
158         static const ParamData pd("asdfghjkl", LYX_INTERNAL);
159         return pd;
160 }
161
162
163 /////////////////////////////////////////////////////////////////////
164 //
165 // InsetCommandParams
166 //
167 /////////////////////////////////////////////////////////////////////
168
169
170 InsetCommandParams::InsetCommandParams(InsetCode code)
171         : insetCode_(code), preview_(false)
172 {
173         cmdName_ = getDefaultCmd(code);
174         info_ = findInfo(code, cmdName_);
175 }
176
177
178 InsetCommandParams::InsetCommandParams(InsetCode code,
179         string const & cmdName)
180         : insetCode_(code), cmdName_(cmdName), preview_(false)
181 {
182         info_ = findInfo(code, cmdName);
183 }
184
185
186 std::string InsetCommandParams::insetType() const
187 {
188         return insetName(insetCode_);
189 }
190
191
192 string InsetCommandParams::getDefaultCmd(InsetCode code)
193 {
194         switch (code) {
195                 case BIBITEM_CODE: 
196                         return InsetBibitem::defaultCommand();
197                 case BIBTEX_CODE:
198                         return InsetBibtex::defaultCommand();
199                 case CITE_CODE:
200                         return InsetCitation::defaultCommand();
201                 case FLOAT_LIST_CODE:
202                         return InsetFloatList::defaultCommand();
203                 case HYPERLINK_CODE:
204                         return InsetHyperlink::defaultCommand();
205                 case INCLUDE_CODE:
206                         return InsetInclude::defaultCommand();
207                 case INDEX_PRINT_CODE:
208                         return InsetPrintIndex::defaultCommand();
209                 case LABEL_CODE:
210                         return InsetLabel::defaultCommand();
211                 case LINE_CODE:
212                         return InsetLine::defaultCommand();
213                 case NOMENCL_CODE:
214                         return InsetNomencl::defaultCommand();
215                 case NOMENCL_PRINT_CODE:
216                         return InsetPrintNomencl::defaultCommand();
217                 case REF_CODE:
218                         return InsetRef::defaultCommand();
219                 case TOC_CODE:
220                         return InsetTOC::defaultCommand();
221                 default:
222                         LATTEST(false);
223                         // fall through in release mode
224         }
225         return string();
226 }
227
228
229 bool InsetCommandParams::isCompatibleCommand(InsetCode code, string const & s)
230 {
231         switch (code) {
232                 case BIBITEM_CODE: 
233                         return InsetBibitem::isCompatibleCommand(s);
234                 case BIBTEX_CODE:
235                         return InsetBibtex::isCompatibleCommand(s);
236                 case CITE_CODE:
237                         return InsetCitation::isCompatibleCommand(s);
238                 case FLOAT_LIST_CODE:
239                         return InsetFloatList::isCompatibleCommand(s);
240                 case HYPERLINK_CODE:
241                         return InsetHyperlink::isCompatibleCommand(s);
242                 case INCLUDE_CODE:
243                         return InsetInclude::isCompatibleCommand(s);
244                 case INDEX_PRINT_CODE:
245                         return InsetPrintIndex::isCompatibleCommand(s);
246                 case LABEL_CODE:
247                         return InsetLabel::isCompatibleCommand(s);
248                 case LINE_CODE:
249                         return InsetLine::isCompatibleCommand(s);
250                 case NOMENCL_CODE:
251                         return InsetNomencl::isCompatibleCommand(s);
252                 case NOMENCL_PRINT_CODE:
253                         return InsetPrintNomencl::isCompatibleCommand(s);
254                 case REF_CODE:
255                         return InsetRef::isCompatibleCommand(s);
256                 case TOC_CODE:
257                         return InsetTOC::isCompatibleCommand(s);
258         default:
259                 LATTEST(false);
260                 // fall through in release mode
261         }
262         return false;
263 }
264
265
266 void InsetCommandParams::setCmdName(string const & name)
267 {
268         if (!isCompatibleCommand(insetCode_, name)) {
269                 LYXERR0("InsetCommand: Incompatible command name " << 
270                                 name << ".");
271                 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
272                                        _("Incompatible command name."));
273         }
274
275         cmdName_ = name;
276         info_ = findInfo(insetCode_, cmdName_);
277 }
278
279
280 void InsetCommandParams::read(Lexer & lex)
281 {
282         Read(lex, 0);
283 }
284
285
286 void InsetCommandParams::Read(Lexer & lex, Buffer const * buffer)
287 {
288         lex.setContext("InsetCommandParams::read");
289         lex >> insetName(insetCode_).c_str();
290         lex >> "LatexCommand";
291         lex >> cmdName_;
292         if (!isCompatibleCommand(insetCode_, cmdName_)) {
293                 lex.printError("Incompatible command name " + cmdName_ + ".");
294                 throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
295                                        _("Incompatible command name."));
296         }
297
298         info_ = findInfo(insetCode_, cmdName_);
299         
300         string token;
301         while (lex.isOK()) {
302                 lex.next();
303                 token = lex.getString();
304                 if (token == "\\end_inset")
305                         break;
306                 if (token == "preview") {
307                         lex.next();
308                         preview_ = lex.getBool();
309                         continue;
310                 }
311                 if (info_.hasParam(token)) {
312                         lex.next(true);
313                         docstring data = lex.getDocString();
314                         if (buffer && token == "filename") {
315                                 data = from_utf8(buffer->includedFilePath(to_utf8(data)));
316                         } else if (buffer && token == "bibfiles") {
317                                 int i = 0;
318                                 docstring newdata;
319                                 docstring bib = support::token(data, ',', i);
320                                 while (!bib.empty()) {
321                                         bib = from_utf8(buffer->includedFilePath(to_utf8(bib), "bib"));
322                                         if (!newdata.empty())
323                                                 newdata.append(1, ',');
324                                         newdata.append(bib);
325                                         bib = support::token(data, ',', ++i);
326                                 }
327                                 data = newdata;
328                         } else if (buffer && token == "options") {
329                                 data = from_utf8(buffer->includedFilePath(to_utf8(data), "bst"));
330                         }
331                         params_[token] = data;
332                 } else {
333                         lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
334                         throw ExceptionMessage(WarningException,
335                                 _("InsetCommandParams: ") + from_ascii(cmdName_),
336                                 _("Unknown parameter name: ") + from_utf8(token));
337                 }
338         }
339         if (token != "\\end_inset") {
340                 lex.printError("Missing \\end_inset at this point. "
341                                "Read: `$$Token'");
342                 throw ExceptionMessage(WarningException,
343                         _("InsetCommandParams Error: "),
344                         _("Missing \\end_inset at this point: ") + from_utf8(token));
345         }
346 }
347
348
349 void InsetCommandParams::write(ostream & os) const
350 {
351         Write(os, 0);
352 }
353
354
355 void InsetCommandParams::Write(ostream & os, Buffer const * buffer) const
356 {
357         os << "CommandInset " << insetType() << '\n';
358         os << "LatexCommand " << cmdName_ << '\n';
359         if (preview_)
360                 os << "preview true\n";
361         ParamInfo::const_iterator it  = info_.begin();
362         ParamInfo::const_iterator end = info_.end();
363         for (; it != end; ++it) {
364                 string const & name = it->name();
365                 string data = to_utf8((*this)[name]);
366                 if (!data.empty()) {
367                         // Adjust path of files if document was moved
368                         if (buffer && name == "filename") {
369                                 data = buffer->includedFilePath(data);
370                         } else if (buffer && name == "bibfiles") {
371                                 int i = 0;
372                                 string newdata;
373                                 string bib = token(data, ',', i);
374                                 while (!bib.empty()) {
375                                         bib = buffer->includedFilePath(bib, "bib");
376                                         if (!newdata.empty())
377                                                 newdata.append(1, ',');
378                                         newdata.append(bib);
379                                         bib = token(data, ',', ++i);
380                                 }
381                                 data = newdata;
382                         } else if (buffer && name == "options") {
383                                 data = buffer->includedFilePath(data, "bst");
384                         }
385                         os << name << ' '
386                            << Lexer::quoteString(data)
387                            << '\n';
388                 }
389         }
390 }
391
392
393 bool InsetCommandParams::writeEmptyOptional(ParamInfo::const_iterator ci) const
394 {
395         LASSERT(ci->isOptional(), return false);
396
397         ++ci; // we want to start with the next one
398         ParamInfo::const_iterator end = info_.end();
399         for (; ci != end; ++ci) {
400                 switch (ci->type()) {
401                 case ParamInfo::LYX_INTERNAL:
402                         break;
403
404                 case ParamInfo::LATEX_REQUIRED:
405                         return false;
406
407                 case ParamInfo::LATEX_OPTIONAL: {
408                         std::string const & name = ci->name();
409                         docstring const & data = (*this)[name];
410                         if (!data.empty())
411                                 return true;
412                         break;
413                 }
414
415                 } //end switch
416         }
417         return false;
418 }
419
420
421 docstring InsetCommandParams::prepareCommand(OutputParams const & runparams,
422                                              docstring const & command,
423                                              ParamInfo::ParamHandling handling) const
424 {
425         docstring result;
426         switch (handling) {
427         case ParamInfo::HANDLING_LATEXIFY: {
428                 pair<docstring, docstring> command_latexed =
429                         runparams.encoding->latexString(command, runparams.dryrun);
430                 result = command_latexed.first;
431                 if (!command_latexed.second.empty()) {
432                         // issue a warning about omitted characters
433                         // FIXME: should be passed to the error dialog
434                         frontend::Alert::warning(_("Uncodable characters"),
435                                 bformat(_("The following characters that are used in the inset %1$s are not\n"
436                                           "representable in the current encoding and therefore have been omitted:\n%2$s."),
437                                         from_utf8(insetType()), command_latexed.second));
438                 }
439                 break;
440         } 
441         case ParamInfo::HANDLING_ESCAPE:
442                 result = escape(command);
443                 break;
444         case ParamInfo::HANDLING_NONE:
445                 result = command;
446                 break;
447         } // switch
448
449         return result;
450 }
451
452
453 docstring InsetCommandParams::getCommand(OutputParams const & runparams) const
454 {
455         docstring s = '\\' + from_ascii(cmdName_);
456         bool noparam = true;
457         ParamInfo::const_iterator it  = info_.begin();
458         ParamInfo::const_iterator end = info_.end();
459         for (; it != end; ++it) {
460                 std::string const & name = it->name();
461                 switch (it->type()) {
462                 case ParamInfo::LYX_INTERNAL:
463                         break;
464
465                 case ParamInfo::LATEX_REQUIRED: {
466                         docstring const data =
467                                 prepareCommand(runparams, (*this)[name], it->handling());
468                         s += '{' + data + '}';
469                         noparam = false;
470                         break;
471                 }
472                 case ParamInfo::LATEX_OPTIONAL: {
473                         docstring const data =
474                                 prepareCommand(runparams, (*this)[name], it->handling());
475                         if (!data.empty()) {
476                                 s += '[' + data + ']';
477                                 noparam = false;
478                         } else if (writeEmptyOptional(it)) {
479                                         s += "[]";
480                                         noparam = false;
481                         }
482                         break;
483                 } 
484                 } //end switch
485         }
486         if (noparam)
487                 // Make sure that following stuff does not change the
488                 // command name.
489                 s += "{}";
490         return s;
491 }
492
493
494 docstring InsetCommandParams::getFirstNonOptParam() const
495 {
496         ParamInfo::const_iterator it = 
497                 find_if(info_.begin(), info_.end(), 
498                         not1(mem_fun_ref(&ParamInfo::ParamData::isOptional)));
499         LASSERT(it != info_.end(), return docstring());
500         return (*this)[it->name()];
501 }
502
503
504 docstring const & InsetCommandParams::operator[](string const & name) const
505 {
506         static const docstring dummy;
507         LASSERT(info_.hasParam(name), return dummy);
508         ParamMap::const_iterator data = params_.find(name);
509         if (data == params_.end() || data->second.empty())
510                 return dummy;
511         return data->second;
512 }
513
514
515 docstring & InsetCommandParams::operator[](string const & name)
516 {
517         LATTEST(info_.hasParam(name));
518         // this will add the name in release mode
519         return params_[name];
520 }
521
522
523 void InsetCommandParams::clear()
524 {
525         params_.clear();
526 }
527
528
529 bool operator==(InsetCommandParams const & o1, InsetCommandParams const & o2)
530 {
531         return o1.insetCode_ == o2.insetCode_
532                 && o1.cmdName_ == o2.cmdName_
533                 && o1.info_ == o2.info_
534                 && o1.params_ == o2.params_
535                 && o1.preview_ == o2.preview_;
536 }
537
538
539 bool operator!=(InsetCommandParams const & o1, InsetCommandParams const & o2)
540 {
541         return !(o1 == o2);
542 }
543
544
545 } // namespace lyx