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