]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
2112005137ce9f53af55c2dd3e1af13a11918127
[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
15 #include "InsetCommandParams.h"
16
17 #include "InsetBibitem.h"
18 #include "InsetBibtex.h"
19 #include "InsetCitation.h"
20 #include "InsetFloatList.h"
21 #include "InsetHyperlink.h"
22 #include "InsetInclude.h"
23 #include "InsetIndex.h"
24 #include "InsetLabel.h"
25 #include "InsetNomencl.h"
26 #include "InsetRef.h"
27 #include "InsetTOC.h"
28
29 #include "Lexer.h"
30
31 #include "support/debug.h"
32 #include "support/docstream.h"
33 #include "support/ExceptionMessage.h"
34 #include "support/gettext.h"
35 #include "support/lstrings.h"
36
37 #include <boost/assert.hpp>
38
39 using namespace std;
40 using namespace lyx::support;
41
42
43 namespace lyx {
44
45 /////////////////////////////////////////////////////////////////////
46 //
47 // ParamInfo::ParamData
48 //
49 /////////////////////////////////////////////////////////////////////
50
51 ParamInfo::ParamData::ParamData(std::string const & s, ParamType t)
52         : name_(s), type_(t)
53 {}
54
55
56 bool ParamInfo::ParamData::isOptional() const
57 {
58         return type_ == ParamInfo::LATEX_OPTIONAL
59             || type_ == ParamInfo::LATEX_KV_OPTIONAL;
60 }
61
62
63 bool ParamInfo::ParamData::isKeyValArg() const
64 {
65         return type_ == ParamInfo::LATEX_KV_REQUIRED 
66             || type_ == ParamInfo::LATEX_KV_OPTIONAL;
67 }
68
69
70 bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const
71 {
72         return name() == rhs.name() && type() == rhs.type();
73 }
74
75
76 bool ParamInfo::hasParam(std::string const & name) const
77 {
78         const_iterator it = begin();
79         const_iterator last = end();
80         for (; it != last; ++it) {
81                 if (it->name() == name)
82                         return !it->isKeyValArg();
83         }
84         return false;
85 }
86
87
88 void ParamInfo::add(std::string const & name, ParamType type)
89
90         info_.push_back(ParamData(name, type)); 
91 }
92
93
94 bool ParamInfo::operator==(ParamInfo const & rhs) const
95 {
96         if (size() != rhs.size())
97                 return false;
98         return equal(begin(), end(), rhs.begin());
99 }
100
101
102 ParamInfo::ParamData const & 
103         ParamInfo::operator[](std::string const & name) const
104 {
105         BOOST_ASSERT(hasParam(name));
106         const_iterator it = begin();
107         const_iterator last = end();
108         for (; it != last; ++it) {
109                 if (it->name() == name)
110                         return *it;
111         }
112         return *it; // silence warning
113 }
114
115
116 /////////////////////////////////////////////////////////////////////
117 //
118 // InsetCommandParams
119 //
120 /////////////////////////////////////////////////////////////////////
121
122
123 InsetCommandParams::InsetCommandParams(InsetCode code)
124         : insetCode_(code), preview_(false)
125 {
126         cmdName_ = getDefaultCmd(code);
127         info_ = findInfo(code, cmdName_);
128 }
129
130
131 InsetCommandParams::InsetCommandParams(InsetCode code,
132         string const & cmdName)
133         : insetCode_(code), cmdName_(cmdName), preview_(false)
134 {
135         info_ = findInfo(code, cmdName);
136 }
137
138
139 ParamInfo const & InsetCommandParams::findInfo(
140         InsetCode code, string const & cmdName)
141 {
142         switch (code) {
143         case BIBITEM_CODE:
144                 return InsetBibitem::findInfo(cmdName);
145         case BIBTEX_CODE:
146                 return InsetBibtex::findInfo(cmdName);
147         case CITE_CODE:
148                 return InsetCitation::findInfo(cmdName);        
149         case FLOAT_LIST_CODE:
150                 return InsetFloatList::findInfo(cmdName);
151         case HYPERLINK_CODE:
152                 return InsetHyperlink::findInfo(cmdName);
153         case INCLUDE_CODE:
154                 return InsetInclude::findInfo(cmdName);
155         case INDEX_PRINT_CODE:
156                 return InsetPrintIndex::findInfo(cmdName);
157         case LABEL_CODE:
158                 return InsetLabel::findInfo(cmdName);   
159         case NOMENCL_CODE:
160                 return InsetNomencl::findInfo(cmdName);
161         case NOMENCL_PRINT_CODE:
162                 return InsetPrintNomencl::findInfo(cmdName);
163         case REF_CODE:
164                 return InsetRef::findInfo(cmdName);
165         case TOC_CODE:
166                 return InsetTOC::findInfo(cmdName);
167         default:
168                 BOOST_ASSERT(false);
169         }
170         static const ParamInfo pi;
171         return pi; // to silence the warning
172 }
173
174
175 std::string InsetCommandParams::insetType() const
176 {
177         return insetName(insetCode_);
178 }
179
180
181 string InsetCommandParams::getDefaultCmd(InsetCode code) {
182         switch (code) {
183                 case BIBITEM_CODE: 
184                         return InsetBibitem::defaultCommand();
185                 case BIBTEX_CODE:
186                         return InsetBibtex::defaultCommand();
187                 case CITE_CODE:
188                         return InsetCitation::defaultCommand();
189                 case FLOAT_LIST_CODE:
190                         return InsetFloatList::defaultCommand();
191                 case HYPERLINK_CODE:
192                         return InsetHyperlink::defaultCommand();
193                 case INCLUDE_CODE:
194                         return InsetInclude::defaultCommand();
195                 case INDEX_PRINT_CODE:
196                         return InsetPrintIndex::defaultCommand();
197                 case LABEL_CODE:
198                         return InsetLabel::defaultCommand();
199                 case NOMENCL_CODE:
200                         return InsetNomencl::defaultCommand();
201                 case NOMENCL_PRINT_CODE:
202                         return InsetPrintNomencl::defaultCommand();
203                 case REF_CODE:
204                         return InsetRef::defaultCommand();
205                 case TOC_CODE:
206                         return InsetTOC::defaultCommand();
207                 default:
208                         BOOST_ASSERT(false);
209         }
210         return string(); // silence the warning
211 }
212
213
214 bool InsetCommandParams::isCompatibleCommand(
215                 InsetCode code, string const & s)
216 {
217         switch (code) {
218                 case BIBITEM_CODE: 
219                         return InsetBibitem::isCompatibleCommand(s);
220                 case BIBTEX_CODE:
221                         return InsetBibtex::isCompatibleCommand(s);
222                 case CITE_CODE:
223                         return InsetCitation::isCompatibleCommand(s);
224                 case FLOAT_LIST_CODE:
225                         return InsetFloatList::isCompatibleCommand(s);
226                 case HYPERLINK_CODE:
227                         return InsetHyperlink::isCompatibleCommand(s);
228                 case INCLUDE_CODE:
229                         return InsetInclude::isCompatibleCommand(s);
230                 case INDEX_PRINT_CODE:
231                         return InsetPrintIndex::isCompatibleCommand(s);
232                 case LABEL_CODE:
233                         return InsetLabel::isCompatibleCommand(s);
234                 case NOMENCL_CODE:
235                         return InsetNomencl::isCompatibleCommand(s);
236                 case NOMENCL_PRINT_CODE:
237                         return InsetPrintNomencl::isCompatibleCommand(s);
238                 case REF_CODE:
239                         return InsetRef::isCompatibleCommand(s);
240                 case TOC_CODE:
241                         return InsetTOC::isCompatibleCommand(s);
242                 default:
243                         BOOST_ASSERT(false);
244         }
245         return false; // silence the warning
246 }
247
248
249 void InsetCommandParams::setCmdName(string const & name)
250 {
251         if (!isCompatibleCommand(insetCode_, cmdName_)){
252                 LYXERR0("InsetCommand: Incompatible command name " << 
253                                 name << ".");
254                 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
255                                        _("Incompatible command name."));
256         }
257
258         cmdName_ = name;
259         info_ = findInfo(insetCode_, cmdName_);
260 }
261
262
263 void InsetCommandParams::read(Lexer & lex)
264 {
265         if (lex.isOK()) {
266                 lex.next();
267                 string const insetType = lex.getString();
268                 InsetCode const code = insetCode(insetType);
269                 if (code != insetCode_) {
270                         lex.printError("InsetCommandParams: Attempt to change type of inset.");
271                         throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
272                                          _("Attempt to change type of parameters."));
273                 }
274         }
275
276         if (lex.isOK()) {
277                 lex.next();
278                 string const test = lex.getString();
279                 if (test != "LatexCommand") {
280                         lex.printError("InsetCommandParams: No LatexCommand line found.");
281                         throw ExceptionMessage(WarningException, _("InsetCommandParams error:"),
282                                                _("Can't find LatexCommand line."));
283                 }
284         }
285         lex.next();
286         cmdName_ = lex.getString();
287         if (!isCompatibleCommand(insetCode_, cmdName_)){
288                 lex.printError("InsetCommandParams: Incompatible command name " + cmdName_ + ".");
289                 throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
290                                        _("Incompatible command name."));
291         }
292
293         info_ = findInfo(insetCode_, cmdName_);
294         
295         string token;
296         while (lex.isOK()) {
297                 lex.next();
298                 token = lex.getString();
299                 if (token == "\\end_inset")
300                         break;
301                 if (token == "preview") {
302                         lex.next();
303                         preview_ = lex.getBool();
304                         continue;
305                 }
306                 if (info_.hasParam(token)) {
307                         lex.next(true);
308                         params_[token] = lex.getDocString();
309                 } else {
310                         lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
311                         throw ExceptionMessage(WarningException,
312                                 _("InsetCommandParams: ") + from_ascii(cmdName_),
313                                 _("Unknown parameter name: ") + from_utf8(token));
314                 }
315         }
316         if (token != "\\end_inset") {
317                 lex.printError("Missing \\end_inset at this point. "
318                                "Read: `$$Token'");
319                 throw ExceptionMessage(WarningException,
320                         _("Missing \\end_inset at this point."),
321                         from_utf8(token));
322         }
323 }
324
325
326 void InsetCommandParams::write(ostream & os) const
327 {
328         os << "CommandInset " << insetType() << '\n';
329         os << "LatexCommand " << cmdName_ << '\n';
330         if (preview_)
331                 os << "preview true\n";
332         ParamInfo::const_iterator it  = info_.begin();
333         ParamInfo::const_iterator end = info_.end();
334         for (; it != end; ++it) {
335                 std::string const & name = it->name();
336                 docstring const & data = (*this)[name];
337                 if (!data.empty()) {
338                         // FIXME UNICODE
339                         os << name << ' '
340                            << Lexer::quoteString(to_utf8(data))
341                            << '\n';
342                 }
343         }
344 }
345
346
347 docstring InsetCommandParams::makeKeyValArgument() const
348 {
349         odocstringstream os;
350         bool didone = false;
351         ParamInfo::const_iterator it  = info_.begin();
352         ParamInfo::const_iterator end = info_.end();
353         for (; it != end; ++it) {
354                 if (!it->isKey())
355                         continue;
356                 string const & name = it->name();
357                 docstring const & data = (*this)[name];
358                 if (data.empty())
359                         continue;
360                 if (didone)
361                         os << ",";
362                 else 
363                         didone = true;
364                 os << from_utf8(name) << "=" << data;
365         }
366         return os.str();
367 }
368
369
370 bool InsetCommandParams::writeEmptyOptional(ParamInfo::const_iterator ci) const
371 {
372         if (!ci->isOptional())
373                 BOOST_ASSERT(false);
374         ++ci; // we want to start with the next one
375         ParamInfo::const_iterator end = info_.end();
376         for (; ci != end; ++ci) {
377                 switch (ci->type()) {
378                 case ParamInfo::LATEX_KEY:
379                 case ParamInfo::LYX_INTERNAL:
380                         break;
381
382                 case ParamInfo::LATEX_REQUIRED:
383                 case ParamInfo::LATEX_KV_REQUIRED:
384                         return false;
385
386                 case ParamInfo::LATEX_OPTIONAL: {
387                         std::string const & name = ci->name();
388                         docstring const & data = (*this)[name];
389                         if (!data.empty())
390                                 return true;
391                         break;
392                 }
393
394                 case ParamInfo::LATEX_KV_OPTIONAL: {
395                         docstring data = makeKeyValArgument();
396                         if (!data.empty())
397                                 return true;
398                         break;
399                 }
400                 } //end switch
401         }
402         return false;
403 }
404
405
406 docstring const InsetCommandParams::getCommand() const
407 {
408         docstring s = '\\' + from_ascii(cmdName_);
409         bool noparam = true;
410         ParamInfo::const_iterator it  = info_.begin();
411         ParamInfo::const_iterator end = info_.end();
412         for (; it != end; ++it) {
413                 std::string const & name = it->name();
414                 switch (it->type()) {
415                 case ParamInfo::LATEX_KEY:
416                 case ParamInfo::LYX_INTERNAL:
417                         break;
418
419                 case ParamInfo::LATEX_REQUIRED: {
420                         docstring const & data = (*this)[name];
421                         s += '{' + data + '}';
422                         noparam = false;
423                         break;
424                 }
425                 case ParamInfo::LATEX_KV_REQUIRED: {
426                         s += "{" + makeKeyValArgument() + "}";
427                         noparam = false;
428                         break;
429                 }
430                 case ParamInfo::LATEX_OPTIONAL: {
431                         docstring const & data = (*this)[name];
432                         if (!data.empty()) {
433                                 s += '[' + data + ']';
434                                 noparam = false;
435                         } else if (writeEmptyOptional(it)) {
436                                         s += "[]";
437                                         noparam = false;
438                         }
439                         break;
440                 } 
441                 case ParamInfo::LATEX_KV_OPTIONAL: {
442                         docstring data = makeKeyValArgument();
443                         if (!data.empty()) {
444                                 s += '[' + data + ']';
445                                 noparam = false;
446                         } else if (writeEmptyOptional(it)) {
447                                         s += "[]";
448                                         noparam = false;
449                                 }
450                         break;
451                 }
452                 } //end switch
453         }
454         if (noparam)
455                 // Make sure that following stuff does not change the
456                 // command name.
457                 s += "{}";
458         return s;
459 }
460
461
462 docstring const InsetCommandParams::getFirstNonOptParam() const
463 {
464         ParamInfo::const_iterator it = 
465                 find_if(info_.begin(), info_.end(), 
466                         not1(mem_fun_ref(&ParamInfo::ParamData::isOptional)));
467         if (it == info_.end())
468                 BOOST_ASSERT(false);
469         return (*this)[it->name()];
470 }
471
472
473 docstring const & InsetCommandParams::operator[](string const & name) const
474 {
475         static const docstring dummy; //so we don't return a ref to temporary
476         BOOST_ASSERT(info_.hasParam(name));
477         ParamMap::const_iterator data = params_.find(name);
478         if (data == params_.end() || data->second.empty())
479                 return dummy;
480         return data->second;
481 }
482
483
484 docstring & InsetCommandParams::operator[](string const & name)
485 {
486         BOOST_ASSERT(info_.hasParam(name));
487         return params_[name];
488 }
489
490
491 void InsetCommandParams::clear()
492 {
493         params_.clear();
494 }
495
496
497 bool operator==(InsetCommandParams const & o1,
498                 InsetCommandParams const & o2)
499 {
500         return o1.insetCode_ == o2.insetCode_ &&
501                o1.cmdName_ == o2.cmdName_ &&
502                o1.info_ == o2.info_ &&
503                o1.params_ == o2.params_ &&
504                o1.preview_ == o2.preview_;
505 }
506
507
508 bool operator!=(InsetCommandParams const & o1,
509                 InsetCommandParams const & o2)
510 {
511         return !(o1 == o2);
512 }
513
514
515 } // namespace lyx