]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommand.cpp
Amend 6c3447c8: FindAdv: sometimes a space is added on some math symbols
[lyx.git] / src / insets / InsetCommand.cpp
1 /**
2  * \file InsetCommand.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 Lars Gullik Bjønnes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetCommand.h"
15
16 #include "Buffer.h"
17 #include "BufferEncodings.h"
18 #include "BufferParams.h"
19 #include "BufferView.h"
20 #include "Cursor.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
23 #include "InsetIterator.h"
24 #include "LyX.h"
25 #include "MetricsInfo.h"
26 #include "texstream.h"
27
28 #include "insets/InsetBox.h"
29 #include "insets/InsetBranch.h"
30 #include "insets/InsetERT.h"
31 #include "insets/InsetExternal.h"
32 #include "insets/InsetFloat.h"
33 #include "insets/InsetGraphics.h"
34 #include "insets/InsetIndex.h"
35 #include "insets/InsetListings.h"
36 #include "insets/InsetNote.h"
37 #include "insets/InsetPhantom.h"
38 #include "insets/InsetSpace.h"
39 #include "insets/InsetVSpace.h"
40 #include "insets/InsetWrap.h"
41
42 #include "support/debug.h"
43 #include "support/Lexer.h"
44 #include "support/lstrings.h"
45
46 #include "frontends/Application.h"
47
48 #include <sstream>
49
50 using namespace std;
51 using namespace lyx::support;
52
53
54 namespace lyx {
55
56 // FIXME Would it now be possible to use the InsetCode in
57 // place of the mailer name and recover that information?
58 InsetCommand::InsetCommand(Buffer * buf, InsetCommandParams const & p)
59         : Inset(buf), p_(p), broken_(false)
60 {}
61
62
63 // The sole purpose of this copy constructor is to make sure
64 // that the mouse_hover_ map is not copied and remains empty.
65 InsetCommand::InsetCommand(InsetCommand const & rhs)
66         : Inset(rhs), p_(rhs.p_), broken_(false)
67 {}
68
69
70 InsetCommand & InsetCommand::operator=(InsetCommand const & rhs)
71 {
72         if (&rhs == this)
73                 return *this;
74
75         Inset::operator=(rhs);
76         p_ = rhs.p_;
77         mouse_hover_.clear();
78         button_ = RenderButton();
79         broken_ = false;
80
81         return *this;
82 }
83
84
85 InsetCommand::~InsetCommand()
86 {
87         if (p_.code() != NO_CODE)
88                 hideDialogs(insetName(p_.code()), this);
89
90         map<BufferView const *, bool>::iterator it = mouse_hover_.begin();
91         map<BufferView const *, bool>::iterator end = mouse_hover_.end();
92         for (; it != end; ++it)
93                 if (it->second)
94                         it->first->clearLastInset(this);
95 }
96
97
98 void InsetCommand::metrics(MetricsInfo & mi, Dimension & dim) const
99 {
100         button_.update(screenLabel(), editable() || clickable(*mi.base.bv, 0, 0),
101                        inheritFont(), broken_);
102         button_.metrics(mi, dim);
103 }
104
105
106 bool InsetCommand::setMouseHover(BufferView const * bv, bool mouse_hover)
107         const
108 {
109         mouse_hover_[bv] = mouse_hover;
110         return true;
111 }
112
113
114 void InsetCommand::draw(PainterInfo & pi, int x, int y) const
115 {
116         button_.setRenderState(mouse_hover_[pi.base.bv]);
117         button_.draw(pi, x, y);
118 }
119
120
121 void InsetCommand::setParam(string const & name, docstring const & value)
122 {
123         p_[name] = value;
124 }
125
126
127 docstring const & InsetCommand::getParam(string const & name) const
128 {
129         return p_[name];
130 }
131
132
133 void InsetCommand::setParams(InsetCommandParams const & p)
134 {
135         p_ = p;
136         initView();
137 }
138
139
140 void InsetCommand::latex(otexstream & os, OutputParams const & runparams_in) const
141 {
142         OutputParams runparams = runparams_in;
143         docstring command = getCommand(runparams);
144         if (buffer().params().use_minted
145             && prefixIs(command, from_ascii("\\lstlistoflistings")))
146                 command.erase(1, 3);
147         os << command;
148 }
149
150
151 int InsetCommand::plaintext(odocstringstream & os,
152         OutputParams const &, size_t) const
153 {
154         docstring const str = "[" + buffer().B_("LaTeX Command: ")
155                 + from_utf8(getCmdName()) + "]";
156         os << str;
157         return str.size();
158 }
159
160
161 void InsetCommand::docbook(XMLStream &, OutputParams const &) const
162 {
163         return;
164 }
165
166
167 void InsetCommand::validate(LaTeXFeatures & features) const
168 {
169         if (params().info().hasParam("literal")
170             && params()["literal"] == "true")
171                 return;
172
173         ParamInfo::const_iterator it = params().info().begin();
174         ParamInfo::const_iterator end = params().info().end();
175         for (; it != end; ++it) {
176                 if (it->handling() == ParamInfo::HANDLING_LATEXIFY) {
177                         docstring const text = params()[it->name()];
178                         // Validate the contents (if we LaTeXify, specific
179                         // macros might require packages)
180                         for (pos_type i = 0; i < int(text.size()) ; ++i)
181                                 BufferEncodings::validate(text[i], features);
182                 }
183         }
184 }
185
186
187 bool InsetCommand::isChangedByCurrentAuthor() const
188 {
189         InsetIterator it = begin(buffer().inset());
190         InsetIterator const itend = end(buffer().inset());
191         for (; it != itend; ++it) {
192                 if (&*it == this)
193                         break;
194         }
195         if (it == itend) {
196                 LYXERR0("Unable to find inset!");
197                 // to be on the safe side.
198                 return true;
199         }
200         Paragraph const & ourpara = it.paragraph();
201         pos_type const ourpos = it.pos();
202         Change const & change = ourpara.lookupChange(ourpos);
203         return change.currentAuthor();
204 }
205
206
207 void InsetCommand::changeCmdName(string const & new_name)
208 {
209         string const & old_name = getCmdName();
210         if (old_name == new_name)
211                 return;
212
213         if (buffer().masterParams().track_changes) {
214                 // With change tracking, we insert a new inset and
215                 // delete the old one.
216                 // But we need to make sure that the inset isn't one
217                 // that the current author inserted. Otherwise, we might
218                 // delete ourselves!
219                 if (isChangedByCurrentAuthor()) {
220                         p_.setCmdName(new_name);
221                         return;
222                 }
223
224                 // OK, so this is not an inset the current author inserted
225                 InsetCommandParams p(p_.code());
226                 p = p_;
227                 p.setCmdName(new_name);
228                 string const data = InsetCommand::params2string(p);
229                 lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, data));
230                 lyx::dispatch(FuncRequest(LFUN_CHAR_DELETE_FORWARD));
231         } else
232                 p_.setCmdName(new_name);
233 }
234
235
236 void InsetCommand::doDispatch(Cursor & cur, FuncRequest & cmd)
237 {
238         switch (cmd.action()) {
239         case LFUN_INSET_MODIFY: {
240                 if (cmd.getArg(0) == "changetype") {
241                         cur.recordUndo();
242                         changeCmdName(cmd.getArg(1));
243                         cur.forceBufferUpdate();
244                         initView();
245                         break;
246                 }
247                 InsetCommandParams p(p_.code());
248                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
249                 if (p == p_)
250                         // no change
251                         break;
252                 if (p.getCmdName().empty())
253                         cur.noScreenUpdate();
254                 else {
255                         cur.recordUndo();
256                         if (buffer().masterParams().track_changes && !isChangedByCurrentAuthor()) {
257                                 // With change tracking, we insert a new inset and
258                                 // delete the old one
259                                 string const data = InsetCommand::params2string(p);
260                                 lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, data));
261                                 lyx::dispatch(FuncRequest(LFUN_CHAR_DELETE_FORWARD));
262                                 cur.forceBufferUpdate();
263                                 break;
264                         } else
265                                 setParams(p);
266                 }
267                 // FIXME We might also want to check here if this one is in the TOC.
268                 // But I think most of those are labeled.
269                 if (isLabeled())
270                         cur.forceBufferUpdate();
271                 break;
272         }
273
274         case LFUN_INSET_DIALOG_UPDATE: {
275                 string const name = to_utf8(cmd.argument());
276                 cur.bv().updateDialog(name, params2string(params()));
277                 break;
278         }
279
280         default:
281                 Inset::doDispatch(cur, cmd);
282                 break;
283         }
284
285 }
286
287
288 bool InsetCommand::getStatus(Cursor & cur, FuncRequest const & cmd,
289         FuncStatus & status) const
290 {
291         switch (cmd.action()) {
292         // suppress these
293         case LFUN_ERT_INSERT:
294                 status.setEnabled(false);
295                 return true;
296
297         // we handle these
298         case LFUN_INSET_MODIFY:
299                 if (cmd.getArg(0) == "changetype") {
300                         string const newtype = cmd.getArg(1);
301                         status.setEnabled(p_.isCompatibleCommand(p_.code(), newtype));
302                         status.setOnOff(newtype == p_.getCmdName());
303                 }
304                 status.setEnabled(true);
305                 return true;
306
307         case LFUN_INSET_DIALOG_UPDATE:
308                 status.setEnabled(true);
309                 return true;
310
311         default:
312                 return Inset::getStatus(cur, cmd, status);
313         }
314 }
315
316
317 string InsetCommand::contextMenuName() const
318 {
319         return "context-" + insetName(p_.code());
320 }
321
322
323 bool InsetCommand::showInsetDialog(BufferView * bv) const
324 {
325         if (p_.code() != NO_CODE)
326                 bv->showDialog(insetName(p_.code()), params2string(p_),
327                         const_cast<InsetCommand *>(this));
328         return true;
329 }
330
331
332 bool InsetCommand::string2params(string const & data,
333         InsetCommandParams & params)
334 {
335         params.clear();
336         if (data.empty())
337                 return false;
338         // This happens when inset-insert is called without argument except for the
339         // inset type; ex:
340         // "inset-insert toc"
341         string const name = insetName(params.code());
342         if (data == name)
343                 return true;
344         istringstream dstream(data);
345         Lexer lex;
346         lex.setStream(dstream);
347         lex.setContext("InsetCommand::string2params");
348         lex >> name.c_str(); // check for name
349         lex >> "CommandInset";
350         params.read(lex);
351         return true;
352 }
353
354
355 string InsetCommand::params2string(InsetCommandParams const & params)
356 {
357         ostringstream data;
358         data << insetName(params.code()) << ' ';
359         params.write(data);
360         data << "\\end_inset\n";
361         return data.str();
362 }
363
364
365 bool decodeInsetParam(string const & name, string & data,
366         Buffer const & buffer)
367 {
368         InsetCode const code = insetCode(name);
369         switch (code) {
370         case BIBITEM_CODE:
371         case BIBTEX_CODE:
372         case INDEX_PRINT_CODE:
373         case LABEL_CODE:
374         case LINE_CODE:
375         case NOMENCL_CODE:
376         case NOMENCL_PRINT_CODE:
377         case REF_CODE:
378         case TOC_CODE:
379         case HYPERLINK_CODE:
380         case COUNTER_CODE: {
381                 InsetCommandParams p(code);
382                 data = InsetCommand::params2string(p);
383                 break;
384         }
385         case INCLUDE_CODE: {
386                 // data is the include type: one of "include",
387                 // "input", "verbatiminput" or "verbatiminput*"
388                 if (data.empty())
389                         // default type is requested
390                         data = "include";
391                 InsetCommandParams p(INCLUDE_CODE, data);
392                 data = InsetCommand::params2string(p);
393                 break;
394         }
395         case BOX_CODE: {
396                 // \c data == "Boxed" || "Frameless" etc
397                 InsetBoxParams p(data);
398                 data = InsetBox::params2string(p);
399                 break;
400         }
401         case BRANCH_CODE: {
402                 InsetBranchParams p;
403                 data = InsetBranch::params2string(p);
404                 break;
405         }
406         case CITE_CODE: {
407                 InsetCommandParams p(CITE_CODE);
408                 data = InsetCommand::params2string(p);
409                 break;
410         }
411         case ERT_CODE: {
412                 data = InsetERT::params2string(InsetCollapsible::Open);
413                 break;
414         }
415         case EXTERNAL_CODE: {
416                 InsetExternalParams p;
417                 data = InsetExternal::params2string(p, buffer);
418                 break;
419         }
420         case FLOAT_CODE:  {
421                 InsetFloatParams p;
422                 data = InsetFloat::params2string(p);
423                 break;
424         }
425         case INDEX_CODE: {
426                 InsetIndexParams p;
427                 data = InsetIndex::params2string(p);
428                 break;
429         }
430         case LISTINGS_CODE: {
431                 InsetListingsParams p;
432                 data = InsetListings::params2string(p);
433                 break;
434         }
435         case GRAPHICS_CODE: {
436                 InsetGraphicsParams p;
437                 data = InsetGraphics::params2string(p, buffer);
438                 break;
439         }
440         case MATH_SPACE_CODE: {
441                 InsetSpaceParams p(true);
442                 data = InsetSpace::params2string(p);
443                 break;
444         }
445         case NOTE_CODE: {
446                 InsetNoteParams p;
447                 data = InsetNote::params2string(p);
448                 break;
449         }
450         case PHANTOM_CODE: {
451                 InsetPhantomParams p;
452                 data = InsetPhantom::params2string(p);
453                 break;
454         }
455         case SPACE_CODE: {
456                 InsetSpaceParams p;
457                 data = InsetSpace::params2string(p);
458                 break;
459         }
460         case VSPACE_CODE: {
461                 VSpace space;
462                 data = InsetVSpace::params2string(space);
463                 break;
464         }
465         case WRAP_CODE: {
466                 InsetWrapParams p;
467                 data = InsetWrap::params2string(p);
468                 break;
469         }
470         default:
471                 return false;
472         } // end switch(code)
473         return true;
474 }
475
476 } // namespace lyx