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