]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
a3e0cbce2465b140f0c5e347f1962fae6b934fa7
[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
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 HFILL_CODE:
153                 return InsetHFill::findInfo(cmdName);
154         case HYPERLINK_CODE:
155                 return InsetHyperlink::findInfo(cmdName);
156         case INCLUDE_CODE:
157                 return InsetInclude::findInfo(cmdName);
158         case INDEX_PRINT_CODE:
159                 return InsetPrintIndex::findInfo(cmdName);
160         case LABEL_CODE:
161                 return InsetLabel::findInfo(cmdName);   
162         case NOMENCL_CODE:
163                 return InsetNomencl::findInfo(cmdName);
164         case NOMENCL_PRINT_CODE:
165                 return InsetPrintNomencl::findInfo(cmdName);
166         case REF_CODE:
167                 return InsetRef::findInfo(cmdName);
168         case TOC_CODE:
169                 return InsetTOC::findInfo(cmdName);
170         default:
171                 BOOST_ASSERT(false);
172         }
173         static const ParamInfo pi;
174         return pi; // to silence the warning
175 }
176
177
178 std::string InsetCommandParams::insetType() const
179 {
180         return insetName(insetCode_);
181 }
182
183
184 string InsetCommandParams::getDefaultCmd(InsetCode code) {
185         switch (code) {
186                 case BIBITEM_CODE: 
187                         return InsetBibitem::defaultCommand();
188                 case BIBTEX_CODE:
189                         return InsetBibtex::defaultCommand();
190                 case CITE_CODE:
191                         return InsetCitation::defaultCommand();
192                 case FLOAT_LIST_CODE:
193                         return InsetFloatList::defaultCommand();
194                 case HFILL_CODE:
195                         return InsetHFill::defaultCommand();
196                 case HYPERLINK_CODE:
197                         return InsetHyperlink::defaultCommand();
198                 case INCLUDE_CODE:
199                         return InsetInclude::defaultCommand();
200                 case INDEX_PRINT_CODE:
201                         return InsetPrintIndex::defaultCommand();
202                 case LABEL_CODE:
203                         return InsetLabel::defaultCommand();
204                 case NOMENCL_CODE:
205                         return InsetNomencl::defaultCommand();
206                 case NOMENCL_PRINT_CODE:
207                         return InsetPrintNomencl::defaultCommand();
208                 case REF_CODE:
209                         return InsetRef::defaultCommand();
210                 case TOC_CODE:
211                         return InsetTOC::defaultCommand();
212                 default:
213                         BOOST_ASSERT(false);
214         }
215         return string(); // silence the warning
216 }
217
218
219 bool InsetCommandParams::isCompatibleCommand(
220                 InsetCode code, string const & s)
221 {
222         switch (code) {
223                 case BIBITEM_CODE: 
224                         return InsetBibitem::isCompatibleCommand(s);
225                 case BIBTEX_CODE:
226                         return InsetBibtex::isCompatibleCommand(s);
227                 case CITE_CODE:
228                         return InsetCitation::isCompatibleCommand(s);
229                 case FLOAT_LIST_CODE:
230                         return InsetFloatList::isCompatibleCommand(s);
231                 case HFILL_CODE:
232                         return InsetHFill::isCompatibleCommand(s);
233                 case HYPERLINK_CODE:
234                         return InsetHyperlink::isCompatibleCommand(s);
235                 case INCLUDE_CODE:
236                         return InsetInclude::isCompatibleCommand(s);
237                 case INDEX_PRINT_CODE:
238                         return InsetPrintIndex::isCompatibleCommand(s);
239                 case LABEL_CODE:
240                         return InsetLabel::isCompatibleCommand(s);
241                 case NOMENCL_CODE:
242                         return InsetNomencl::isCompatibleCommand(s);
243                 case NOMENCL_PRINT_CODE:
244                         return InsetPrintNomencl::isCompatibleCommand(s);
245                 case REF_CODE:
246                         return InsetRef::isCompatibleCommand(s);
247                 case TOC_CODE:
248                         return InsetTOC::isCompatibleCommand(s);
249                 default:
250                         BOOST_ASSERT(false);
251         }
252         return false; // silence the warning
253 }
254
255
256 void InsetCommandParams::setCmdName(string const & name)
257 {
258         if (!isCompatibleCommand(insetCode_, cmdName_)){
259                 LYXERR0("InsetCommand: Incompatible command name " << 
260                                 name << ".");
261                 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
262                                        _("Incompatible command name."));
263         }
264
265         cmdName_ = name;
266         info_ = findInfo(insetCode_, cmdName_);
267 }
268
269
270 void InsetCommandParams::read(Lexer & lex)
271 {
272         if (lex.isOK()) {
273                 lex.next();
274                 string const insetType = lex.getString();
275                 InsetCode const code = insetCode(insetType);
276                 if (code != insetCode_) {
277                         lex.printError("InsetCommandParams: Attempt to change type of inset.");
278                         throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
279                                          _("Attempt to change type of parameters."));
280                 }
281         }
282
283         if (lex.isOK()) {
284                 lex.next();
285                 string const test = lex.getString();
286                 if (test != "LatexCommand") {
287                         lex.printError("InsetCommandParams: No LatexCommand line found.");
288                         throw ExceptionMessage(WarningException, _("InsetCommandParams error:"),
289                                                _("Can't find LatexCommand line."));
290                 }
291         }
292         lex.next();
293         cmdName_ = lex.getString();
294         if (!isCompatibleCommand(insetCode_, cmdName_)){
295                 lex.printError("InsetCommandParams: Incompatible command name " + cmdName_ + ".");
296                 throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
297                                        _("Incompatible command name."));
298         }
299
300         info_ = findInfo(insetCode_, cmdName_);
301         
302         string token;
303         while (lex.isOK()) {
304                 lex.next();
305                 token = lex.getString();
306                 if (token == "\\end_inset")
307                         break;
308                 if (token == "preview") {
309                         lex.next();
310                         preview_ = lex.getBool();
311                         continue;
312                 }
313                 if (info_.hasParam(token)) {
314                         lex.next(true);
315                         params_[token] = lex.getDocString();
316                 } else {
317                         lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
318                         throw ExceptionMessage(WarningException,
319                                 _("InsetCommandParams: ") + from_ascii(cmdName_),
320                                 _("Unknown parameter name: ") + from_utf8(token));
321                 }
322         }
323         if (token != "\\end_inset") {
324                 lex.printError("Missing \\end_inset at this point. "
325                                "Read: `$$Token'");
326                 throw ExceptionMessage(WarningException,
327                         _("Missing \\end_inset at this point."),
328                         from_utf8(token));
329         }
330 }
331
332
333 void InsetCommandParams::write(ostream & os) const
334 {
335         os << "CommandInset " << insetType() << '\n';
336         os << "LatexCommand " << cmdName_ << '\n';
337         if (preview_)
338                 os << "preview true\n";
339         ParamInfo::const_iterator it  = info_.begin();
340         ParamInfo::const_iterator end = info_.end();
341         for (; it != end; ++it) {
342                 std::string const & name = it->name();
343                 docstring const & data = (*this)[name];
344                 if (!data.empty()) {
345                         // FIXME UNICODE
346                         os << name << ' '
347                            << Lexer::quoteString(to_utf8(data))
348                            << '\n';
349                 }
350         }
351 }
352
353
354 docstring InsetCommandParams::makeKeyValArgument() const
355 {
356         odocstringstream os;
357         bool didone = false;
358         ParamInfo::const_iterator it  = info_.begin();
359         ParamInfo::const_iterator end = info_.end();
360         for (; it != end; ++it) {
361                 if (!it->isKey())
362                         continue;
363                 string const & name = it->name();
364                 docstring const & data = (*this)[name];
365                 if (data.empty())
366                         continue;
367                 if (didone)
368                         os << ",";
369                 else 
370                         didone = true;
371                 os << from_utf8(name) << "=" << data;
372         }
373         return os.str();
374 }
375
376
377 bool InsetCommandParams::writeEmptyOptional(ParamInfo::const_iterator ci) const
378 {
379         if (!ci->isOptional())
380                 BOOST_ASSERT(false);
381         ++ci; // we want to start with the next one
382         ParamInfo::const_iterator end = info_.end();
383         for (; ci != end; ++ci) {
384                 switch (ci->type()) {
385                 case ParamInfo::LATEX_KEY:
386                 case ParamInfo::LYX_INTERNAL:
387                         break;
388
389                 case ParamInfo::LATEX_REQUIRED:
390                 case ParamInfo::LATEX_KV_REQUIRED:
391                         return false;
392
393                 case ParamInfo::LATEX_OPTIONAL: {
394                         std::string const & name = ci->name();
395                         docstring const & data = (*this)[name];
396                         if (!data.empty())
397                                 return true;
398                         break;
399                 }
400
401                 case ParamInfo::LATEX_KV_OPTIONAL: {
402                         docstring data = makeKeyValArgument();
403                         if (!data.empty())
404                                 return true;
405                         break;
406                 }
407                 } //end switch
408         }
409         return false;
410 }
411
412
413 docstring const InsetCommandParams::getCommand() const
414 {
415         docstring s = '\\' + from_ascii(cmdName_);
416         bool noparam = true;
417         ParamInfo::const_iterator it  = info_.begin();
418         ParamInfo::const_iterator end = info_.end();
419         for (; it != end; ++it) {
420                 std::string const & name = it->name();
421                 switch (it->type()) {
422                 case ParamInfo::LATEX_KEY:
423                 case ParamInfo::LYX_INTERNAL:
424                         break;
425
426                 case ParamInfo::LATEX_REQUIRED: {
427                         docstring const & data = (*this)[name];
428                         s += '{' + data + '}';
429                         noparam = false;
430                         break;
431                 }
432                 case ParamInfo::LATEX_KV_REQUIRED: {
433                         s += "{" + makeKeyValArgument() + "}";
434                         noparam = false;
435                         break;
436                 }
437                 case ParamInfo::LATEX_OPTIONAL: {
438                         docstring const & data = (*this)[name];
439                         if (!data.empty()) {
440                                 s += '[' + data + ']';
441                                 noparam = false;
442                         } else if (writeEmptyOptional(it)) {
443                                         s += "[]";
444                                         noparam = false;
445                         }
446                         break;
447                 } 
448                 case ParamInfo::LATEX_KV_OPTIONAL: {
449                         docstring data = makeKeyValArgument();
450                         if (!data.empty()) {
451                                 s += '[' + data + ']';
452                                 noparam = false;
453                         } else if (writeEmptyOptional(it)) {
454                                         s += "[]";
455                                         noparam = false;
456                                 }
457                         break;
458                 }
459                 } //end switch
460         }
461         if (noparam)
462                 // Make sure that following stuff does not change the
463                 // command name.
464                 s += "{}";
465         return s;
466 }
467
468
469 docstring const InsetCommandParams::getFirstNonOptParam() const
470 {
471         ParamInfo::const_iterator it = 
472                 find_if(info_.begin(), info_.end(), 
473                         not1(mem_fun_ref(&ParamInfo::ParamData::isOptional)));
474         if (it == info_.end())
475                 BOOST_ASSERT(false);
476         return (*this)[it->name()];
477 }
478
479
480 docstring const & InsetCommandParams::operator[](string const & name) const
481 {
482         static const docstring dummy; //so we don't return a ref to temporary
483         BOOST_ASSERT(info_.hasParam(name));
484         ParamMap::const_iterator data = params_.find(name);
485         if (data == params_.end() || data->second.empty())
486                 return dummy;
487         return data->second;
488 }
489
490
491 docstring & InsetCommandParams::operator[](string const & name)
492 {
493         BOOST_ASSERT(info_.hasParam(name));
494         return params_[name];
495 }
496
497
498 void InsetCommandParams::clear()
499 {
500         params_.clear();
501 }
502
503
504 bool operator==(InsetCommandParams const & o1,
505                 InsetCommandParams const & o2)
506 {
507         return o1.insetCode_ == o2.insetCode_ &&
508                o1.cmdName_ == o2.cmdName_ &&
509                o1.info_ == o2.info_ &&
510                o1.params_ == o2.params_ &&
511                o1.preview_ == o2.preview_;
512 }
513
514
515 bool operator!=(InsetCommandParams const & o1,
516                 InsetCommandParams const & o2)
517 {
518         return !(o1 == o2);
519 }
520
521
522 } // namespace lyx