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"
19 #include "Dimension.h"
20 #include "FuncRequest.h"
21 #include "FuncStatus.h"
24 #include "MetricsInfo.h"
25 #include "OutputParams.h"
27 #include "frontends/FontMetrics.h"
28 #include "frontends/Painter.h"
30 #include "support/debug.h"
31 #include "support/docstream.h"
32 #include "support/gettext.h"
33 #include "support/lstrings.h"
40 InsetSpace::InsetSpace()
44 InsetSpace::InsetSpace(InsetSpaceParams par)
46 params_.kind = par.kind;
47 params_.length = par.length;
51 InsetSpaceParams::Kind InsetSpace::kind() const
57 Length InsetSpace::length() const
59 return params_.length;
63 InsetSpace::~InsetSpace()
65 InsetSpaceMailer(*this).hideDialog();
69 docstring InsetSpace::toolTip(BufferView const &, int, int) const
72 switch (params_.kind) {
73 case InsetSpaceParams::NORMAL:
74 message = _("Interword Space");
76 case InsetSpaceParams::PROTECTED:
77 message = _("Protected Space");
79 case InsetSpaceParams::THIN:
80 message = _("Thin Space");
82 case InsetSpaceParams::QUAD:
83 message = _("Quad Space");
85 case InsetSpaceParams::QQUAD:
86 message = _("QQuad Space");
88 case InsetSpaceParams::ENSPACE:
89 message = _("Enspace");
91 case InsetSpaceParams::ENSKIP:
92 message = _("Enskip");
94 case InsetSpaceParams::NEGTHIN:
95 message = _("Negative Thin Space");
97 case InsetSpaceParams::HFILL:
98 message = _("Horizontal Fill");
100 case InsetSpaceParams::HFILL_PROTECTED:
101 message = _("Protected Horizontal Fill");
103 case InsetSpaceParams::DOTFILL:
104 message = _("Horizontal Fill (Dots)");
106 case InsetSpaceParams::HRULEFILL:
107 message = _("Horizontal Fill (Rule)");
109 case InsetSpaceParams::CUSTOM:
110 message = support::bformat(_("Horizontal Space (%1$s)"),
111 params_.length.asDocstring());
113 case InsetSpaceParams::CUSTOM_PROTECTED:
114 message = support::bformat(_("Protected Horizontal Space (%1$s)"),
115 params_.length.asDocstring());
122 void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
124 switch (cmd.action) {
126 case LFUN_INSET_MODIFY: {
127 InsetSpaceParams params;
128 InsetSpaceMailer::string2params(to_utf8(cmd.argument()), params);
129 params_.kind = params.kind;
130 params_.length = params.length;
134 case LFUN_MOUSE_RELEASE:
135 if (!cur.selection() && cmd.button() == mouse_button::button1)
136 InsetSpaceMailer(*this).showDialog(&cur.bv());
140 Inset::doDispatch(cur, cmd);
146 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
147 FuncStatus & status) const
149 switch (cmd.action) {
151 case LFUN_INSET_MODIFY:
152 if (cmd.getArg(0) == "space") {
153 InsetSpaceParams params;
154 InsetSpaceMailer::string2params(to_utf8(cmd.argument()), params);
155 status.setOnOff(params_.kind == params.kind);
157 status.enabled(true);
160 return Inset::getStatus(cur, cmd, status);
165 void InsetSpace::edit(Cursor & cur, bool, EntryDirection)
167 InsetSpaceMailer(*this).showDialog(&cur.bv());
171 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
173 if (isStretchableSpace()) {
174 // The metrics for this kinds are calculated externally in
175 // \c TextMetrics::computeRowMetrics. Those are dummy value:
176 dim = Dimension(10, 10, 10);
180 frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
181 dim.asc = fm.maxAscent();
182 dim.des = fm.maxDescent();
184 switch (params_.kind) {
185 case InsetSpaceParams::THIN:
186 case InsetSpaceParams::NEGTHIN:
187 dim.wid = fm.width(char_type('M')) / 6;
189 case InsetSpaceParams::PROTECTED:
190 case InsetSpaceParams::NORMAL:
191 dim.wid = fm.width(char_type(' '));
193 case InsetSpaceParams::QUAD:
194 dim.wid = fm.width(char_type('M'));
196 case InsetSpaceParams::QQUAD:
197 dim.wid = 2 * fm.width(char_type('M'));
199 case InsetSpaceParams::ENSPACE:
200 case InsetSpaceParams::ENSKIP:
201 dim.wid = int(0.5 * fm.width(char_type('M')));
203 case InsetSpaceParams::CUSTOM:
204 case InsetSpaceParams::CUSTOM_PROTECTED:
205 dim.wid = params_.length.inBP();
207 case InsetSpaceParams::HFILL:
208 case InsetSpaceParams::HFILL_PROTECTED:
209 case InsetSpaceParams::DOTFILL:
210 case InsetSpaceParams::HRULEFILL:
214 // Cache the inset dimension.
215 setDimCache(mi, dim);
219 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
221 Dimension const dim = dimension(*pi.base.bv);
223 if (isStretchableSpace()) {
224 int const asc = theFontMetrics(pi.base.font).ascent('M');
225 int const desc = theFontMetrics(pi.base.font).descent('M');
226 int const x0 = x + 1;
227 int const x1 = x + dim.wid - 2;
228 int const y0 = y + desc;
229 int const y1 = y - asc;
230 int const y2 = y - asc / 2;
232 if (params_.kind == InsetSpaceParams::HFILL) {
233 pi.pain.line(x0, y1, x0, y0, Color_added_space);
234 pi.pain.line(x0, y2 , x1, y2, Color_added_space,
235 frontend::Painter::line_onoffdash);
236 pi.pain.line(x1, y1, x1, y0, Color_added_space);
237 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
238 pi.pain.line(x0, y1, x0, y0, Color_latex);
239 pi.pain.line(x0, y2 , x1, y2, Color_latex,
240 frontend::Painter::line_onoffdash);
241 pi.pain.line(x1, y1, x1, y0, Color_latex);
242 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
243 pi.pain.line(x0, y1, x0, y0, Color_special);
244 pi.pain.line(x0, y, x1, y, Color_special,
245 frontend::Painter::line_onoffdash);
246 pi.pain.line(x1, y1, x1, y0, Color_special);
247 } if (params_.kind == InsetSpaceParams::HRULEFILL) {
248 pi.pain.line(x0, y1, x0, y0, Color_special);
249 pi.pain.line(x0, y, x1, y, Color_special);
250 pi.pain.line(x1, y1, x1, y0, Color_special);
255 int const w = dim.wid;
256 int const h = theFontMetrics(pi.base.font).ascent('x');
260 yp[0] = y - max(h / 4, 1);
261 if (params_.kind == InsetSpaceParams::NORMAL ||
262 params_.kind == InsetSpaceParams::PROTECTED) {
263 xp[1] = x; yp[1] = y;
264 xp[2] = x + w; yp[2] = y;
266 xp[1] = x; yp[1] = y + max(h / 4, 1);
267 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
270 yp[3] = y - max(h / 4, 1);
272 if (params_.kind == InsetSpaceParams::PROTECTED ||
273 params_.kind == InsetSpaceParams::ENSPACE ||
274 params_.kind == InsetSpaceParams::NEGTHIN ||
275 params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
276 pi.pain.lines(xp, yp, 4, Color_latex);
278 pi.pain.lines(xp, yp, 4, Color_special);
282 void InsetSpaceParams::write(ostream & os) const
286 case InsetSpaceParams::NORMAL:
289 case InsetSpaceParams::PROTECTED:
292 case InsetSpaceParams::THIN:
293 os << "\\thinspace{}";
295 case InsetSpaceParams::QUAD:
298 case InsetSpaceParams::QQUAD:
301 case InsetSpaceParams::ENSPACE:
304 case InsetSpaceParams::ENSKIP:
307 case InsetSpaceParams::NEGTHIN:
308 os << "\\negthinspace{}";
310 case InsetSpaceParams::HFILL:
313 case InsetSpaceParams::HFILL_PROTECTED:
314 os << "\\hspace*{\\fill}";
316 case InsetSpaceParams::DOTFILL:
319 case InsetSpaceParams::HRULEFILL:
320 os << "\\hrulefill{}";
322 case InsetSpaceParams::CUSTOM:
325 case InsetSpaceParams::CUSTOM_PROTECTED:
331 os << "\n\\length " << length.asString();
335 void InsetSpaceParams::read(Lexer & lex)
338 string const command = lex.getString();
340 if (command == "\\space{}")
341 kind = InsetSpaceParams::NORMAL;
342 else if (command == "~")
343 kind = InsetSpaceParams::PROTECTED;
344 else if (command == "\\thinspace{}")
345 kind = InsetSpaceParams::THIN;
346 else if (command == "\\quad{}")
347 kind = InsetSpaceParams::QUAD;
348 else if (command == "\\qquad{}")
349 kind = InsetSpaceParams::QQUAD;
350 else if (command == "\\enspace{}")
351 kind = InsetSpaceParams::ENSPACE;
352 else if (command == "\\enskip{}")
353 kind = InsetSpaceParams::ENSKIP;
354 else if (command == "\\negthinspace{}")
355 kind = InsetSpaceParams::NEGTHIN;
356 else if (command == "\\hfill{}")
357 kind = InsetSpaceParams::HFILL;
358 else if (command == "\\hspace*{\\fill}")
359 kind = InsetSpaceParams::HFILL_PROTECTED;
360 else if (command == "\\dotfill{}")
361 kind = InsetSpaceParams::DOTFILL;
362 else if (command == "\\hrulefill{}")
363 kind = InsetSpaceParams::HRULEFILL;
364 else if (command == "\\hspace{}")
365 kind = InsetSpaceParams::CUSTOM;
366 else if (command == "\\hspace*{}")
367 kind = InsetSpaceParams::CUSTOM_PROTECTED;
369 lex.printError("InsetSpace: Unknown kind: `$$Token'");
374 if (token == "\\length") {
376 string const len = lex.getString();
377 length = Length(len);
379 token = lex.getString();
383 if (token != "\\end_inset")
384 lex.printError("Missing \\end_inset at this point. "
389 void InsetSpace::write(ostream & os) const
396 void InsetSpace::read(Lexer & lex)
402 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
404 switch (params_.kind) {
405 case InsetSpaceParams::NORMAL:
406 os << (runparams.free_spacing ? " " : "\\ ");
408 case InsetSpaceParams::PROTECTED:
409 os << (runparams.free_spacing ? ' ' : '~');
411 case InsetSpaceParams::THIN:
412 os << (runparams.free_spacing ? " " : "\\,");
414 case InsetSpaceParams::QUAD:
415 os << (runparams.free_spacing ? " " : "\\quad{}");
417 case InsetSpaceParams::QQUAD:
418 os << (runparams.free_spacing ? " " : "\\qquad{}");
420 case InsetSpaceParams::ENSPACE:
421 os << (runparams.free_spacing ? " " : "\\enspace{}");
423 case InsetSpaceParams::ENSKIP:
424 os << (runparams.free_spacing ? " " : "\\enskip{}");
426 case InsetSpaceParams::NEGTHIN:
427 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
429 case InsetSpaceParams::HFILL:
430 os << (runparams.free_spacing ? " " : "\\hfill{}");
432 case InsetSpaceParams::HFILL_PROTECTED:
433 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
435 case InsetSpaceParams::DOTFILL:
436 os << (runparams.free_spacing ? " " : "\\dotfill{}");
438 case InsetSpaceParams::HRULEFILL:
439 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
441 case InsetSpaceParams::CUSTOM:
442 if (runparams.free_spacing)
445 os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
447 case InsetSpaceParams::CUSTOM_PROTECTED:
448 if (runparams.free_spacing)
451 os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
458 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
460 switch (params_.kind) {
461 case InsetSpaceParams::HFILL:
462 case InsetSpaceParams::HFILL_PROTECTED:
465 case InsetSpaceParams::DOTFILL:
468 case InsetSpaceParams::HRULEFILL:
478 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
480 switch (params_.kind) {
481 case InsetSpaceParams::NORMAL:
482 case InsetSpaceParams::QUAD:
483 case InsetSpaceParams::QQUAD:
484 case InsetSpaceParams::ENSKIP:
487 case InsetSpaceParams::PROTECTED:
488 case InsetSpaceParams::ENSPACE:
489 case InsetSpaceParams::THIN:
490 case InsetSpaceParams::NEGTHIN:
493 case InsetSpaceParams::HFILL:
494 case InsetSpaceParams::HFILL_PROTECTED:
496 case InsetSpaceParams::DOTFILL:
499 case InsetSpaceParams::HRULEFILL:
502 case InsetSpaceParams::CUSTOM:
503 case InsetSpaceParams::CUSTOM_PROTECTED:
511 void InsetSpace::textString(odocstream & os) const
513 plaintext(os, OutputParams(0));
517 bool InsetSpace::isStretchableSpace() const
519 return (params_.kind == InsetSpaceParams::HFILL ||
520 params_.kind == InsetSpaceParams::HFILL_PROTECTED ||
521 params_.kind == InsetSpaceParams::DOTFILL ||
522 params_.kind == InsetSpaceParams::HRULEFILL);
526 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
528 return from_ascii("context-space");
532 string const InsetSpaceMailer::name_ = "space";
535 InsetSpaceMailer::InsetSpaceMailer(InsetSpace & inset)
540 string const InsetSpaceMailer::inset2string(Buffer const &) const
542 return params2string(inset_.params());
546 void InsetSpaceMailer::string2params(string const & in, InsetSpaceParams & params)
548 params = InsetSpaceParams();
552 istringstream data(in);
558 if (!lex || name != name_)
559 return print_mailer_error("InsetSpaceMailer", in, 1, name_);
565 string const InsetSpaceMailer::params2string(InsetSpaceParams const & params)
568 data << name_ << ' ';