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