3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup Nielsen
7 * \author Jean-Marc Lasgouttes
8 * \author Lars Gullik Bjønnes
9 * \author Jürgen Spitzmüller
11 * Full author contact details are available in file CREDITS.
16 #include "InsetSpace.h"
18 #include "BufferView.h"
20 #include "Dimension.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
25 #include "MetricsInfo.h"
26 #include "OutputParams.h"
28 #include "support/debug.h"
29 #include "support/docstream.h"
30 #include "support/gettext.h"
31 #include "support/lstrings.h"
33 #include "frontends/Application.h"
34 #include "frontends/FontMetrics.h"
35 #include "frontends/Painter.h"
42 InsetSpace::InsetSpace(InsetSpaceParams const & params)
47 InsetSpaceParams::Kind InsetSpace::kind() const
53 Length InsetSpace::length() const
55 return params_.length;
59 InsetSpace::~InsetSpace()
61 hideDialogs("space", this);
65 docstring InsetSpace::toolTip(BufferView const &, int, int) const
68 switch (params_.kind) {
69 case InsetSpaceParams::NORMAL:
70 message = _("Interword Space");
72 case InsetSpaceParams::PROTECTED:
73 message = _("Protected Space");
75 case InsetSpaceParams::THIN:
76 message = _("Thin Space");
78 case InsetSpaceParams::QUAD:
79 message = _("Quad Space");
81 case InsetSpaceParams::QQUAD:
82 message = _("QQuad Space");
84 case InsetSpaceParams::ENSPACE:
85 message = _("Enspace");
87 case InsetSpaceParams::ENSKIP:
88 message = _("Enskip");
90 case InsetSpaceParams::NEGTHIN:
91 message = _("Negative Thin Space");
93 case InsetSpaceParams::HFILL:
94 message = _("Horizontal Fill");
96 case InsetSpaceParams::HFILL_PROTECTED:
97 message = _("Protected Horizontal Fill");
99 case InsetSpaceParams::DOTFILL:
100 message = _("Horizontal Fill (Dots)");
102 case InsetSpaceParams::HRULEFILL:
103 message = _("Horizontal Fill (Rule)");
105 case InsetSpaceParams::LEFTARROWFILL:
106 message = _("Horizontal Fill (Left Arrow)");
108 case InsetSpaceParams::RIGHTARROWFILL:
109 message = _("Horizontal Fill (Right Arrow)");
111 case InsetSpaceParams::UPBRACEFILL:
112 message = _("Horizontal Fill (Up Brace)");
114 case InsetSpaceParams::DOWNBRACEFILL:
115 message = _("Horizontal Fill (Down Brace)");
117 case InsetSpaceParams::CUSTOM:
118 message = support::bformat(_("Horizontal Space (%1$s)"),
119 params_.length.asDocstring());
121 case InsetSpaceParams::CUSTOM_PROTECTED:
122 message = support::bformat(_("Protected Horizontal Space (%1$s)"),
123 params_.length.asDocstring());
130 void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
132 switch (cmd.action) {
134 case LFUN_INSET_MODIFY: {
135 string2params(to_utf8(cmd.argument()), params_);
139 case LFUN_MOUSE_RELEASE:
140 if (!cur.selection() && cmd.button() == mouse_button::button1)
141 cur.bv().showDialog("space", params2string(params()), this);
145 Inset::doDispatch(cur, cmd);
151 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
152 FuncStatus & status) const
154 switch (cmd.action) {
156 case LFUN_INSET_MODIFY:
157 if (cmd.getArg(0) == "space") {
158 InsetSpaceParams params;
159 string2params(to_utf8(cmd.argument()), params);
160 status.setOnOff(params_.kind == params.kind);
162 status.enabled(true);
166 return Inset::getStatus(cur, cmd, status);
171 void InsetSpace::edit(Cursor & cur, bool, EntryDirection)
173 cur.bv().showDialog("space", params2string(params()), this);
177 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
179 if (isStretchableSpace()) {
180 // The metrics for this kinds are calculated externally in
181 // \c TextMetrics::computeRowMetrics. Those are dummy value:
182 dim = Dimension(10, 10, 10);
186 frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
187 dim.asc = fm.maxAscent();
188 dim.des = fm.maxDescent();
190 switch (params_.kind) {
191 case InsetSpaceParams::THIN:
192 case InsetSpaceParams::NEGTHIN:
193 dim.wid = fm.width(char_type('M')) / 6;
195 case InsetSpaceParams::PROTECTED:
196 case InsetSpaceParams::NORMAL:
197 dim.wid = fm.width(char_type(' '));
199 case InsetSpaceParams::QUAD:
200 dim.wid = fm.width(char_type('M'));
202 case InsetSpaceParams::QQUAD:
203 dim.wid = 2 * fm.width(char_type('M'));
205 case InsetSpaceParams::ENSPACE:
206 case InsetSpaceParams::ENSKIP:
207 dim.wid = int(0.5 * fm.width(char_type('M')));
209 case InsetSpaceParams::CUSTOM:
210 case InsetSpaceParams::CUSTOM_PROTECTED:
211 dim.wid = params_.length.inBP();
213 case InsetSpaceParams::HFILL:
214 case InsetSpaceParams::HFILL_PROTECTED:
215 case InsetSpaceParams::DOTFILL:
216 case InsetSpaceParams::HRULEFILL:
217 case InsetSpaceParams::LEFTARROWFILL:
218 case InsetSpaceParams::RIGHTARROWFILL:
219 case InsetSpaceParams::UPBRACEFILL:
220 case InsetSpaceParams::DOWNBRACEFILL:
224 // Cache the inset dimension.
225 setDimCache(mi, dim);
229 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
231 Dimension const dim = dimension(*pi.base.bv);
233 if (isStretchableSpace()) {
234 int const asc = theFontMetrics(pi.base.font).ascent('M');
235 int const desc = theFontMetrics(pi.base.font).descent('M');
236 int const x0 = x + 1;
237 int const x1 = x + dim.wid - 2;
238 int const y0 = y + desc;
239 int const y1 = y - asc;
240 int const y2 = y - asc / 2;
241 int const xoffset = (y0 - y1) / 2;
242 int const x2 = x0 + xoffset;
243 int const x3 = x1 - xoffset;
245 if (params_.kind == InsetSpaceParams::HFILL) {
246 pi.pain.line(x0, y1, x0, y0, Color_added_space);
247 pi.pain.line(x0, y2 , x1, y2, Color_added_space,
248 frontend::Painter::line_onoffdash);
249 pi.pain.line(x1, y1, x1, y0, Color_added_space);
250 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
251 pi.pain.line(x0, y1, x0, y0, Color_latex);
252 pi.pain.line(x0, y2 , x1, y2, Color_latex,
253 frontend::Painter::line_onoffdash);
254 pi.pain.line(x1, y1, x1, y0, Color_latex);
255 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
256 pi.pain.line(x0, y1, x0, y0, Color_special);
257 pi.pain.line(x0, y, x1, y, Color_special,
258 frontend::Painter::line_onoffdash);
259 pi.pain.line(x1, y1, x1, y0, Color_special);
260 } else if (params_.kind == InsetSpaceParams::HRULEFILL) {
261 pi.pain.line(x0, y1, x0, y0, Color_special);
262 pi.pain.line(x0, y, x1, y, Color_special);
263 pi.pain.line(x1, y1, x1, y0, Color_special);
264 } else if (params_.kind == InsetSpaceParams::LEFTARROWFILL) {
265 pi.pain.line(x2, y1 , x0, y2, Color_special);
266 pi.pain.line(x0, y2 , x2, y0, Color_special);
267 pi.pain.line(x0, y2 , x1, y2, Color_special);
268 } else if (params_.kind == InsetSpaceParams::RIGHTARROWFILL) {
269 pi.pain.line(x3, y1 , x1, y2, Color_special);
270 pi.pain.line(x1, y2 , x3, y0, Color_special);
271 pi.pain.line(x0, y2 , x1, y2, Color_special);
272 } else if (params_.kind == InsetSpaceParams::UPBRACEFILL) {
273 pi.pain.line(x0, y1 , x2, y2, Color_special);
274 pi.pain.line(x3, y2 , x1, y1, Color_special);
275 pi.pain.line(x2, y2 , x3, y2, Color_special);
276 } else if (params_.kind == InsetSpaceParams::DOWNBRACEFILL) {
277 pi.pain.line(x0, y0 , x2, y2, Color_special);
278 pi.pain.line(x3, y2 , x1, y0, Color_special);
279 pi.pain.line(x2, y2 , x3, y2, Color_special);
284 int const w = dim.wid;
285 int const h = theFontMetrics(pi.base.font).ascent('x');
289 yp[0] = y - max(h / 4, 1);
290 if (params_.kind == InsetSpaceParams::NORMAL ||
291 params_.kind == InsetSpaceParams::PROTECTED) {
292 xp[1] = x; yp[1] = y;
293 xp[2] = x + w; yp[2] = y;
295 xp[1] = x; yp[1] = y + max(h / 4, 1);
296 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
299 yp[3] = y - max(h / 4, 1);
301 if (params_.kind == InsetSpaceParams::PROTECTED ||
302 params_.kind == InsetSpaceParams::ENSPACE ||
303 params_.kind == InsetSpaceParams::NEGTHIN ||
304 params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
305 pi.pain.lines(xp, yp, 4, Color_latex);
307 pi.pain.lines(xp, yp, 4, Color_special);
311 void InsetSpaceParams::write(ostream & os) const
315 case InsetSpaceParams::NORMAL:
318 case InsetSpaceParams::PROTECTED:
321 case InsetSpaceParams::THIN:
322 os << "\\thinspace{}";
324 case InsetSpaceParams::QUAD:
327 case InsetSpaceParams::QQUAD:
330 case InsetSpaceParams::ENSPACE:
333 case InsetSpaceParams::ENSKIP:
336 case InsetSpaceParams::NEGTHIN:
337 os << "\\negthinspace{}";
339 case InsetSpaceParams::HFILL:
342 case InsetSpaceParams::HFILL_PROTECTED:
343 os << "\\hspace*{\\fill}";
345 case InsetSpaceParams::DOTFILL:
348 case InsetSpaceParams::HRULEFILL:
349 os << "\\hrulefill{}";
351 case InsetSpaceParams::LEFTARROWFILL:
352 os << "\\leftarrowfill{}";
354 case InsetSpaceParams::RIGHTARROWFILL:
355 os << "\\rightarrowfill{}";
357 case InsetSpaceParams::UPBRACEFILL:
358 os << "\\upbracefill{}";
360 case InsetSpaceParams::DOWNBRACEFILL:
361 os << "\\downbracefill{}";
363 case InsetSpaceParams::CUSTOM:
366 case InsetSpaceParams::CUSTOM_PROTECTED:
372 os << "\n\\length " << length.asString();
376 void InsetSpaceParams::read(Lexer & lex)
378 lex.setContext("InsetSpaceParams::read");
382 if (command == "\\space{}")
383 kind = InsetSpaceParams::NORMAL;
384 else if (command == "~")
385 kind = InsetSpaceParams::PROTECTED;
386 else if (command == "\\thinspace{}")
387 kind = InsetSpaceParams::THIN;
388 else if (command == "\\quad{}")
389 kind = InsetSpaceParams::QUAD;
390 else if (command == "\\qquad{}")
391 kind = InsetSpaceParams::QQUAD;
392 else if (command == "\\enspace{}")
393 kind = InsetSpaceParams::ENSPACE;
394 else if (command == "\\enskip{}")
395 kind = InsetSpaceParams::ENSKIP;
396 else if (command == "\\negthinspace{}")
397 kind = InsetSpaceParams::NEGTHIN;
398 else if (command == "\\hfill{}")
399 kind = InsetSpaceParams::HFILL;
400 else if (command == "\\hspace*{\\fill}")
401 kind = InsetSpaceParams::HFILL_PROTECTED;
402 else if (command == "\\dotfill{}")
403 kind = InsetSpaceParams::DOTFILL;
404 else if (command == "\\hrulefill{}")
405 kind = InsetSpaceParams::HRULEFILL;
406 else if (command == "\\hspace{}")
407 kind = InsetSpaceParams::CUSTOM;
408 else if (command == "\\leftarrowfill{}")
409 kind = InsetSpaceParams::LEFTARROWFILL;
410 else if (command == "\\rightarrowfill{}")
411 kind = InsetSpaceParams::RIGHTARROWFILL;
412 else if (command == "\\upbracefill{}")
413 kind = InsetSpaceParams::UPBRACEFILL;
414 else if (command == "\\downbracefill{}")
415 kind = InsetSpaceParams::DOWNBRACEFILL;
416 else if (command == "\\hspace*{}")
417 kind = InsetSpaceParams::CUSTOM_PROTECTED;
419 lex.printError("InsetSpace: Unknown kind: `$$Token'");
421 if (lex.checkFor("\\length"))
423 lex >> "\\end_inset";
427 void InsetSpace::write(ostream & os) const
434 void InsetSpace::read(Lexer & lex)
440 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
442 switch (params_.kind) {
443 case InsetSpaceParams::NORMAL:
444 os << (runparams.free_spacing ? " " : "\\ ");
446 case InsetSpaceParams::PROTECTED:
447 os << (runparams.free_spacing ? ' ' : '~');
449 case InsetSpaceParams::THIN:
450 os << (runparams.free_spacing ? " " : "\\,");
452 case InsetSpaceParams::QUAD:
453 os << (runparams.free_spacing ? " " : "\\quad{}");
455 case InsetSpaceParams::QQUAD:
456 os << (runparams.free_spacing ? " " : "\\qquad{}");
458 case InsetSpaceParams::ENSPACE:
459 os << (runparams.free_spacing ? " " : "\\enspace{}");
461 case InsetSpaceParams::ENSKIP:
462 os << (runparams.free_spacing ? " " : "\\enskip{}");
464 case InsetSpaceParams::NEGTHIN:
465 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
467 case InsetSpaceParams::HFILL:
468 os << (runparams.free_spacing ? " " : "\\hfill{}");
470 case InsetSpaceParams::HFILL_PROTECTED:
471 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
473 case InsetSpaceParams::DOTFILL:
474 os << (runparams.free_spacing ? " " : "\\dotfill{}");
476 case InsetSpaceParams::HRULEFILL:
477 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
479 case InsetSpaceParams::LEFTARROWFILL:
480 os << (runparams.free_spacing ? " " : "\\leftarrowfill{}");
482 case InsetSpaceParams::RIGHTARROWFILL:
483 os << (runparams.free_spacing ? " " : "\\rightarrowfill{}");
485 case InsetSpaceParams::UPBRACEFILL:
486 os << (runparams.free_spacing ? " " : "\\upbracefill{}");
488 case InsetSpaceParams::DOWNBRACEFILL:
489 os << (runparams.free_spacing ? " " : "\\downbracefill{}");
491 case InsetSpaceParams::CUSTOM:
492 if (runparams.free_spacing)
495 os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
497 case InsetSpaceParams::CUSTOM_PROTECTED:
498 if (runparams.free_spacing)
501 os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
508 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
510 switch (params_.kind) {
511 case InsetSpaceParams::HFILL:
512 case InsetSpaceParams::HFILL_PROTECTED:
515 case InsetSpaceParams::DOTFILL:
518 case InsetSpaceParams::HRULEFILL:
521 case InsetSpaceParams::LEFTARROWFILL:
524 case InsetSpaceParams::RIGHTARROWFILL:
527 case InsetSpaceParams::UPBRACEFILL:
530 case InsetSpaceParams::DOWNBRACEFILL:
540 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
542 switch (params_.kind) {
543 case InsetSpaceParams::NORMAL:
544 case InsetSpaceParams::QUAD:
545 case InsetSpaceParams::QQUAD:
546 case InsetSpaceParams::ENSKIP:
549 case InsetSpaceParams::PROTECTED:
550 case InsetSpaceParams::ENSPACE:
551 case InsetSpaceParams::THIN:
552 case InsetSpaceParams::NEGTHIN:
555 case InsetSpaceParams::HFILL:
556 case InsetSpaceParams::HFILL_PROTECTED:
558 case InsetSpaceParams::DOTFILL:
561 case InsetSpaceParams::HRULEFILL:
564 case InsetSpaceParams::LEFTARROWFILL:
565 case InsetSpaceParams::RIGHTARROWFILL:
566 case InsetSpaceParams::UPBRACEFILL:
567 case InsetSpaceParams::DOWNBRACEFILL:
568 case InsetSpaceParams::CUSTOM:
569 case InsetSpaceParams::CUSTOM_PROTECTED:
577 void InsetSpace::textString(odocstream & os) const
579 plaintext(os, OutputParams(0));
583 bool InsetSpace::isStretchableSpace() const
585 return params_.kind == InsetSpaceParams::HFILL
586 || params_.kind == InsetSpaceParams::HFILL_PROTECTED
587 || params_.kind == InsetSpaceParams::DOTFILL
588 || params_.kind == InsetSpaceParams::HRULEFILL
589 || params_.kind == InsetSpaceParams::LEFTARROWFILL
590 || params_.kind == InsetSpaceParams::RIGHTARROWFILL
591 || params_.kind == InsetSpaceParams::UPBRACEFILL
592 || params_.kind == InsetSpaceParams::DOWNBRACEFILL;
596 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
598 return from_ascii("context-space");
602 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
604 params = InsetSpaceParams();
608 istringstream data(in);
611 lex.setContext("InsetSpace::string2params");
618 string InsetSpace::params2string(InsetSpaceParams const & params)
621 data << "space" << ' ';