]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
5ac876a43824a58cf7cc8081dfc724944a965107
[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  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetCommandParams.h"
15
16 #include "debug.h"
17 #include "gettext.h"
18 #include "Lexer.h"
19
20 #include "support/ExceptionMessage.h"
21 #include "support/lstrings.h"
22
23 #include <boost/assert.hpp>
24
25
26 namespace lyx {
27
28 using support::findToken;
29
30 using std::string;
31 using std::endl;
32 using std::ostream;
33
34 using support::ExceptionMessage;
35 using support::WarningException;
36
37
38 InsetCommandParams::InsetCommandParams(InsetCode code)
39         : insetCode_(code), preview_(false)
40 {
41         cmdName_ = getDefaultCmd(code);
42         info_ = findInfo(code, cmdName_);
43         BOOST_ASSERT(info_);
44         params_.resize(info_->n);
45 }
46
47
48 InsetCommandParams::InsetCommandParams(InsetCode code,
49         string const & cmdName)
50         : insetCode_(code), cmdName_(cmdName), preview_(false)
51 {
52         info_ = findInfo(code, cmdName);
53         BOOST_ASSERT(info_);
54         params_.resize(info_->n);
55 }
56
57
58 //FIXME This should go into the Insets themselves...so they will tell
59 //us what parameters they want.
60 //Should this just vanish in favor of the two arg version, or is there
61 //a reason to use it in some cases? What should happen in the single
62 //arg case, then? Maybe use the default? or leave that up to the inset?
63 InsetCommandParams::CommandInfo const *
64         InsetCommandParams::findInfo(InsetCode code)
65 {
66         // No parameter may be named "preview", because that is a required
67         // flag for all commands.
68
69         switch (code) {
70         case BIBITEM_CODE: {
71                 static const char * const paramnames[] = {"label", "key", ""};
72                 static const bool isoptional[] = {true, false};
73                 static const CommandInfo info = {2, paramnames, isoptional};
74                 return &info;
75         }
76         case BIBTEX_CODE: {
77                 static const char * const paramnames[] =
78                                 {"options", "btprint", "bibfiles", ""};
79                 static const bool isoptional[] = {true, true, false};
80                 static const CommandInfo info = {3, paramnames, isoptional};
81                 return &info;
82         }
83         case CITE_CODE: {
84                 // standard cite does only take one argument if jurabib is
85                 // not used, but jurabib extends this to two arguments, so
86                 // we have to allow both here. InsetCitation takes care that
87                 // LaTeX output is nevertheless correct.
88                 static const char * const paramnames[] =
89                                 {"after", "before", "key", ""};
90                 static const bool isoptional[] = {true, true, false};
91                 static const CommandInfo info = {3, paramnames, isoptional};
92                 return &info;
93         }
94         case FLOAT_LIST_CODE: {
95                 static const char * const paramnames[] = {"type", ""};
96                 static const bool isoptional[] = {false};
97                 static const CommandInfo info = {1, paramnames, isoptional};
98                 return &info;
99         }
100         case HFILL_CODE: {
101                 static const char * const paramnames[] = {""};
102                 static const CommandInfo info = {0, paramnames, 0};
103                 return &info;
104         }
105         case HYPERLINK_CODE: {
106                 static const char * const paramnames[] =
107                                 {"name", "target", ""};
108                 static const bool isoptional[] = {true, false};
109                 static const CommandInfo info = {2, paramnames, isoptional};
110                 return &info;
111         }
112         case INCLUDE_CODE: {
113                 //This is only correct for the case of listings, but it'll do for now.
114                 //In the other cases, this second parameter should just be empty.
115                 static const char * const paramnames[] = {"filename", "lstparams", ""};
116                 static const bool isoptional[] = {false, true};
117                 static const CommandInfo info = {2, paramnames, isoptional};
118                 return &info;
119         }
120         case INDEX_CODE: 
121         case INDEX_PRINT_CODE:
122         case LABEL_CODE: {
123                 static const char * const paramnames[] = {"name", ""};
124                 static const bool isoptional[] = {false};
125                 static const CommandInfo info = {1, paramnames, isoptional};
126                 return &info;
127         }
128         case NOMENCL_CODE: {
129                 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
130                 static const bool isoptional[] = {true, false, false};
131                 static const CommandInfo info = {3, paramnames, isoptional};
132                 return &info;
133         }
134         case NOMENCL_PRINT_CODE: {
135                 static const char * const paramnames[] = {"labelwidth", ""};
136                 static const bool isoptional[] = {true};
137                 static const CommandInfo info = {1, paramnames, isoptional};
138                 return &info;
139         }
140         case REF_CODE: {
141                 static const char * const paramnames[] =
142                                 {"name", "reference", ""};
143                 static const bool isoptional[] = {true, false};
144                 static const CommandInfo info = {2, paramnames, isoptional};
145                 return &info;
146         }
147         case TOC_CODE: {
148                 static const char * const paramnames[] = {"type", ""};
149                 static const bool isoptional[] = {false};
150                 static const CommandInfo info = {1, paramnames, isoptional};
151                 return &info;
152         }
153         default:
154                 BOOST_ASSERT(false);
155         }
156         return 0;
157 }
158
159
160 //FIXME Will eventually call a static method, etc.
161 InsetCommandParams::CommandInfo const *
162                 InsetCommandParams::findInfo(InsetCode code,
163                                              std::string const &/* cmdName*/)
164 {
165         return findInfo(code);
166 }
167
168
169 //FIXME Should call InsetBibitem::getDefaultCmd(), eg
170 std::string InsetCommandParams::getDefaultCmd(InsetCode code) {
171         switch (code) {
172                 case BIBITEM_CODE: 
173                         return "bibitem";
174                 case BIBTEX_CODE:
175                         return "bibtex"; //this is an unused dummy
176                 case CITE_CODE:
177                         return "cite";
178                 case FLOAT_LIST_CODE:
179                         return "listoftables";
180                 case HFILL_CODE:
181                         return "hfill";
182                 case HYPERLINK_CODE:
183                         return "href";
184                 case INCLUDE_CODE:
185                         return "include";
186                 case INDEX_CODE: 
187                         return "index";
188                 case INDEX_PRINT_CODE:
189                         return "printindex";
190                 case LABEL_CODE:
191                         return "label";
192                 case NOMENCL_CODE:
193                         return "nomenclature";
194                 case NOMENCL_PRINT_CODE:
195                         return "printnomenclature";
196                 case REF_CODE:
197                         return "ref";
198                 case TOC_CODE:
199                         return "tableofcontents";
200                 default:
201                         BOOST_ASSERT(false);
202         }
203         return "";
204 }
205
206
207 void InsetCommandParams::setCmdName(string const & name)
208 {
209         //FIXME Check command compatibility
210         cmdName_ = name;
211         CommandInfo const * const info = findInfo(insetCode_, cmdName_);
212         if (!info) {
213                 lyxerr << "Command '" << name << "' is not compatible with a '" <<
214                         insetType() << "' inset." << std::endl;
215                 return;
216         }
217         ParamVector params(info->n);
218         // Overtake parameters with the same name
219         for (size_t i = 0; i < info_->n; ++i) {
220                 int j = findToken(info->paramnames, info_->paramnames[i]);
221                 if (j >= 0)
222                         params[j] = params_[i];
223         }
224         info_ = info;
225         std::swap(params, params_);
226 }
227
228
229 void InsetCommandParams::read(Lexer & lex)
230 {
231         if (lex.isOK()) {
232                 lex.next();
233                 string const insetType = lex.getString();
234                 InsetCode const code = insetCode(insetType);
235                 if (code != insetCode_) {
236                         lex.printError("InsetCommand: Attempt to change type of parameters.");
237                         throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
238                                 from_utf8("Attempt to change type of parameters."));
239                 }
240         }
241
242         if (lex.isOK()) {
243                 lex.next();
244                 string const test = lex.getString();
245                 if (test != "LatexCommand") {
246                         lex.printError("InsetCommand: no LatexCommand line found.");
247                         throw ExceptionMessage(WarningException, _("InsetCommand error:"),
248                                 from_utf8("Can't find LatexCommand line."));
249                 }
250         }
251         lex.next();
252         cmdName_ = lex.getString();
253         //FIXME
254         //check that this command is ok with the inset...
255         //so that'll be some kind of static call, depending 
256         //upon what insetType_ is.
257         //it's possible that should go into InsetCommand.cpp,
258         //or maybe it's a standalone function.
259         info_ = findInfo(insetCode_, cmdName_);
260         if (!info_) {
261                 lex.printError("InsetCommand: Unknown inset name `$$Token'");
262                 throw ExceptionMessage(WarningException,
263                         _("Unknown inset name: "), from_utf8(insetType()));
264         }
265         
266         string token;
267         while (lex.isOK()) {
268                 lex.next();
269                 token = lex.getString();
270                 if (token == "\\end_inset")
271                         break;
272                 if (token == "preview") {
273                         lex.next();
274                         preview_ = lex.getBool();
275                         continue;
276                 }
277                 int const i = findToken(info_->paramnames, token);
278                 if (i >= 0) {
279                         lex.next(true);
280                         params_[i] = lex.getDocString();
281                 } else {
282                         lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
283                         throw ExceptionMessage(WarningException,
284                                 _("Inset Command: ") + from_ascii(cmdName_),
285                                 _("Unknown parameter name: ") + from_utf8(token));
286                 }
287         }
288         if (token != "\\end_inset") {
289                 lex.printError("Missing \\end_inset at this point. "
290                                "Read: `$$Token'");
291                 throw ExceptionMessage(WarningException,
292                         _("Missing \\end_inset at this point."),
293                         from_utf8(token));
294         }
295 }
296
297
298 void InsetCommandParams::write(ostream & os) const
299 {
300         os << "CommandInset " << insetType() << '\n';
301         os << "LatexCommand " << cmdName_ << '\n';
302         if (preview_)
303                 os << "preview true\n";
304         for (size_t i = 0; i < info_->n; ++i)
305                 if (!params_[i].empty())
306                         // FIXME UNICODE
307                         os << info_->paramnames[i] << ' '
308                            << Lexer::quoteString(to_utf8(params_[i]))
309                            << '\n';
310 }
311
312
313 docstring const InsetCommandParams::getCommand() const
314 {
315         docstring s = '\\' + from_ascii(cmdName_);
316         bool noparam = true;
317         for (size_t i = 0; i < info_->n; ++i) {
318                 if (info_->optional[i]) {
319                         if (params_[i].empty()) {
320                                 // We need to write this parameter even if
321                                 // it is empty if nonempty optional parameters
322                                 // follow before the next required parameter.
323                                 for (size_t j = i + 1; j < info_->n; ++j) {
324                                         if (!info_->optional[j])
325                                                 break;
326                                         if (!params_[j].empty()) {
327                                                 s += "[]";
328                                                 noparam = false;
329                                                 break;
330                                         }
331                                 }
332                         } else {
333                                 s += '[' + params_[i] + ']';
334                                 noparam = false;
335                         }
336                 } else {
337                         s += '{' + params_[i] + '}';
338                         noparam = false;
339                 }
340         }
341         if (noparam)
342                 // Make sure that following stuff does not change the
343                 // command name.
344                 s += "{}";
345         return s;
346 }
347
348
349 docstring const InsetCommandParams::getFirstNonOptParam() const
350 {
351         for (size_t i = 0; i < info_->n; ++i)
352                 if (!info_->optional[i])
353                         return params_[i];
354         BOOST_ASSERT(false);
355         return docstring();
356 }
357
358
359 docstring const & InsetCommandParams::operator[](string const & name) const
360 {
361         int const i = findToken(info_->paramnames, name);
362         BOOST_ASSERT(i >= 0);
363         return params_[i];
364 }
365
366
367 docstring & InsetCommandParams::operator[](string const & name)
368 {
369         int const i = findToken(info_->paramnames, name);
370         BOOST_ASSERT(i >= 0);
371         return params_[i];
372 }
373
374
375 void InsetCommandParams::clear()
376 {
377         for (size_t i = 0; i < info_->n; ++i)
378                 params_[i].clear();
379 }
380
381
382 bool operator==(InsetCommandParams const & o1,
383                 InsetCommandParams const & o2)
384 {
385         return o1.insetCode_ == o2.insetCode_ &&
386                o1.cmdName_ == o2.cmdName_ &&
387                o1.info_ == o2.info_ &&
388                o1.params_ == o2.params_ &&
389                o1.preview_ == o2.preview_;
390 }
391
392
393 bool operator!=(InsetCommandParams const & o1,
394                 InsetCommandParams const & o2)
395 {
396         return !(o1 == o2);
397 }
398
399
400 } // namespace lyx