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.setEnabled(true);
165 return Inset::getStatus(cur, cmd, status);
170 void InsetSpace::edit(Cursor & cur, bool, EntryDirection)
172 cur.bv().showDialog("space", params2string(params()), this);
177 int const arrow_size = 8;
181 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
183 if (isStretchableSpace()) {
184 // The metrics for this kinds are calculated externally in
185 // \c TextMetrics::computeRowMetrics. Those are dummy value:
186 dim = Dimension(10, 10, 10);
190 frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
191 dim.asc = fm.maxAscent();
192 dim.des = fm.maxDescent();
194 switch (params_.kind) {
195 case InsetSpaceParams::THIN:
196 case InsetSpaceParams::NEGTHIN:
197 dim.wid = fm.width(char_type('M')) / 6;
199 case InsetSpaceParams::PROTECTED:
200 case InsetSpaceParams::NORMAL:
201 dim.wid = fm.width(char_type(' '));
203 case InsetSpaceParams::QUAD:
204 dim.wid = fm.width(char_type('M'));
206 case InsetSpaceParams::QQUAD:
207 dim.wid = 2 * fm.width(char_type('M'));
209 case InsetSpaceParams::ENSPACE:
210 case InsetSpaceParams::ENSKIP:
211 dim.wid = int(0.5 * fm.width(char_type('M')));
213 case InsetSpaceParams::CUSTOM:
214 case InsetSpaceParams::CUSTOM_PROTECTED: {
216 params_.length.inPixels(mi.base.textwidth,
217 fm.width(char_type('M')));
218 int const minw = (w < 0) ? 3 * arrow_size : 4;
219 dim.wid = max(minw, abs(w));
222 case InsetSpaceParams::HFILL:
223 case InsetSpaceParams::HFILL_PROTECTED:
224 case InsetSpaceParams::DOTFILL:
225 case InsetSpaceParams::HRULEFILL:
226 case InsetSpaceParams::LEFTARROWFILL:
227 case InsetSpaceParams::RIGHTARROWFILL:
228 case InsetSpaceParams::UPBRACEFILL:
229 case InsetSpaceParams::DOWNBRACEFILL:
233 // Cache the inset dimension.
234 setDimCache(mi, dim);
238 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
240 Dimension const dim = dimension(*pi.base.bv);
242 if (isStretchableSpace() || params_.length.value() < 0) {
243 int const asc = theFontMetrics(pi.base.font).ascent('M');
244 int const desc = theFontMetrics(pi.base.font).descent('M');
245 // Pixel height divisible by 2 for prettier fill graphics:
246 int const oddheight = (asc ^ desc) & 1;
247 int const x0 = x + 1;
248 int const x1 = x + dim.wid - 2;
249 int const y0 = y + desc - 1;
250 int const y1 = y - asc + oddheight - 1;
251 int const y2 = (y0 + y1) / 2;
252 int xoffset = (y0 - y1) / 2;
254 // Two tests for very narrow insets
255 if (xoffset > x1 - x0
256 && (params_.kind == InsetSpaceParams::LEFTARROWFILL
257 || params_.kind == InsetSpaceParams::RIGHTARROWFILL))
259 if (xoffset * 6 > (x1 - x0)
260 && (params_.kind == InsetSpaceParams::UPBRACEFILL
261 || params_.kind == InsetSpaceParams::DOWNBRACEFILL))
262 xoffset = (x1 - x0) / 6;
264 int const x2 = x0 + xoffset;
265 int const x3 = x1 - xoffset;
266 int const xm = (x0 + x1) / 2;
267 int const xml = xm - xoffset;
268 int const xmr = xm + xoffset;
270 if (params_.kind == InsetSpaceParams::HFILL) {
271 pi.pain.line(x0, y1, x0, y0, Color_added_space);
272 pi.pain.line(x0, y2 , x1, y2, Color_added_space,
273 frontend::Painter::line_onoffdash);
274 pi.pain.line(x1, y1, x1, y0, Color_added_space);
275 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
276 pi.pain.line(x0, y1, x0, y0, Color_latex);
277 pi.pain.line(x0, y2 , x1, y2, Color_latex,
278 frontend::Painter::line_onoffdash);
279 pi.pain.line(x1, y1, x1, y0, Color_latex);
280 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
281 pi.pain.line(x0, y1, x0, y0, Color_special);
282 pi.pain.line(x0, y0, x1, y0, Color_special,
283 frontend::Painter::line_onoffdash);
284 pi.pain.line(x1, y1, x1, y0, Color_special);
285 } else if (params_.kind == InsetSpaceParams::HRULEFILL) {
286 pi.pain.line(x0, y1, x0, y0, Color_special);
287 pi.pain.line(x0, y0, x1, y0, Color_special);
288 pi.pain.line(x1, y1, x1, y0, Color_special);
289 } else if (params_.kind == InsetSpaceParams::LEFTARROWFILL) {
290 pi.pain.line(x2, y1 + 1 , x0 + 1, y2, Color_special);
291 pi.pain.line(x0 + 1, y2 + 1 , x2, y0, Color_special);
292 pi.pain.line(x0, y2 , x1, y2, Color_special);
293 } else if (params_.kind == InsetSpaceParams::RIGHTARROWFILL) {
294 pi.pain.line(x3 + 1, y1 + 1 , x1, y2, Color_special);
295 pi.pain.line(x1, y2 + 1 , x3 + 1, y0, Color_special);
296 pi.pain.line(x0, y2 , x1, y2, Color_special);
297 } else if (params_.kind == InsetSpaceParams::UPBRACEFILL) {
298 pi.pain.line(x0 + 1, y1 + 1 , x2, y2, Color_special);
299 pi.pain.line(x2, y2 , xml, y2, Color_special);
300 pi.pain.line(xml + 1, y2 + 1 , xm, y0, Color_special);
301 pi.pain.line(xm + 1, y0 , xmr, y2 + 1, Color_special);
302 pi.pain.line(xmr, y2 , x3, y2, Color_special);
303 pi.pain.line(x3 + 1, y2 , x1, y1 + 1, Color_special);
304 } else if (params_.kind == InsetSpaceParams::DOWNBRACEFILL) {
305 pi.pain.line(x0 + 1, y0 , x2, y2 + 1, Color_special);
306 pi.pain.line(x2, y2 , xml, y2, Color_special);
307 pi.pain.line(xml + 1, y2 , xm, y1 + 1, Color_special);
308 pi.pain.line(xm + 1, y1 + 1 , xmr, y2, Color_special);
309 pi.pain.line(xmr, y2 , x3, y2, Color_special);
310 pi.pain.line(x3 + 1, y2 + 1 , x1, y0, Color_special);
311 } else if (params_.kind == InsetSpaceParams::CUSTOM) {
312 pi.pain.line(x0, y1 + 1 , x2 + 1, y2, Color_special);
313 pi.pain.line(x2 + 1, y2 + 1 , x0, y0, Color_special);
314 pi.pain.line(x1 + 1, y1 + 1 , x3, y2, Color_special);
315 pi.pain.line(x3, y2 + 1 , x1 + 1, y0, Color_special);
316 pi.pain.line(x2, y2 , x3, y2, Color_special);
317 } else if (params_.kind == InsetSpaceParams::CUSTOM_PROTECTED) {
318 pi.pain.line(x0, y1 + 1 , x2 + 1, y2, Color_latex);
319 pi.pain.line(x2 + 1, y2 + 1 , x0, y0, Color_latex);
320 pi.pain.line(x1 + 1, y1 + 1 , x3, y2, Color_latex);
321 pi.pain.line(x3, y2 + 1 , x1 + 1, y0, Color_latex);
322 pi.pain.line(x2, y2 , x3, y2, Color_latex);
327 int const w = dim.wid;
328 int const h = theFontMetrics(pi.base.font).ascent('x');
332 yp[0] = y - max(h / 4, 1);
333 if (params_.kind == InsetSpaceParams::NORMAL ||
334 params_.kind == InsetSpaceParams::PROTECTED) {
335 xp[1] = x; yp[1] = y;
336 xp[2] = x + w; yp[2] = y;
338 xp[1] = x; yp[1] = y + max(h / 4, 1);
339 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
342 yp[3] = y - max(h / 4, 1);
344 if (params_.kind == InsetSpaceParams::PROTECTED ||
345 params_.kind == InsetSpaceParams::ENSPACE ||
346 params_.kind == InsetSpaceParams::NEGTHIN ||
347 params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
348 pi.pain.lines(xp, yp, 4, Color_latex);
350 pi.pain.lines(xp, yp, 4, Color_special);
354 void InsetSpaceParams::write(ostream & os) const
358 case InsetSpaceParams::NORMAL:
361 case InsetSpaceParams::PROTECTED:
364 case InsetSpaceParams::THIN:
365 os << "\\thinspace{}";
367 case InsetSpaceParams::QUAD:
370 case InsetSpaceParams::QQUAD:
373 case InsetSpaceParams::ENSPACE:
376 case InsetSpaceParams::ENSKIP:
379 case InsetSpaceParams::NEGTHIN:
380 os << "\\negthinspace{}";
382 case InsetSpaceParams::HFILL:
385 case InsetSpaceParams::HFILL_PROTECTED:
386 os << "\\hspace*{\\fill}";
388 case InsetSpaceParams::DOTFILL:
391 case InsetSpaceParams::HRULEFILL:
392 os << "\\hrulefill{}";
394 case InsetSpaceParams::LEFTARROWFILL:
395 os << "\\leftarrowfill{}";
397 case InsetSpaceParams::RIGHTARROWFILL:
398 os << "\\rightarrowfill{}";
400 case InsetSpaceParams::UPBRACEFILL:
401 os << "\\upbracefill{}";
403 case InsetSpaceParams::DOWNBRACEFILL:
404 os << "\\downbracefill{}";
406 case InsetSpaceParams::CUSTOM:
409 case InsetSpaceParams::CUSTOM_PROTECTED:
415 os << "\n\\length " << length.asString();
419 void InsetSpaceParams::read(Lexer & lex)
421 lex.setContext("InsetSpaceParams::read");
425 if (command == "\\space{}")
426 kind = InsetSpaceParams::NORMAL;
427 else if (command == "~")
428 kind = InsetSpaceParams::PROTECTED;
429 else if (command == "\\thinspace{}")
430 kind = InsetSpaceParams::THIN;
431 else if (command == "\\quad{}")
432 kind = InsetSpaceParams::QUAD;
433 else if (command == "\\qquad{}")
434 kind = InsetSpaceParams::QQUAD;
435 else if (command == "\\enspace{}")
436 kind = InsetSpaceParams::ENSPACE;
437 else if (command == "\\enskip{}")
438 kind = InsetSpaceParams::ENSKIP;
439 else if (command == "\\negthinspace{}")
440 kind = InsetSpaceParams::NEGTHIN;
441 else if (command == "\\hfill{}")
442 kind = InsetSpaceParams::HFILL;
443 else if (command == "\\hspace*{\\fill}")
444 kind = InsetSpaceParams::HFILL_PROTECTED;
445 else if (command == "\\dotfill{}")
446 kind = InsetSpaceParams::DOTFILL;
447 else if (command == "\\hrulefill{}")
448 kind = InsetSpaceParams::HRULEFILL;
449 else if (command == "\\hspace{}")
450 kind = InsetSpaceParams::CUSTOM;
451 else if (command == "\\leftarrowfill{}")
452 kind = InsetSpaceParams::LEFTARROWFILL;
453 else if (command == "\\rightarrowfill{}")
454 kind = InsetSpaceParams::RIGHTARROWFILL;
455 else if (command == "\\upbracefill{}")
456 kind = InsetSpaceParams::UPBRACEFILL;
457 else if (command == "\\downbracefill{}")
458 kind = InsetSpaceParams::DOWNBRACEFILL;
459 else if (command == "\\hspace*{}")
460 kind = InsetSpaceParams::CUSTOM_PROTECTED;
462 lex.printError("InsetSpace: Unknown kind: `$$Token'");
464 if (lex.checkFor("\\length"))
469 void InsetSpace::write(ostream & os) const
476 void InsetSpace::read(Lexer & lex)
479 lex >> "\\end_inset";
483 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
485 switch (params_.kind) {
486 case InsetSpaceParams::NORMAL:
487 os << (runparams.free_spacing ? " " : "\\ ");
489 case InsetSpaceParams::PROTECTED:
490 os << (runparams.free_spacing ? ' ' : '~');
492 case InsetSpaceParams::THIN:
493 os << (runparams.free_spacing ? " " : "\\,");
495 case InsetSpaceParams::QUAD:
496 os << (runparams.free_spacing ? " " : "\\quad{}");
498 case InsetSpaceParams::QQUAD:
499 os << (runparams.free_spacing ? " " : "\\qquad{}");
501 case InsetSpaceParams::ENSPACE:
502 os << (runparams.free_spacing ? " " : "\\enspace{}");
504 case InsetSpaceParams::ENSKIP:
505 os << (runparams.free_spacing ? " " : "\\enskip{}");
507 case InsetSpaceParams::NEGTHIN:
508 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
510 case InsetSpaceParams::HFILL:
511 os << (runparams.free_spacing ? " " : "\\hfill{}");
513 case InsetSpaceParams::HFILL_PROTECTED:
514 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
516 case InsetSpaceParams::DOTFILL:
517 os << (runparams.free_spacing ? " " : "\\dotfill{}");
519 case InsetSpaceParams::HRULEFILL:
520 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
522 case InsetSpaceParams::LEFTARROWFILL:
523 os << (runparams.free_spacing ? " " : "\\leftarrowfill{}");
525 case InsetSpaceParams::RIGHTARROWFILL:
526 os << (runparams.free_spacing ? " " : "\\rightarrowfill{}");
528 case InsetSpaceParams::UPBRACEFILL:
529 os << (runparams.free_spacing ? " " : "\\upbracefill{}");
531 case InsetSpaceParams::DOWNBRACEFILL:
532 os << (runparams.free_spacing ? " " : "\\downbracefill{}");
534 case InsetSpaceParams::CUSTOM:
535 if (runparams.free_spacing)
538 os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
540 case InsetSpaceParams::CUSTOM_PROTECTED:
541 if (runparams.free_spacing)
544 os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
551 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
553 switch (params_.kind) {
554 case InsetSpaceParams::HFILL:
555 case InsetSpaceParams::HFILL_PROTECTED:
558 case InsetSpaceParams::DOTFILL:
561 case InsetSpaceParams::HRULEFILL:
564 case InsetSpaceParams::LEFTARROWFILL:
567 case InsetSpaceParams::RIGHTARROWFILL:
570 case InsetSpaceParams::UPBRACEFILL:
573 case InsetSpaceParams::DOWNBRACEFILL:
583 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
585 switch (params_.kind) {
586 case InsetSpaceParams::NORMAL:
587 case InsetSpaceParams::QUAD:
588 case InsetSpaceParams::QQUAD:
589 case InsetSpaceParams::ENSKIP:
592 case InsetSpaceParams::PROTECTED:
593 case InsetSpaceParams::ENSPACE:
594 case InsetSpaceParams::THIN:
595 case InsetSpaceParams::NEGTHIN:
598 case InsetSpaceParams::HFILL:
599 case InsetSpaceParams::HFILL_PROTECTED:
601 case InsetSpaceParams::DOTFILL:
604 case InsetSpaceParams::HRULEFILL:
607 case InsetSpaceParams::LEFTARROWFILL:
608 case InsetSpaceParams::RIGHTARROWFILL:
609 case InsetSpaceParams::UPBRACEFILL:
610 case InsetSpaceParams::DOWNBRACEFILL:
611 case InsetSpaceParams::CUSTOM:
612 case InsetSpaceParams::CUSTOM_PROTECTED:
620 void InsetSpace::textString(odocstream & os) const
622 plaintext(os, OutputParams(0));
626 bool InsetSpace::isStretchableSpace() const
628 return params_.kind == InsetSpaceParams::HFILL
629 || params_.kind == InsetSpaceParams::HFILL_PROTECTED
630 || params_.kind == InsetSpaceParams::DOTFILL
631 || params_.kind == InsetSpaceParams::HRULEFILL
632 || params_.kind == InsetSpaceParams::LEFTARROWFILL
633 || params_.kind == InsetSpaceParams::RIGHTARROWFILL
634 || params_.kind == InsetSpaceParams::UPBRACEFILL
635 || params_.kind == InsetSpaceParams::DOWNBRACEFILL;
639 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
641 return from_ascii("context-space");
645 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
647 params = InsetSpaceParams();
651 istringstream data(in);
654 lex.setContext("InsetSpace::string2params");
657 // There are cases, such as when we are called via getStatus() from
658 // Dialog::canApply(), where we are just called with "space" rather
659 // than a full "space \type{}\n\\end_inset".
665 string InsetSpace::params2string(InsetSpaceParams const & params)
668 data << "space" << ' ';