]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
f1804a29d9cefb8762b56064b5b86a08d86c0944
[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/ExceptionMessage.h"
34 #include "support/gettext.h"
35 #include "support/lstrings.h"
36 #include "support/docstream.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, bool b) :
46         name_(s), optional_(b)
47 {}
48
49
50 bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const
51 {
52         return name() == rhs.name() && isOptional() == rhs.isOptional();
53 }
54
55
56 bool ParamInfo::hasParam(std::string const & name) const
57 {
58         const_iterator it = begin();
59         for (; it != end(); ++it) {
60                 if (it->name() == name)
61                         return true;
62         }
63         return false;
64 }
65
66
67 void ParamInfo::add(std::string const & name, bool opt)
68
69         info_.push_back(ParamData(name, opt)); 
70 }
71
72
73 bool ParamInfo::operator==(ParamInfo const & rhs) const
74 {
75         // the idea here is to check each ParamData for equality
76         const_iterator itL  = begin();
77         const_iterator itR  = rhs.begin();
78         const_iterator endL = end();
79         const_iterator endR = rhs.end();
80         while (true) {
81                 // if they both end together, return true
82                 if (itL == endL && itR == endR)
83                                 return true;
84                 // but if one ends before the other, return false
85                 if (itL == endL || itR == endR)
86                         return false;
87                 //check this one for equality
88                 if (*itL != *itR)
89                         return false;
90                 // equal, so check the next one
91                 ++itL;
92                 ++itR;
93         }
94 }
95
96
97 InsetCommandParams::InsetCommandParams(InsetCode code)
98         : insetCode_(code), preview_(false)
99 {
100         cmdName_ = getDefaultCmd(code);
101         info_ = findInfo(code, cmdName_);
102 }
103
104
105 InsetCommandParams::InsetCommandParams(InsetCode code,
106         string const & cmdName)
107         : insetCode_(code), cmdName_(cmdName), preview_(false)
108 {
109         info_ = findInfo(code, cmdName);
110 }
111
112
113 ParamInfo const & InsetCommandParams::findInfo(
114         InsetCode code, string const & cmdName)
115 {
116         switch (code) {
117         case BIBITEM_CODE:
118                 return InsetBibitem::findInfo(cmdName);
119         case BIBTEX_CODE:
120                 return InsetBibtex::findInfo(cmdName);
121         case CITE_CODE:
122                 return InsetCitation::findInfo(cmdName);        
123         case FLOAT_LIST_CODE:
124                 return InsetFloatList::findInfo(cmdName);
125         case HFILL_CODE:
126                 return InsetHFill::findInfo(cmdName);
127         case HYPERLINK_CODE:
128                 return InsetHyperlink::findInfo(cmdName);
129         case INCLUDE_CODE:
130                 return InsetInclude::findInfo(cmdName);
131         case INDEX_PRINT_CODE:
132                 return InsetPrintIndex::findInfo(cmdName);
133         case LABEL_CODE:
134                 return InsetLabel::findInfo(cmdName);   
135         case NOMENCL_CODE:
136                 return InsetNomencl::findInfo(cmdName);
137         case NOMENCL_PRINT_CODE:
138                 return InsetPrintNomencl::findInfo(cmdName);
139         case REF_CODE:
140                 return InsetRef::findInfo(cmdName);
141         case TOC_CODE:
142                 return InsetTOC::findInfo(cmdName);
143         default:
144                 BOOST_ASSERT(false);
145         }
146         static const ParamInfo pi;
147         return pi; // to silence the warning
148 }
149
150
151 string InsetCommandParams::getDefaultCmd(InsetCode code) {
152         switch (code) {
153                 case BIBITEM_CODE: 
154                         return InsetBibitem::defaultCommand();
155                 case BIBTEX_CODE:
156                         return InsetBibtex::defaultCommand();
157                 case CITE_CODE:
158                         return InsetCitation::defaultCommand();
159                 case FLOAT_LIST_CODE:
160                         return InsetFloatList::defaultCommand();
161                 case HFILL_CODE:
162                         return InsetHFill::defaultCommand();
163                 case HYPERLINK_CODE:
164                         return InsetHyperlink::defaultCommand();
165                 case INCLUDE_CODE:
166                         return InsetInclude::defaultCommand();
167                 case INDEX_PRINT_CODE:
168                         return InsetPrintIndex::defaultCommand();
169                 case LABEL_CODE:
170                         return InsetLabel::defaultCommand();
171                 case NOMENCL_CODE:
172                         return InsetNomencl::defaultCommand();
173                 case NOMENCL_PRINT_CODE:
174                         return InsetPrintNomencl::defaultCommand();
175                 case REF_CODE:
176                         return InsetRef::defaultCommand();
177                 case TOC_CODE:
178                         return InsetTOC::defaultCommand();
179                 default:
180                         BOOST_ASSERT(false);
181         }
182         return string(); // silence the warning
183 }
184
185
186 bool InsetCommandParams::isCompatibleCommand(
187                 InsetCode code, string const & s)
188 {
189         switch (code) {
190                 case BIBITEM_CODE: 
191                         return InsetBibitem::isCompatibleCommand(s);
192                 case BIBTEX_CODE:
193                         return InsetBibtex::isCompatibleCommand(s);
194                 case CITE_CODE:
195                         return InsetCitation::isCompatibleCommand(s);
196                 case FLOAT_LIST_CODE:
197                         return InsetFloatList::isCompatibleCommand(s);
198                 case HFILL_CODE:
199                         return InsetHFill::isCompatibleCommand(s);
200                 case HYPERLINK_CODE:
201                         return InsetHyperlink::isCompatibleCommand(s);
202                 case INCLUDE_CODE:
203                         return InsetInclude::isCompatibleCommand(s);
204                 case INDEX_PRINT_CODE:
205                         return InsetPrintIndex::isCompatibleCommand(s);
206                 case LABEL_CODE:
207                         return InsetLabel::isCompatibleCommand(s);
208                 case NOMENCL_CODE:
209                         return InsetNomencl::isCompatibleCommand(s);
210                 case NOMENCL_PRINT_CODE:
211                         return InsetPrintNomencl::isCompatibleCommand(s);
212                 case REF_CODE:
213                         return InsetRef::isCompatibleCommand(s);
214                 case TOC_CODE:
215                         return InsetTOC::isCompatibleCommand(s);
216                 default:
217                         BOOST_ASSERT(false);
218         }
219         return false; // silence the warning
220 }
221
222
223 void InsetCommandParams::setCmdName(string const & name)
224 {
225         if (!isCompatibleCommand(insetCode_, cmdName_)){
226                 LYXERR0("InsetCommand: Incompatible command name " << 
227                                 name << ".");
228                 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
229                                        _("Incompatible command name."));
230         }
231
232         cmdName_ = name;
233         info_ = findInfo(insetCode_, cmdName_);
234 }
235
236
237 void InsetCommandParams::read(Lexer & lex)
238 {
239         if (lex.isOK()) {
240                 lex.next();
241                 string const insetType = lex.getString();
242                 InsetCode const code = insetCode(insetType);
243                 if (code != insetCode_) {
244                         lex.printError("InsetCommandParams: Attempt to change type of inset.");
245                         throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
246                                          _("Attempt to change type of parameters."));
247                 }
248         }
249
250         if (lex.isOK()) {
251                 lex.next();
252                 string const test = lex.getString();
253                 if (test != "LatexCommand") {
254                         lex.printError("InsetCommandParams: No LatexCommand line found.");
255                         throw ExceptionMessage(WarningException, _("InsetCommandParams error:"),
256                                                _("Can't find LatexCommand line."));
257                 }
258         }
259         lex.next();
260         cmdName_ = lex.getString();
261         if (!isCompatibleCommand(insetCode_, cmdName_)){
262                 lex.printError("InsetCommandParams: Incompatible command name " + cmdName_ + ".");
263                 throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
264                                        _("Incompatible command name."));
265         }
266
267         info_ = findInfo(insetCode_, cmdName_);
268         
269         string token;
270         while (lex.isOK()) {
271                 lex.next();
272                 token = lex.getString();
273                 if (token == "\\end_inset")
274                         break;
275                 if (token == "preview") {
276                         lex.next();
277                         preview_ = lex.getBool();
278                         continue;
279                 }
280                 if (info_.hasParam(token)) {
281                         lex.next(true);
282                         params_[token] = lex.getDocString();
283                 } else {
284                         lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
285                         throw ExceptionMessage(WarningException,
286                                 _("InsetCommandParams: ") + from_ascii(cmdName_),
287                                 _("Unknown parameter name: ") + from_utf8(token));
288                 }
289         }
290         if (token != "\\end_inset") {
291                 lex.printError("Missing \\end_inset at this point. "
292                                "Read: `$$Token'");
293                 throw ExceptionMessage(WarningException,
294                         _("Missing \\end_inset at this point."),
295                         from_utf8(token));
296         }
297 }
298
299
300 void InsetCommandParams::write(ostream & os) const
301 {
302         os << "CommandInset " << insetType() << '\n';
303         os << "LatexCommand " << cmdName_ << '\n';
304         if (preview_)
305                 os << "preview true\n";
306         ParamInfo::const_iterator it  = info_.begin();
307         ParamInfo::const_iterator end = info_.end();
308         for (; it != end; ++it) {
309                 std::string const & name = it->name();
310                 docstring const & data = (*this)[name];
311                 if (!data.empty()) {
312                         // FIXME UNICODE
313                         os << name << ' '
314                            << Lexer::quoteString(to_utf8(data))
315                            << '\n';
316                 }
317         }
318 }
319
320
321 docstring const InsetCommandParams::getCommand() const
322 {
323         docstring s = '\\' + from_ascii(cmdName_);
324         bool noparam = true;
325         ParamInfo::const_iterator it  = info_.begin();
326         ParamInfo::const_iterator end = info_.end();
327         for (; it != end; ++it) {
328                 std::string const & name = it->name();
329                 docstring const & data = (*this)[name];
330                 if (!it->isOptional()) {
331                         s += '{' + data + '}';
332                         noparam = false;
333                         continue;
334                 }
335                 if (!data.empty()) {
336                         s += '[' + data + ']';
337                         noparam = false;
338                         continue;
339                 }
340                 // This param is therefore optional but empty.
341                 // But we need to write it anyway if nonempty
342                 // optional parameters follow before the next
343                 // required parameter.
344                 ParamInfo::const_iterator it2 = it;
345                 for (++it2; it2 != end; ++it2) {
346                         if (!it2->isOptional())
347                                 break;
348                         std::string const & name2 = it2->name();
349                         docstring const & data2 = (*this)[name2];
350                         if (!data2.empty()) {
351                                 s += "[]";
352                                 noparam = false;
353                                 break;
354                         }
355                 }
356         }
357         if (noparam)
358                 // Make sure that following stuff does not change the
359                 // command name.
360                 s += "{}";
361         return s;
362 }
363
364
365 namespace {
366         //predicate for what follows
367         bool paramIsNonOptional(ParamInfo::ParamData pi)
368         {
369                 return !pi.isOptional();
370         }
371 }
372
373 docstring const InsetCommandParams::getFirstNonOptParam() const
374 {
375         ParamInfo::const_iterator it = 
376                 find_if(info_.begin(), info_.end(), paramIsNonOptional);
377         if (it == info_.end())
378                 BOOST_ASSERT(false);
379         return (*this)[it->name()];
380 }
381
382
383 docstring const & InsetCommandParams::operator[](string const & name) const
384 {
385         static const docstring dummy; //so we don't return a ref to temporary
386         if (!info_.hasParam(name))
387                 BOOST_ASSERT(false);
388         ParamMap::const_iterator data = params_.find(name);
389         if (data == params_.end() || data->second.empty())
390                 return dummy;
391         return data->second;
392 }
393
394
395 docstring & InsetCommandParams::operator[](string const & name)
396 {
397         if (!info_.hasParam(name))
398                 BOOST_ASSERT(false);
399         return params_[name];
400 }
401
402
403 void InsetCommandParams::clear()
404 {
405         params_.clear();
406 }
407
408
409 bool operator==(InsetCommandParams const & o1,
410                 InsetCommandParams const & o2)
411 {
412         return o1.insetCode_ == o2.insetCode_ &&
413                o1.cmdName_ == o2.cmdName_ &&
414                o1.info_ == o2.info_ &&
415                o1.params_ == o2.params_ &&
416                o1.preview_ == o2.preview_;
417 }
418
419
420 bool operator!=(InsetCommandParams const & o1,
421                 InsetCommandParams const & o2)
422 {
423         return !(o1 == o2);
424 }
425
426
427 } // namespace lyx