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::CUSTOM:
106 message = support::bformat(_("Horizontal Space (%1$s)"),
107 params_.length.asDocstring());
109 case InsetSpaceParams::CUSTOM_PROTECTED:
110 message = support::bformat(_("Protected Horizontal Space (%1$s)"),
111 params_.length.asDocstring());
118 void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
120 switch (cmd.action) {
122 case LFUN_INSET_MODIFY: {
123 InsetSpaceParams params;
124 string2params(to_utf8(cmd.argument()), params);
125 params_.kind = params.kind;
126 params_.length = params.length;
130 case LFUN_MOUSE_RELEASE:
131 if (!cur.selection() && cmd.button() == mouse_button::button1)
132 cur.bv().showDialog("wrap", params2string(params()), this);
136 Inset::doDispatch(cur, cmd);
142 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
143 FuncStatus & status) const
145 switch (cmd.action) {
147 case LFUN_INSET_MODIFY:
148 if (cmd.getArg(0) == "space") {
149 InsetSpaceParams params;
150 string2params(to_utf8(cmd.argument()), params);
151 status.setOnOff(params_.kind == params.kind);
153 status.enabled(true);
156 return Inset::getStatus(cur, cmd, status);
161 void InsetSpace::edit(Cursor & cur, bool, EntryDirection)
163 cur.bv().showDialog("wrap", params2string(params()), this);
167 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
169 if (isStretchableSpace()) {
170 // The metrics for this kinds are calculated externally in
171 // \c TextMetrics::computeRowMetrics. Those are dummy value:
172 dim = Dimension(10, 10, 10);
176 frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
177 dim.asc = fm.maxAscent();
178 dim.des = fm.maxDescent();
180 switch (params_.kind) {
181 case InsetSpaceParams::THIN:
182 case InsetSpaceParams::NEGTHIN:
183 dim.wid = fm.width(char_type('M')) / 6;
185 case InsetSpaceParams::PROTECTED:
186 case InsetSpaceParams::NORMAL:
187 dim.wid = fm.width(char_type(' '));
189 case InsetSpaceParams::QUAD:
190 dim.wid = fm.width(char_type('M'));
192 case InsetSpaceParams::QQUAD:
193 dim.wid = 2 * fm.width(char_type('M'));
195 case InsetSpaceParams::ENSPACE:
196 case InsetSpaceParams::ENSKIP:
197 dim.wid = int(0.5 * fm.width(char_type('M')));
199 case InsetSpaceParams::CUSTOM:
200 case InsetSpaceParams::CUSTOM_PROTECTED:
201 dim.wid = params_.length.inBP();
203 case InsetSpaceParams::HFILL:
204 case InsetSpaceParams::HFILL_PROTECTED:
205 case InsetSpaceParams::DOTFILL:
206 case InsetSpaceParams::HRULEFILL:
210 // Cache the inset dimension.
211 setDimCache(mi, dim);
215 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
217 Dimension const dim = dimension(*pi.base.bv);
219 if (isStretchableSpace()) {
220 int const asc = theFontMetrics(pi.base.font).ascent('M');
221 int const desc = theFontMetrics(pi.base.font).descent('M');
222 int const x0 = x + 1;
223 int const x1 = x + dim.wid - 2;
224 int const y0 = y + desc;
225 int const y1 = y - asc;
226 int const y2 = y - asc / 2;
228 if (params_.kind == InsetSpaceParams::HFILL) {
229 pi.pain.line(x0, y1, x0, y0, Color_added_space);
230 pi.pain.line(x0, y2 , x1, y2, Color_added_space,
231 frontend::Painter::line_onoffdash);
232 pi.pain.line(x1, y1, x1, y0, Color_added_space);
233 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
234 pi.pain.line(x0, y1, x0, y0, Color_latex);
235 pi.pain.line(x0, y2 , x1, y2, Color_latex,
236 frontend::Painter::line_onoffdash);
237 pi.pain.line(x1, y1, x1, y0, Color_latex);
238 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
239 pi.pain.line(x0, y1, x0, y0, Color_special);
240 pi.pain.line(x0, y, x1, y, Color_special,
241 frontend::Painter::line_onoffdash);
242 pi.pain.line(x1, y1, x1, y0, Color_special);
243 } if (params_.kind == InsetSpaceParams::HRULEFILL) {
244 pi.pain.line(x0, y1, x0, y0, Color_special);
245 pi.pain.line(x0, y, x1, y, Color_special);
246 pi.pain.line(x1, y1, x1, y0, Color_special);
251 int const w = dim.wid;
252 int const h = theFontMetrics(pi.base.font).ascent('x');
256 yp[0] = y - max(h / 4, 1);
257 if (params_.kind == InsetSpaceParams::NORMAL ||
258 params_.kind == InsetSpaceParams::PROTECTED) {
259 xp[1] = x; yp[1] = y;
260 xp[2] = x + w; yp[2] = y;
262 xp[1] = x; yp[1] = y + max(h / 4, 1);
263 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
266 yp[3] = y - max(h / 4, 1);
268 if (params_.kind == InsetSpaceParams::PROTECTED ||
269 params_.kind == InsetSpaceParams::ENSPACE ||
270 params_.kind == InsetSpaceParams::NEGTHIN ||
271 params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
272 pi.pain.lines(xp, yp, 4, Color_latex);
274 pi.pain.lines(xp, yp, 4, Color_special);
278 void InsetSpaceParams::write(ostream & os) const
282 case InsetSpaceParams::NORMAL:
285 case InsetSpaceParams::PROTECTED:
288 case InsetSpaceParams::THIN:
289 os << "\\thinspace{}";
291 case InsetSpaceParams::QUAD:
294 case InsetSpaceParams::QQUAD:
297 case InsetSpaceParams::ENSPACE:
300 case InsetSpaceParams::ENSKIP:
303 case InsetSpaceParams::NEGTHIN:
304 os << "\\negthinspace{}";
306 case InsetSpaceParams::HFILL:
309 case InsetSpaceParams::HFILL_PROTECTED:
310 os << "\\hspace*{\\fill}";
312 case InsetSpaceParams::DOTFILL:
315 case InsetSpaceParams::HRULEFILL:
316 os << "\\hrulefill{}";
318 case InsetSpaceParams::CUSTOM:
321 case InsetSpaceParams::CUSTOM_PROTECTED:
327 os << "\n\\length " << length.asString();
331 void InsetSpaceParams::read(Lexer & lex)
334 string const command = lex.getString();
336 if (command == "\\space{}")
337 kind = InsetSpaceParams::NORMAL;
338 else if (command == "~")
339 kind = InsetSpaceParams::PROTECTED;
340 else if (command == "\\thinspace{}")
341 kind = InsetSpaceParams::THIN;
342 else if (command == "\\quad{}")
343 kind = InsetSpaceParams::QUAD;
344 else if (command == "\\qquad{}")
345 kind = InsetSpaceParams::QQUAD;
346 else if (command == "\\enspace{}")
347 kind = InsetSpaceParams::ENSPACE;
348 else if (command == "\\enskip{}")
349 kind = InsetSpaceParams::ENSKIP;
350 else if (command == "\\negthinspace{}")
351 kind = InsetSpaceParams::NEGTHIN;
352 else if (command == "\\hfill{}")
353 kind = InsetSpaceParams::HFILL;
354 else if (command == "\\hspace*{\\fill}")
355 kind = InsetSpaceParams::HFILL_PROTECTED;
356 else if (command == "\\dotfill{}")
357 kind = InsetSpaceParams::DOTFILL;
358 else if (command == "\\hrulefill{}")
359 kind = InsetSpaceParams::HRULEFILL;
360 else if (command == "\\hspace{}")
361 kind = InsetSpaceParams::CUSTOM;
362 else if (command == "\\hspace*{}")
363 kind = InsetSpaceParams::CUSTOM_PROTECTED;
365 lex.printError("InsetSpace: Unknown kind: `$$Token'");
370 if (token == "\\length") {
372 string const len = lex.getString();
373 length = Length(len);
375 token = lex.getString();
379 if (token != "\\end_inset")
380 lex.printError("Missing \\end_inset at this point. "
385 void InsetSpace::write(ostream & os) const
392 void InsetSpace::read(Lexer & lex)
398 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
400 switch (params_.kind) {
401 case InsetSpaceParams::NORMAL:
402 os << (runparams.free_spacing ? " " : "\\ ");
404 case InsetSpaceParams::PROTECTED:
405 os << (runparams.free_spacing ? ' ' : '~');
407 case InsetSpaceParams::THIN:
408 os << (runparams.free_spacing ? " " : "\\,");
410 case InsetSpaceParams::QUAD:
411 os << (runparams.free_spacing ? " " : "\\quad{}");
413 case InsetSpaceParams::QQUAD:
414 os << (runparams.free_spacing ? " " : "\\qquad{}");
416 case InsetSpaceParams::ENSPACE:
417 os << (runparams.free_spacing ? " " : "\\enspace{}");
419 case InsetSpaceParams::ENSKIP:
420 os << (runparams.free_spacing ? " " : "\\enskip{}");
422 case InsetSpaceParams::NEGTHIN:
423 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
425 case InsetSpaceParams::HFILL:
426 os << (runparams.free_spacing ? " " : "\\hfill{}");
428 case InsetSpaceParams::HFILL_PROTECTED:
429 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
431 case InsetSpaceParams::DOTFILL:
432 os << (runparams.free_spacing ? " " : "\\dotfill{}");
434 case InsetSpaceParams::HRULEFILL:
435 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
437 case InsetSpaceParams::CUSTOM:
438 if (runparams.free_spacing)
441 os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
443 case InsetSpaceParams::CUSTOM_PROTECTED:
444 if (runparams.free_spacing)
447 os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
454 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
456 switch (params_.kind) {
457 case InsetSpaceParams::HFILL:
458 case InsetSpaceParams::HFILL_PROTECTED:
461 case InsetSpaceParams::DOTFILL:
464 case InsetSpaceParams::HRULEFILL:
474 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
476 switch (params_.kind) {
477 case InsetSpaceParams::NORMAL:
478 case InsetSpaceParams::QUAD:
479 case InsetSpaceParams::QQUAD:
480 case InsetSpaceParams::ENSKIP:
483 case InsetSpaceParams::PROTECTED:
484 case InsetSpaceParams::ENSPACE:
485 case InsetSpaceParams::THIN:
486 case InsetSpaceParams::NEGTHIN:
489 case InsetSpaceParams::HFILL:
490 case InsetSpaceParams::HFILL_PROTECTED:
492 case InsetSpaceParams::DOTFILL:
495 case InsetSpaceParams::HRULEFILL:
498 case InsetSpaceParams::CUSTOM:
499 case InsetSpaceParams::CUSTOM_PROTECTED:
507 void InsetSpace::textString(odocstream & os) const
509 plaintext(os, OutputParams(0));
513 bool InsetSpace::isStretchableSpace() const
515 return (params_.kind == InsetSpaceParams::HFILL ||
516 params_.kind == InsetSpaceParams::HFILL_PROTECTED ||
517 params_.kind == InsetSpaceParams::DOTFILL ||
518 params_.kind == InsetSpaceParams::HRULEFILL);
522 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
524 return from_ascii("context-space");
528 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
530 params = InsetSpaceParams();
534 istringstream data(in);
540 if (!lex || name != "space") {
541 LYXERR0("Expected arg 1 to be \"space\" in " << in);
549 string InsetSpace::params2string(InsetSpaceParams const & params)
552 data << "space" << ' ';