]> git.lyx.org Git - features.git/blob - src/insets/InsetArgument.cpp
a3cf1c5202971fd76cb2529dcf67cb5fb93028be
[features.git] / src / insets / InsetArgument.cpp
1 /**
2  * \file InsetArgument.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Martin Vermeer
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetArgument.h"
15
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "Cursor.h"
19 #include "FuncStatus.h"
20 #include "FuncRequest.h"
21 #include "InsetList.h"
22 #include "Language.h"
23 #include "Layout.h"
24 #include "Lexer.h"
25 #include "OutputParams.h"
26 #include "ParIterator.h"
27 #include "TexRow.h"
28 #include "texstream.h"
29 #include "TocBackend.h"
30
31 #include "support/convert.h"
32 #include "support/debug.h"
33 #include "support/docstream.h"
34 #include "support/gettext.h"
35 #include "support/lstrings.h"
36
37 using namespace std;
38
39 namespace lyx {
40
41
42 InsetArgument::InsetArgument(Buffer * buf, string const & name)
43     : InsetCollapsible(buf), name_(name), labelstring_(docstring()),
44       font_(inherit_font), labelfont_(inherit_font), decoration_(string()),
45       pass_thru_context_(false), pass_thru_local_(false), pass_thru_(false),
46       pass_thru_chars_(docstring())
47 {}
48
49
50 void InsetArgument::write(ostream & os) const
51 {
52         os << "Argument " << name_ << "\n";
53         InsetCollapsible::write(os);
54 }
55
56
57 void InsetArgument::read(Lexer & lex)
58 {
59         lex >> name_;
60         InsetCollapsible::read(lex);
61 }
62
63
64 void InsetArgument::updateBuffer(ParIterator const & it, UpdateType utype)
65 {
66         bool const insetlayout = !it.paragraph().layout().hasArgs();
67         Layout::LaTeXArgMap const args = insetlayout ?
68                 it.inset().getLayout().args() : it.paragraph().layout().args();
69         pass_thru_context_ = insetlayout ?
70                 it.inset().getLayout().isPassThru() : it.paragraph().layout().pass_thru;
71         // Record PassThru status in order to act on changes.
72         bool const former_pass_thru = pass_thru_;
73
74         // Handle pre 2.1 ArgInsets (lyx2lyx cannot classify them)
75         if (name_ == "999") {
76                 unsigned int const req = insetlayout ? it.inset().getLayout().requiredArgs()
77                                       : it.paragraph().layout().requiredArgs();
78                 unsigned int const opts = insetlayout ? it.inset().getLayout().optArgs()
79                                       : it.paragraph().layout().optArgs();
80                 unsigned int nr = 0;
81                 unsigned int ours = 0;
82                 InsetList::const_iterator parit = it.paragraph().insetList().begin();
83                 InsetList::const_iterator parend = it.paragraph().insetList().end();
84                 for (; parit != parend; ++parit) {
85                         if (parit->inset->lyxCode() == ARG_CODE) {
86                                 ++nr;
87                                 if (parit->inset == this)
88                                         ours = nr;
89                         }
90                 }
91                 bool done = false;
92                 unsigned int realopts = 0;
93                 if (nr > req) {
94                         // We have optional arguments
95                         realopts = nr - req;
96                         if (ours <= realopts) {
97                                 name_ = convert<string>(ours);
98                                 done = true;
99                         }
100                 }
101                 if (!done) {
102                         // This is a mandatory argument. We have to consider
103                         // non-given optional arguments for the numbering
104                         int offset = opts - realopts;
105                         ours += offset;
106                         name_ = convert<string>(ours);
107                 }
108         }
109         Layout::LaTeXArgMap::const_iterator const lait = args.find(name_);
110         caption_of_toc_ = string();
111         if (lait != args.end()) {
112                 docstring label = translateIfPossible((*lait).second.labelstring);
113                 docstring striplabel;
114                 support::rsplit(label, striplabel, '|');
115                 labelstring_ = striplabel.empty() ? label: striplabel;
116                 tooltip_ = translateIfPossible((*lait).second.tooltip);
117                 font_ = (*lait).second.font;
118                 labelfont_ = (*lait).second.labelfont;
119                 decoration_ = (*lait).second.decoration;
120                 pass_thru_chars_ = (*lait).second.pass_thru_chars;
121                 pass_thru_local_ = false;
122                 if (lait->second.is_toc_caption)
123                         // empty if AddToToc is not set
124                         caption_of_toc_ = insetlayout
125                                 ? it.inset().getLayout().tocType()
126                                 : it.paragraph().layout().tocType();
127
128                 switch ((*lait).second.passthru) {
129                         case PT_INHERITED:
130                                 pass_thru_ = pass_thru_context_;
131                                 break;
132                         case PT_TRUE:
133                                 pass_thru_ = true;
134                                 pass_thru_local_ = true;
135                                 break;
136                         case PT_FALSE:
137                                 pass_thru_ = false;
138                                 break;
139                 }
140         } else {
141                 labelstring_ = _("Unknown Argument");
142                 tooltip_ = _("Argument not known in this Layout. Will be suppressed in the output.");
143         }
144
145         if (former_pass_thru != pass_thru_) {
146                 // PassThru status changed. We might need to update
147                 // the language of the contents
148                 Language const * l  = insetlayout
149                         ? it.inset().buffer().language()
150                         : it.buffer()->language();
151                 fixParagraphLanguage(l);
152         }
153
154         setButtonLabel();
155         InsetCollapsible::updateBuffer(it, utype);
156 }
157
158
159 void InsetArgument::setButtonLabel()
160 {
161         setLabel(labelstring_);
162 }
163
164
165 docstring InsetArgument::toolTip(BufferView const & bv, int, int) const
166 {
167         if (isOpen(bv))
168                 return tooltip_;
169         return toolTipText(tooltip_ + from_ascii(":\n"));
170 }
171
172
173 void InsetArgument::doDispatch(Cursor & cur, FuncRequest & cmd)
174 {
175         switch (cmd.action()) {
176
177         case LFUN_INSET_MODIFY: {
178                 string const first_arg = cmd.getArg(0);
179                 bool const change_type = first_arg == "changetype";
180                 if (!change_type) {
181                         // not for us
182                         // this will not be handled higher up
183                         cur.undispatched();
184                         return;
185                 }
186                 cur.recordUndoInset(this);
187                 name_ = cmd.getArg(1);
188                 cur.forceBufferUpdate();
189                 break;
190         }
191
192         case LFUN_PASTE:
193         case LFUN_CLIPBOARD_PASTE:
194         case LFUN_SELECTION_PASTE:
195         case LFUN_PRIMARY_SELECTION_PASTE:
196         case LFUN_SELF_INSERT:
197                 // With (only) inherited pass_thru, call Text::dispatch()
198                 // directly to avoid call for fixParagraphsFont() and/or
199                 // forcing to latex_language in InsetText::dispatch(),
200                 // since this does not play nicely with inherited pass_thru
201                 // (see #8471).
202                 if (pass_thru_ && !pass_thru_local_) {
203                         text().dispatch(cur, cmd);
204                         // For the paste operations, check if we have
205                         // non-latex_language, and if so, fix.
206                         if (cmd.action() != LFUN_SELF_INSERT)
207                                 fixParagraphLanguage(buffer().params().language);
208                 }
209                 else
210                         InsetCollapsible::doDispatch(cur, cmd);
211                 break;
212
213         default:
214                 InsetCollapsible::doDispatch(cur, cmd);
215                 break;
216         }
217 }
218
219
220 bool InsetArgument::getStatus(Cursor & cur, FuncRequest const & cmd,
221                 FuncStatus & flag) const
222 {
223         switch (cmd.action()) {
224
225         case LFUN_INSET_MODIFY: {
226                 string const first_arg = cmd.getArg(0);
227                 if (first_arg == "changetype") {
228                         string const type = cmd.getArg(1);
229                         flag.setOnOff(type == name_);
230                         if (type == name_) {
231                                 flag.setEnabled(true);
232                                 return true;
233                         }
234                         Layout::LaTeXArgMap args;
235                         bool const insetlayout = cur.paragraph().layout().latexargs().empty();
236                         if (insetlayout)
237                                 args = cur.inset().getLayout().latexargs();
238                         else
239                                 args = cur.paragraph().layout().latexargs();
240                         Layout::LaTeXArgMap::const_iterator const lait = args.find(type);
241                         if (lait != args.end()) {
242                                 flag.setEnabled(true);
243                                 for (auto const & table : cur.paragraph().insetList())
244                                         if (InsetArgument const * ins = table.inset->asInsetArgument())
245                                                 if (ins->name() == type) {
246                                                         // we have this already
247                                                         flag.setEnabled(false);
248                                                         return true;
249                                                 }
250                         } else
251                                 flag.setEnabled(false);
252                         return true;
253                 }
254                 return InsetCollapsible::getStatus(cur, cmd, flag);
255         }
256
257         default:
258                 return InsetCollapsible::getStatus(cur, cmd, flag);
259         }
260 }
261
262
263 string InsetArgument::contextMenuName() const
264 {
265         if (decoration() == InsetLayout::CONGLOMERATE)
266                 return "context-argument-conglomerate";
267         else
268                 return "context-argument";
269 }
270
271
272 FontInfo InsetArgument::getFont() const
273 {
274         if (font_ != inherit_font)
275                 return font_;
276         return InsetCollapsible::getFont();
277 }
278
279
280 FontInfo InsetArgument::getLabelfont() const
281 {
282         if (labelfont_ != inherit_font)
283                 return labelfont_;
284         return InsetCollapsible::getLabelfont();
285 }
286
287
288 ColorCode InsetArgument::labelColor() const {
289         if (labelfont_.color() != Color_inherit)
290                 return labelfont_.color();
291         return InsetCollapsible::labelColor();
292 }
293
294
295 InsetLayout::InsetDecoration InsetArgument::decoration() const
296 {
297         InsetLayout::InsetDecoration dec = getLayout().decoration();
298         if (!decoration_.empty())
299                 dec = translateDecoration(decoration_);
300         return dec == InsetLayout::DEFAULT ? InsetLayout::CLASSIC : dec;
301 }
302
303
304 void InsetArgument::latexArgument(otexstream & os,
305                 OutputParams const & runparams_in, docstring const & ldelim,
306                 docstring const & rdelim, docstring const & presetarg) const
307 {
308         otexstringstream ots;
309         OutputParams runparams = runparams_in;
310         if (!pass_thru_chars_.empty())
311                 runparams.pass_thru_chars += pass_thru_chars_;
312         runparams.pass_thru = isPassThru();
313         InsetText::latex(ots, runparams);
314         TexString ts = ots.release();
315         bool const add_braces = ldelim != "{" && support::contains(ts.str, rdelim);
316         os << ldelim;
317         if (add_braces)
318                 os << '{';
319         os << presetarg;
320         if (!presetarg.empty() && !ts.str.empty())
321                 os << ", ";
322         os << move(ts);
323         if (add_braces)
324                 os << '}';
325         os << rdelim;
326 }
327
328
329 void InsetArgument::addToToc(DocIterator const & dit, bool output_active,
330                              UpdateType utype, TocBackend & backend) const
331 {
332         if (!caption_of_toc_.empty()) {
333                 docstring str;
334                 text().forOutliner(str, TOC_ENTRY_LENGTH);
335                 backend.builder(caption_of_toc_).argumentItem(str);
336         }
337         // Proceed with the rest of the inset.
338         InsetText::addToToc(dit, output_active, utype, backend);
339 }
340
341
342 void InsetArgument::fixParagraphLanguage(Language const * l)
343 {
344         Font font(inherit_font, l);
345         if (pass_thru_)
346                 font.setLanguage(latex_language);
347         paragraphs().front().resetFonts(font);
348 }
349
350
351 } // namespace lyx