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