]> git.lyx.org Git - lyx.git/blob - src/insets/InsetScript.cpp
Amend 6c3447c8: FindAdv: sometimes a space is added on some math symbols
[lyx.git] / src / insets / InsetScript.cpp
1 /**
2  * \file InsetScript.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Georg Baum
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetScript.h"
14
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "BufferView.h"
18 #include "Cursor.h"
19 #include "Dimension.h"
20 #include "DispatchResult.h"
21 #include "Exporter.h"
22 #include "FuncRequest.h"
23 #include "FuncStatus.h"
24 #include "LaTeXFeatures.h"
25 #include "LyXAction.h"
26 #include "MetricsInfo.h"
27 #include "output_docbook.h"
28 #include "output_xhtml.h"
29 #include "TextClass.h"
30 #include "TextMetrics.h"
31
32 #include "support/docstream.h"
33 #include "support/gettext.h"
34 #include "support/Lexer.h"
35 #include "support/lstrings.h"
36 #include "support/Translator.h"
37
38 #include "frontends/Application.h"
39 #include "frontends/FontMetrics.h"
40 #include "frontends/Painter.h"
41
42 #include <algorithm>
43
44 using namespace std;
45
46 namespace lyx {
47
48 using support::Lexer;
49
50 namespace {
51
52 typedef Translator<string, InsetScriptParams::Type> ScriptTranslator;
53 typedef Translator<docstring, InsetScriptParams::Type> ScriptTranslatorLoc;
54
55 ScriptTranslator const init_scripttranslator()
56 {
57         ScriptTranslator translator("subscript", InsetScriptParams::Subscript);
58         translator.addPair("superscript", InsetScriptParams::Superscript);
59         return translator;
60 }
61
62
63 ScriptTranslatorLoc const init_scripttranslator_loc()
64 {
65         ScriptTranslatorLoc translator(_("Subscript"), InsetScriptParams::Subscript);
66         translator.addPair(_("Superscript"), InsetScriptParams::Superscript);
67         return translator;
68 }
69
70
71 ScriptTranslator const & scripttranslator()
72 {
73         static ScriptTranslator const translator =
74             init_scripttranslator();
75         return translator;
76 }
77
78
79 ScriptTranslatorLoc const & scripttranslator_loc()
80 {
81         static ScriptTranslatorLoc const translator =
82             init_scripttranslator_loc();
83         return translator;
84 }
85
86 } // namespace
87
88
89 InsetScriptParams::InsetScriptParams()
90         : type(Subscript)
91 {}
92
93
94 void InsetScriptParams::write(ostream & os) const
95 {
96         string const label = scripttranslator().find(type);
97         os << "script " << label << "\n";
98 }
99
100
101 void InsetScriptParams::read(Lexer & lex)
102 {
103         string label;
104         lex >> label;
105         if (lex)
106                 type = scripttranslator().find(label);
107 }
108
109
110 int InsetScriptParams::shift(FontInfo const & font) const
111 {
112         frontend::FontMetrics const & fm = theFontMetrics(font);
113         switch (type) {
114         case Subscript:
115                 return fm.maxAscent() / 3;
116         case Superscript:
117                 return -fm.maxAscent() / 2;
118         }
119         // shut up compiler
120         return 0;
121 }
122
123
124 /////////////////////////////////////////////////////////////////////
125 //
126 // InsetScript
127 //
128 /////////////////////////////////////////////////////////////////////
129
130 InsetScript::InsetScript(Buffer * buf, InsetScriptParams const & params)
131         : InsetText(buf, InsetText::PlainLayout), params_(params)
132 {
133         setDrawFrame(false);
134 }
135
136
137 InsetScript::InsetScript(Buffer * buf, string const & label)
138         : InsetText(buf)
139 {
140         setDrawFrame(false);
141         params_.type = scripttranslator().find(label);
142 }
143
144
145 InsetScript::~InsetScript()
146 {
147 }
148
149
150 docstring InsetScript::layoutName() const
151 {
152         return from_ascii("Script:" + scripttranslator().find(params_.type));
153 }
154
155
156 void InsetScript::metrics(MetricsInfo & mi, Dimension & dim) const
157 {
158         int const shift = params_.shift(mi.base.font);
159         // Remember the value of the outser font, so that it can be used in cursorPos.
160         outer_font_ = mi.base.font;
161         Changer dummy = mi.base.changeScript();
162         InsetText::metrics(mi, dim);
163         dim.asc -= shift;
164         dim.des += shift;
165 }
166
167
168 void InsetScript::draw(PainterInfo & pi, int x, int y) const
169 {
170         int const shift = params_.shift(pi.base.font);
171         Changer dummy = pi.base.changeScript();
172         InsetText::draw(pi, x, y + shift);
173 }
174
175
176 void InsetScript::cursorPos(BufferView const & bv,
177                 CursorSlice const & sl, bool boundary, int & x, int & y) const
178 {
179         int const shift = params_.shift(outer_font_);
180         InsetText::cursorPos(bv, sl, boundary, x, y);
181         y += shift;
182 }
183
184
185 void InsetScript::write(ostream & os) const
186 {
187         params_.write(os);
188         text().write(os);
189 }
190
191
192 void InsetScript::read(Lexer & lex)
193 {
194         params_.read(lex);
195         InsetText::read(lex);
196 }
197
198
199 void InsetScript::edit(Cursor & cur, bool front, EntryDirection entry_from)
200 {
201         cur.push(*this);
202         InsetText::edit(cur, front, entry_from);
203 }
204
205
206 Inset * InsetScript::editXY(Cursor & cur, int x, int y)
207 {
208         cur.push(*this);
209         return InsetText::editXY(cur, x, y);
210 }
211
212 void InsetScript::doDispatch(Cursor & cur, FuncRequest & cmd)
213 {
214         switch (cmd.action()) {
215         case LFUN_INSET_MODIFY:
216                 cur.recordUndoInset(this);
217                 string2params(to_utf8(cmd.argument()), params_);
218                 break;
219         default:
220                 InsetText::doDispatch(cur, cmd);
221                 break;
222         }
223 }
224
225
226 bool InsetScript::insetAllowed(InsetCode code) const
227 {
228         switch (code) {
229         // code that is not allowed in a script
230         case BIBITEM_CODE:
231         case BIBTEX_CODE:
232         case BOX_CODE:
233         case BRANCH_CODE:
234         case CAPTION_CODE:
235         case COLLAPSIBLE_CODE:
236         case FLOAT_CODE:
237         case FLOAT_LIST_CODE:
238         case FOOT_CODE:
239         case INCLUDE_CODE:
240         case INDEX_PRINT_CODE:
241         case LISTINGS_CODE:
242         case MARGIN_CODE:
243         case MATH_MACRO_CODE:
244         case MATHMACRO_CODE:
245         case NEWLINE_CODE:
246         case NEWPAGE_CODE:
247         case NOMENCL_PRINT_CODE:
248         case QUOTE_CODE:
249         case PREVIEW_CODE:
250         case TABULAR_CODE:
251         case TOC_CODE:
252         case WRAP_CODE:
253                 return false;
254         default:
255                 return InsetText::insetAllowed(code);
256         }
257 }
258
259 bool InsetScript::getStatus(Cursor & cur, FuncRequest const & cmd,
260                 FuncStatus & flag) const
261 {
262         switch (cmd.action()) {
263         case LFUN_MATH_DISPLAY:
264         case LFUN_BOX_INSERT:
265         case LFUN_BRANCH_INSERT:
266         case LFUN_CAPTION_INSERT:
267         case LFUN_FLOAT_INSERT:
268         case LFUN_FLOAT_LIST_INSERT:
269         case LFUN_FLOAT_WIDE_INSERT:
270         case LFUN_FOOTNOTE_INSERT:
271         case LFUN_INDEX_PRINT:
272         case LFUN_LISTING_INSERT:
273         case LFUN_MARGINALNOTE_INSERT:
274         case LFUN_NEWLINE_INSERT:
275         case LFUN_NEWPAGE_INSERT:
276         case LFUN_NOMENCL_PRINT:
277         case LFUN_PREVIEW_INSERT:
278         case LFUN_QUOTE_INSERT:
279         case LFUN_TABULAR_INSERT:
280         case LFUN_TABULAR_STYLE_INSERT:
281         case LFUN_WRAP_INSERT:
282                 flag.setEnabled(false);
283                 return true;
284         case LFUN_INSET_MODIFY:
285                 flag.setEnabled(true);
286                 return true;
287         case LFUN_COMMAND_SEQUENCE: {
288                 // argument contains ';'-terminated commands
289                 string arg = to_utf8(cmd.argument());
290                 // prevent insertion of display math formulas like AMS align
291                 while (!arg.empty()) {
292                         string first;
293                         arg = support::split(arg, first, ';');
294                         FuncRequest func(lyxaction.lookupFunc(first));
295                         if (func.action() == LFUN_MATH_MUTATE) {
296                                 flag.setEnabled(false);
297                                 return true;
298                         }
299                 }
300                 break;
301         }
302         default:
303                 break;
304         }
305         return InsetText::getStatus(cur, cmd, flag);
306 }
307
308
309 docstring InsetScript::toolTip(BufferView const &, int, int) const
310 {
311         OutputParams rp(&buffer().params().encoding());
312         odocstringstream ods;
313         InsetText::plaintext(ods, rp, 200);
314         docstring content_tip = ods.str();
315         // shorten it if necessary
316         support::truncateWithEllipsis(content_tip, 200);
317         docstring res = scripttranslator_loc().find(params_.type);
318         if (!content_tip.empty())
319                 res += from_ascii(": ") + content_tip;
320         return res;
321 }
322
323
324 int InsetScript::plaintext(odocstringstream & os,
325         OutputParams const & runparams, size_t max_length) const
326 {
327         odocstringstream oss;
328         InsetText::plaintext(oss, runparams, max_length);
329         docstring const text = oss.str();
330         switch (params_.type) {
331         case InsetScriptParams::Subscript:
332                 if (text.size() == 1) {
333                         char_type const c = support::subscript(text[0]);
334                         if (c != text[0]) {
335                                 os.put(c);
336                                 return 0;
337                         }
338                 }
339                 os << '[' << buffer().B_("subscript") << ':';
340                 break;
341         case InsetScriptParams::Superscript:
342                 if (text.size() == 1) {
343                         char_type const c = support::superscript(text[0]);
344                         if (c != text[0]) {
345                                 os.put(c);
346                                 return 0;
347                         }
348                 }
349                 os << '[' << buffer().B_("superscript") << ':';
350                 break;
351         }
352         InsetText::plaintext(os, runparams, max_length);
353         os << ']';
354
355         return PLAINTEXT_NEWLINE;
356 }
357
358
359 void InsetScript::docbook(XMLStream & xs, OutputParams const & runparams) const
360 {
361         docstring cmdname;
362         switch (params_.type) {
363         case InsetScriptParams::Subscript:
364                 cmdname = from_ascii("subscript");
365                 break;
366         case InsetScriptParams::Superscript:
367                 cmdname = from_ascii("superscript");
368                 break;
369         }
370
371         xs << xml::StartTag(cmdname);
372         InsetText::docbook(xs, runparams);
373         xs << xml::EndTag(cmdname);
374 }
375
376
377 string InsetScript::contextMenuName() const
378 {
379         return "context-script";
380 }
381
382
383 string InsetScript::params2string(InsetScriptParams const & params)
384 {
385         ostringstream data;
386         data << "script ";
387         params.write(data);
388         return data.str();
389 }
390
391
392 void InsetScript::string2params(string const & in, InsetScriptParams & params)
393 {
394         params = InsetScriptParams();
395
396         if (in.empty())
397                 return;
398
399         istringstream data(in);
400         Lexer lex;
401         lex.setStream(data);
402         lex.setContext("InsetScript::string2params");
403         lex >> "script" >> "script";
404
405         params.read(lex);
406 }
407
408
409 } // namespace lyx