]> git.lyx.org Git - lyx.git/blob - src/insets/insetcommandparams.C
Use UTF8 for LaTeX export.
[lyx.git] / src / insets / insetcommandparams.C
1 /**
2  * \file insetcommandparams.C
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 "lyxlex.h"
18
19 #include "support/lstrings.h"
20
21 #include <boost/assert.hpp>
22
23
24 using lyx::docstring;
25 using lyx::support::findToken;
26
27 using std::string;
28 using std::endl;
29 using std::ostream;
30
31
32 InsetCommandParams::InsetCommandParams(string const & name)
33         : name_(name), preview_(false)
34 {
35         info_ = findInfo(name);
36         BOOST_ASSERT(info_);
37         params_.resize(info_->n);
38 }
39
40
41 InsetCommandParams::CommandInfo const *
42 InsetCommandParams::findInfo(std::string const & name)
43 {
44         // No parameter may be named "preview", because that is a required
45         // flag for all commands.
46
47         // InsetBibitem
48         if (name == "bibitem") {
49                 static const char * const paramnames[] = {"label", "key", ""};
50                 static const bool isoptional[] = {true, false};
51                 static const CommandInfo info = {2, paramnames, isoptional};
52                 return &info;
53         }
54
55         // InsetBibtex
56         if (name == "bibtex") {
57                 static const char * const paramnames[] =
58                                 {"options", "btprint", "bibfiles", ""};
59                 static const bool isoptional[] = {true, true, false};
60                 static const CommandInfo info = {3, paramnames, isoptional};
61                 return &info;
62         }
63
64         // InsetCitation
65         // FIXME: Use is_possible_cite_command() in
66         // src/frontends/controllers/biblio.C, see comment in src/factory.C.
67         if (name == "cite" || name == "citet" || name == "citep" || name == "citealt" ||
68             name == "citealp" || name == "citeauthor" || name == "citeyear" ||
69             name == "citeyearpar" || name == "citet*" || name == "citep*" ||
70             name == "citealt*" || name == "citealp*" ||
71             name == "citeauthor*" || name == "Citet" || name == "Citep" ||
72             name == "Citealt" || name == "Citealp" || name == "Citeauthor" ||
73             name == "Citet*" || name == "Citep*" || name == "Citealt*" ||
74             name == "Citealp*" || name == "Citeauthor*" ||
75             name == "citefield" || name == "citetitle" || name == "cite*") {
76                 // standard cite does only take one argument if jurabib is
77                 // not used, but jurabib extends this to two arguments, so
78                 // we have to allow both here. InsetCitation takes care that
79                 // LaTeX output is nevertheless correct.
80                 static const char * const paramnames[] =
81                                 {"after", "before", "key", ""};
82                 static const bool isoptional[] = {true, true, false};
83                 static const CommandInfo info = {3, paramnames, isoptional};
84                 return &info;
85         }
86
87         // InsetFloatlist
88         if (name == "floatlist") {
89                 static const char * const paramnames[] = {"type", ""};
90                 static const bool isoptional[] = {false};
91                 static const CommandInfo info = {1, paramnames, isoptional};
92                 return &info;
93         }
94
95         // InsetHfill
96         if (name == "hfill") {
97                 static const char * const paramnames[] = {""};
98                 static const CommandInfo info = {0, paramnames, 0};
99                 return &info;
100         }
101
102         // InsetInclude
103         if (name == "include" || name == "input" || name == "verbatiminput" ||
104             name == "verbatiminput*") {
105                 static const char * const paramnames[] = {"filename", ""};
106                 static const bool isoptional[] = {false};
107                 static const CommandInfo info = {1, paramnames, isoptional};
108                 return &info;
109         }
110
111         // InsetIndex, InsetPrintIndex, InsetLabel
112         if (name == "index" || name == "printindex" || name == "label") {
113                 static const char * const paramnames[] = {"name", ""};
114                 static const bool isoptional[] = {false};
115                 static const CommandInfo info = {1, paramnames, isoptional};
116                 return &info;
117         }
118
119         // InsetRef
120         if (name == "eqref" || name == "pageref" || name == "vpageref" ||
121             name == "vref" || name == "prettyref" || name == "ref") {
122                 static const char * const paramnames[] =
123                                 {"name", "reference", ""};
124                 static const bool isoptional[] = {true, false};
125                 static const CommandInfo info = {2, paramnames, isoptional};
126                 return &info;
127         }
128
129         // InsetTOC
130         if (name == "tableofcontents") {
131                 static const char * const paramnames[] = {"type", ""};
132                 static const bool isoptional[] = {false};
133                 static const CommandInfo info = {1, paramnames, isoptional};
134                 return &info;
135         }
136
137         // InsetUrl
138         if (name == "htmlurl" || name == "url") {
139                 static const char * const paramnames[] =
140                                 {"name", "target", ""};
141                 static const bool isoptional[] = {true, false};
142                 static const CommandInfo info = {2, paramnames, isoptional};
143                 return &info;
144         }
145
146         return 0;
147 }
148
149
150 void InsetCommandParams::setCmdName(string const & name)
151 {
152         name_ = name;
153         CommandInfo const * const info = findInfo(name);
154         BOOST_ASSERT(info);
155         ParamVector params(info->n);
156         // Overtake parameters with the same name
157         for (size_t i = 0; i < info_->n; ++i) {
158                 int j = findToken(info->paramnames, info_->paramnames[i]);
159                 if (j >= 0)
160                         params[j] = params_[i];
161         }
162         info_ = info;
163         std::swap(params, params_);
164 }
165
166
167 void InsetCommandParams::scanCommand(string const & cmd)
168 {
169         string tcmdname, toptions, tsecoptions, tcontents;
170
171         if (cmd.empty()) return;
172
173         enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
174
175         // Used to handle things like \command[foo[bar]]{foo{bar}}
176         int nestdepth = 0;
177
178         for (string::size_type i = 0; i < cmd.length(); ++i) {
179                 char const c = cmd[i];
180                 if ((state == CMDNAME && c == ' ') ||
181                     (state == CMDNAME && c == '[') ||
182                     (state == CMDNAME && c == '{')) {
183                         state = WS;
184                 }
185                 if ((state == OPTION  && c == ']') ||
186                     (state == SECOPTION  && c == ']') ||
187                     (state == CONTENT && c == '}')) {
188                         if (nestdepth == 0) {
189                                 state = WS;
190                         } else {
191                                 --nestdepth;
192                         }
193                 }
194                 if ((state == OPTION  && c == '[') ||
195                     (state == SECOPTION  && c == '[') ||
196                     (state == CONTENT && c == '{')) {
197                         ++nestdepth;
198                 }
199                 switch (state) {
200                 case CMDNAME:   tcmdname += c; break;
201                 case OPTION:    toptions += c; break;
202                 case SECOPTION: tsecoptions += c; break;
203                 case CONTENT:   tcontents += c; break;
204                 case WS: {
205                         char const b = i? cmd[i-1]: 0;
206                         if (c == '\\') {
207                                 state = CMDNAME;
208                         } else if (c == '[' && b != ']') {
209                                 state = OPTION;
210                                 nestdepth = 0; // Just to be sure
211                         } else if (c == '[' && b == ']') {
212                                 state = SECOPTION;
213                                 nestdepth = 0; // Just to be sure
214                         } else if (c == '{') {
215                                 state = CONTENT;
216                                 nestdepth = 0; // Just to be sure
217                         }
218                         break;
219                 }
220                 }
221         }
222
223         // Don't mess with this.
224         if (!tcmdname.empty())  setCmdName(tcmdname);
225         if (!toptions.empty())  setOptions(toptions);
226         if (!tsecoptions.empty())  setSecOptions(tsecoptions);
227         if (!tcontents.empty()) setContents(tcontents);
228
229         if (lyxerr.debugging(Debug::PARSER))
230                 lyxerr << "Command <" <<  cmd
231                        << "> == <" << lyx::to_utf8(getCommand())
232                        << "> == <" << getCmdName()
233                        << '|' << getContents()
234                        << '|' << getOptions()
235                        << '|' << getSecOptions() << '>' << endl;
236 }
237
238
239 void InsetCommandParams::read(LyXLex & lex)
240 {
241         if (lex.isOK()) {
242                 lex.next();
243                 name_ = lex.getString();
244                 info_ = findInfo(name_);
245                 if (!info_)
246                         lex.printError("InsetCommand: Unknown inset name `$$Token'");
247         }
248
249         string token;
250         while (lex.isOK()) {
251                 lex.next();
252                 token = lex.getString();
253                 if (token == "\\end_inset")
254                         break;
255                 // FIXME Why is preview_ read but not written?
256                 if (token == "preview") {
257                         lex.next();
258                         preview_ = lex.getBool();
259                         continue;
260                 }
261                 int const i = findToken(info_->paramnames, token);
262                 if (i >= 0) {
263                         lex.next(true);
264                         params_[i] = lex.getDocString();
265                 } else
266                         lex.printError("Unknown parameter name `$$Token' for command " + name_);
267         }
268         if (token != "\\end_inset") {
269                 lex.printError("Missing \\end_inset at this point. "
270                                "Read: `$$Token'");
271         }
272 }
273
274
275 void InsetCommandParams::write(ostream & os) const
276 {
277         os << "LatexCommand " << name_ << '\n';
278         for (size_t i = 0; i < info_->n; ++i)
279                 if (!params_[i].empty())
280                         // FIXME UNICODE
281                         os << info_->paramnames[i] << ' '
282                            << LyXLex::quoteString(lyx::to_utf8(params_[i]))
283                            << '\n';
284 }
285
286
287 docstring const InsetCommandParams::getCommand() const
288 {
289         docstring s = '\\' + lyx::from_ascii(name_);
290         for (size_t i = 0; i < info_->n; ++i) {
291                 if (info_->optional[i]) {
292                         if (params_[i].empty()) {
293                                 // We need to write this parameter even if
294                                 // it is empty if nonempty optional parameters
295                                 // follow before the next required parameter.
296                                 for (size_t j = i + 1; j < info_->n; ++j) {
297                                         if (!info_->optional[j])
298                                                 break;
299                                         if (!params_[j].empty()) {
300                                                 s += "[]";
301                                                 break;
302                                         }
303                                 }
304                         } else
305                                 s += '[' + params_[i] + ']';
306                 } else
307                         s += '{' + params_[i] + '}';
308         }
309         return s;
310 }
311
312
313 std::string const InsetCommandParams::getOptions() const
314 {
315         for (size_t i = 0; i < info_->n; ++i)
316                 if (info_->optional[i])
317                         return lyx::to_utf8(params_[i]);
318         lyxerr << "Programming error: get nonexisting option in "
319                << name_ << " inset." << endl;; 
320         return string();
321 }
322
323
324 std::string const InsetCommandParams::getSecOptions() const
325 {
326         bool first = true;
327         for (size_t i = 0; i < info_->n; ++i)
328                 if (info_->optional[i]) {
329                         if (first)
330                                 first = false;
331                         else
332                                 return lyx::to_utf8(params_[i]);
333                 }
334         // Happens in InsetCitation
335         lyxerr << "Programming error: get nonexisting second option in "
336                << name_ << " inset." << endl;; 
337         return string();
338 }
339
340
341 std::string const InsetCommandParams::getContents() const
342 {
343         for (size_t i = 0; i < info_->n; ++i)
344                 if (!info_->optional[i])
345                         return lyx::to_utf8(params_[i]);
346         BOOST_ASSERT(false);
347         return string();
348 }
349
350
351 void InsetCommandParams::setOptions(std::string const & o)
352 {
353         for (size_t i = 0; i < info_->n; ++i)
354                 if (info_->optional[i]) {
355                         params_[i] = lyx::from_utf8(o);
356                         return;
357                 }
358         lyxerr << "Programming error: set nonexisting option in "
359                << name_ << " inset." << endl;; 
360 }
361
362
363 void InsetCommandParams::setSecOptions(std::string const & s)
364 {
365         bool first = true;
366         for (size_t i = 0; i < info_->n; ++i)
367                 if (info_->optional[i]) {
368                         if (first)
369                                 first = false;
370                         else {
371                                 params_[i] = lyx::from_utf8(s);
372                                 return;
373                         }
374                 }
375         // Happens in InsetCitation
376         lyxerr << "Programming error: set nonexisting second option in "
377                << name_ << " inset." << endl;; 
378 }
379
380
381 void InsetCommandParams::setContents(std::string const & c)
382 {
383         for (size_t i = 0; i < info_->n; ++i)
384                 if (!info_->optional[i]) {
385                         params_[i] = lyx::from_utf8(c);
386                         return;
387                 }
388         BOOST_ASSERT(false);
389 }
390
391
392 docstring const & InsetCommandParams::operator[](string const & name) const
393 {
394         int const i = findToken(info_->paramnames, name);
395         BOOST_ASSERT(i >= 0);
396         return params_[i];
397 }
398
399
400 docstring & InsetCommandParams::operator[](string const & name)
401 {
402         int const i = findToken(info_->paramnames, name);
403         BOOST_ASSERT(i >= 0);
404         return params_[i];
405 }
406
407
408 void InsetCommandParams::clear()
409 {
410         for (size_t i = 0; i < info_->n; ++i)
411                 params_[i].clear();
412 }
413
414
415 bool operator==(InsetCommandParams const & o1,
416                 InsetCommandParams const & o2)
417 {
418         return o1.name_ == o2.name_ &&
419                o1.info_ == o2.info_ &&
420                o1.params_ == o2.params_ &&
421                o1.preview_ == o2.preview_;
422 }
423
424
425 bool operator!=(InsetCommandParams const & o1,
426                 InsetCommandParams const & o2)
427 {
428         return !(o1 == o2);
429 }