]> git.lyx.org Git - features.git/blob - src/insets/InsetSpace.cpp
a5fed47fbfec5f27896ac344a847f298e8b22186
[features.git] / src / insets / InsetSpace.cpp
1 /**
2  * \file InsetSpace.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Jean-Marc Lasgouttes
8  * \author Lars Gullik Bjønnes
9  * \author Jürgen Spitzmüller
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "InsetSpace.h"
17
18 #include "BufferView.h"
19 #include "Cursor.h"
20 #include "Dimension.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
23 #include "Language.h"
24 #include "LaTeXFeatures.h"
25 #include "Length.h"
26 #include "Lexer.h"
27 #include "MetricsInfo.h"
28 #include "OutputParams.h"
29 #include "output_xhtml.h"
30 #include "texstream.h"
31
32 #include "support/debug.h"
33 #include "support/docstream.h"
34 #include "support/gettext.h"
35 #include "support/lassert.h"
36 #include "support/lstrings.h"
37
38 #include "frontends/Application.h"
39 #include "frontends/FontMetrics.h"
40 #include "frontends/Painter.h"
41
42 using namespace std;
43
44 namespace lyx {
45
46
47 InsetSpace::InsetSpace(InsetSpaceParams const & params)
48         : Inset(0), params_(params)
49 {}
50
51
52 InsetSpaceParams::Kind InsetSpace::kind() const
53 {
54         return params_.kind;
55 }
56
57
58 GlueLength InsetSpace::length() const
59 {
60         return params_.length;
61 }
62
63
64 docstring InsetSpace::toolTip(BufferView const &, int, int) const
65 {
66         docstring message;
67         switch (params_.kind) {
68         case InsetSpaceParams::NORMAL:
69                 message = _("Interword Space");
70                 break;
71         case InsetSpaceParams::PROTECTED:
72                 message = _("Protected Space");
73                 break;
74         case InsetSpaceParams::VISIBLE:
75                 message = _("Visible Space");
76                 break;
77         case InsetSpaceParams::THIN:
78                 message = _("Thin Space");
79                 break;
80         case InsetSpaceParams::MEDIUM:
81                 message = _("Medium Space");
82                 break;
83         case InsetSpaceParams::THICK:
84                 message = _("Thick Space");
85                 break;
86         case InsetSpaceParams::QUAD:
87                 message = _("Quad Space");
88                 break;
89         case InsetSpaceParams::QQUAD:
90                 message = _("Double Quad Space");
91                 break;
92         case InsetSpaceParams::ENSPACE:
93                 message = _("Enspace");
94                 break;
95         case InsetSpaceParams::ENSKIP:
96                 message = _("Enskip");
97                 break;
98         case InsetSpaceParams::NEGTHIN:
99                 message = _("Negative Thin Space");
100                 break;
101         case InsetSpaceParams::NEGMEDIUM:
102                 message = _("Negative Medium Space");
103                 break;
104         case InsetSpaceParams::NEGTHICK:
105                 message = _("Negative Thick Space");
106                 break;
107         case InsetSpaceParams::HFILL:
108                 message = _("Horizontal Fill");
109                 break;
110         case InsetSpaceParams::HFILL_PROTECTED:
111                 message = _("Protected Horizontal Fill");
112                 break;
113         case InsetSpaceParams::DOTFILL:
114                 message = _("Horizontal Fill (Dots)");
115                 break;
116         case InsetSpaceParams::HRULEFILL:
117                 message = _("Horizontal Fill (Rule)");
118                 break;
119         case InsetSpaceParams::LEFTARROWFILL:
120                 message = _("Horizontal Fill (Left Arrow)");
121                 break;
122         case InsetSpaceParams::RIGHTARROWFILL:
123                 message = _("Horizontal Fill (Right Arrow)");
124                 break;
125         case InsetSpaceParams::UPBRACEFILL:
126                 message = _("Horizontal Fill (Up Brace)");
127                 break;
128         case InsetSpaceParams::DOWNBRACEFILL:
129                 message = _("Horizontal Fill (Down Brace)");
130                 break;
131         case InsetSpaceParams::CUSTOM:
132                 // FIXME unicode
133                 message = support::bformat(_("Horizontal Space (%1$s)"),
134                                 from_ascii(params_.length.asString()));
135                 break;
136         case InsetSpaceParams::CUSTOM_PROTECTED:
137                 // FIXME unicode
138                 message = support::bformat(_("Protected Horizontal Space (%1$s)"),
139                                 from_ascii(params_.length.asString()));
140                 break;
141         }
142         return message;
143 }
144
145
146 void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
147 {
148         switch (cmd.action()) {
149
150         case LFUN_INSET_MODIFY: {
151                 cur.recordUndo();
152                 string arg = to_utf8(cmd.argument());
153                 if (arg == "space \\hspace{}")
154                         arg += params_.length.len().empty()
155                                 ? " \\length 1" + string(stringFromUnit(Length::defaultUnit()))
156                                 : " \\length " + params_.length.asString();
157                 string2params(arg, params_);
158                 break;
159         }
160
161         case LFUN_INSET_DIALOG_UPDATE:
162                 cur.bv().updateDialog("space", params2string(params()));
163                 break;
164
165         default:
166                 Inset::doDispatch(cur, cmd);
167                 break;
168         }
169 }
170
171
172 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
173         FuncStatus & status) const
174 {
175         switch (cmd.action()) {
176         // we handle these
177         case LFUN_INSET_MODIFY:
178                 if (cmd.getArg(0) == "space") {
179                         InsetSpaceParams params;
180                         string2params(to_utf8(cmd.argument()), params);
181                         status.setOnOff(params_.kind == params.kind);
182                         status.setEnabled(true);
183                 } else
184                         status.setEnabled(false);
185                 return true;
186
187         case LFUN_INSET_DIALOG_UPDATE:
188                 status.setEnabled(true);
189                 return true;
190         default:
191                 return Inset::getStatus(cur, cmd, status);
192         }
193 }
194
195
196 namespace {
197 int const arrow_size = 8;
198 }
199
200
201 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
202 {
203         if (isHfill()) {
204                 // The width for hfills is calculated externally in
205                 // TextMetrics::setRowAlignment. The value of 5 is the
206                 // minimal value when the hfill is not active.
207                 dim = Dimension(5, 10, 10);
208                 return;
209         }
210
211         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
212         dim.asc = fm.maxAscent();
213         dim.des = fm.maxDescent();
214         int const em = fm.em();
215
216         switch (params_.kind) {
217                 case InsetSpaceParams::THIN:
218                 case InsetSpaceParams::NEGTHIN:
219                         dim.wid = em / 6;
220                         break;
221                 case InsetSpaceParams::MEDIUM:
222                 case InsetSpaceParams::NEGMEDIUM:
223                         dim.wid = em / 4;
224                         break;
225                 case InsetSpaceParams::THICK:
226                 case InsetSpaceParams::NEGTHICK:
227                         dim.wid = em / 2;
228                         break;
229                 case InsetSpaceParams::PROTECTED:
230                 case InsetSpaceParams::VISIBLE:
231                 case InsetSpaceParams::NORMAL:
232                         dim.wid = fm.width(char_type(' '));
233                         break;
234                 case InsetSpaceParams::QUAD:
235                         dim.wid = em;
236                         break;
237                 case InsetSpaceParams::QQUAD:
238                         dim.wid = 2 * em;
239                         break;
240                 case InsetSpaceParams::ENSPACE:
241                 case InsetSpaceParams::ENSKIP:
242                         dim.wid = int(0.5 * em);
243                         break;
244                 case InsetSpaceParams::CUSTOM:
245                 case InsetSpaceParams::CUSTOM_PROTECTED: {
246                         int const w = mi.base.inPixels(params_.length.len());
247                         int const minw = (w < 0) ? 3 * arrow_size : 4;
248                         dim.wid = max(minw, abs(w));
249                         break;
250                 }
251                 case InsetSpaceParams::HFILL:
252                 case InsetSpaceParams::HFILL_PROTECTED:
253                 case InsetSpaceParams::DOTFILL:
254                 case InsetSpaceParams::HRULEFILL:
255                 case InsetSpaceParams::LEFTARROWFILL:
256                 case InsetSpaceParams::RIGHTARROWFILL:
257                 case InsetSpaceParams::UPBRACEFILL:
258                 case InsetSpaceParams::DOWNBRACEFILL:
259                         // shut up compiler
260                         break;
261         }
262 }
263
264
265 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
266 {
267         Dimension const dim = dimension(*pi.base.bv);
268
269         if (isHfill() || params_.length.len().value() < 0) {
270                 int const asc = theFontMetrics(pi.base.font).ascent('M');
271                 int const desc = theFontMetrics(pi.base.font).descent('M');
272                 // Pixel height divisible by 2 for prettier fill graphics:
273                 int const oddheight = (asc ^ desc) & 1;
274                 int const x0 = x + 1;
275                 int const x1 = x + dim.wid - 2;
276                 int const y0 = y + desc - 1;
277                 int const y1 = y - asc + oddheight - 1;
278                 int const y2 = (y0 + y1) / 2;
279                 int xoffset = (y0 - y1) / 2;
280
281                 // Two tests for very narrow insets
282                 if (xoffset > x1 - x0
283                      && (params_.kind == InsetSpaceParams::LEFTARROWFILL
284                          || params_.kind == InsetSpaceParams::RIGHTARROWFILL))
285                                 xoffset = x1 - x0;
286                 if (xoffset * 6 > (x1 - x0)
287                      && (params_.kind == InsetSpaceParams::UPBRACEFILL
288                          || params_.kind == InsetSpaceParams::DOWNBRACEFILL))
289                                 xoffset = (x1 - x0) / 6;
290
291                 int const x2 = x0 + xoffset;
292                 int const x3 = x1 - xoffset;
293                 int const xm = (x0 + x1) / 2;
294                 int const xml = xm - xoffset;
295                 int const xmr = xm + xoffset;
296
297                 if (params_.kind == InsetSpaceParams::HFILL) {
298                         pi.pain.line(x0, y1, x0, y0, Color_added_space);
299                         pi.pain.line(x0, y2, x1, y2, Color_added_space,
300                                 frontend::Painter::line_onoffdash);
301                         pi.pain.line(x1, y1, x1, y0, Color_added_space);
302                 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
303                         pi.pain.line(x0, y1, x0, y0, Color_latex);
304                         pi.pain.line(x0, y2, x1, y2, Color_latex,
305                                 frontend::Painter::line_onoffdash);
306                         pi.pain.line(x1, y1, x1, y0, Color_latex);
307                 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
308                         pi.pain.line(x0, y1, x0, y0, Color_special);
309                         pi.pain.line(x0, y0, x1, y0, Color_special,
310                                 frontend::Painter::line_onoffdash);
311                         pi.pain.line(x1, y1, x1, y0, Color_special);
312                 } else if (params_.kind == InsetSpaceParams::HRULEFILL) {
313                         pi.pain.line(x0, y1, x0, y0, Color_special);
314                         pi.pain.line(x0, y0, x1, y0, Color_special);
315                         pi.pain.line(x1, y1, x1, y0, Color_special);
316                 } else if (params_.kind == InsetSpaceParams::LEFTARROWFILL) {
317                         pi.pain.line(x2, y1 + 1 , x0 + 1, y2, Color_special);
318                         pi.pain.line(x0 + 1, y2 + 1 , x2, y0, Color_special);
319                         pi.pain.line(x0, y2 , x1, y2, Color_special);
320                 } else if (params_.kind == InsetSpaceParams::RIGHTARROWFILL) {
321                         pi.pain.line(x3 + 1, y1 + 1 , x1, y2, Color_special);
322                         pi.pain.line(x1, y2 + 1 , x3 + 1, y0, Color_special);
323                         pi.pain.line(x0, y2 , x1, y2, Color_special);
324                 } else if (params_.kind == InsetSpaceParams::UPBRACEFILL) {
325                         pi.pain.line(x0 + 1, y1 + 1 , x2, y2, Color_special);
326                         pi.pain.line(x2, y2 , xml, y2, Color_special);
327                         pi.pain.line(xml + 1, y2 + 1 , xm, y0, Color_special);
328                         pi.pain.line(xm + 1, y0 , xmr, y2 + 1, Color_special);
329                         pi.pain.line(xmr, y2 , x3, y2, Color_special);
330                         pi.pain.line(x3 + 1, y2 , x1, y1 + 1, Color_special);
331                 } else if (params_.kind == InsetSpaceParams::DOWNBRACEFILL) {
332                         pi.pain.line(x0 + 1, y0 , x2, y2 + 1, Color_special);
333                         pi.pain.line(x2, y2 , xml, y2, Color_special);
334                         pi.pain.line(xml + 1, y2 , xm, y1 + 1, Color_special);
335                         pi.pain.line(xm + 1, y1 + 1 , xmr, y2, Color_special);
336                         pi.pain.line(xmr, y2 , x3, y2, Color_special);
337                         pi.pain.line(x3 + 1, y2 + 1 , x1, y0, Color_special);
338                 } else if (params_.kind == InsetSpaceParams::CUSTOM) {
339                         pi.pain.line(x0, y1 + 1 , x2 + 1, y2, Color_special);
340                         pi.pain.line(x2 + 1, y2 + 1 , x0, y0, Color_special);
341                         pi.pain.line(x1 + 1, y1 + 1 , x3, y2, Color_special);
342                         pi.pain.line(x3, y2 + 1 , x1 + 1, y0, Color_special);
343                         pi.pain.line(x2, y2 , x3, y2, Color_special);
344                 } else if (params_.kind == InsetSpaceParams::CUSTOM_PROTECTED) {
345                         pi.pain.line(x0, y1 + 1 , x2 + 1, y2, Color_latex);
346                         pi.pain.line(x2 + 1, y2 + 1 , x0, y0, Color_latex);
347                         pi.pain.line(x1 + 1, y1 + 1 , x3, y2, Color_latex);
348                         pi.pain.line(x3, y2 + 1 , x1 + 1, y0, Color_latex);
349                         pi.pain.line(x2, y2 , x3, y2, Color_latex);
350                 }
351                 return;
352         }
353
354         int const w = dim.wid;
355         int const h = theFontMetrics(pi.base.font).xHeight();
356         int xp[4], yp[4];
357
358         xp[0] = x + 1;
359         yp[0] = y - max(h / 4, 1);
360         if (params_.kind == InsetSpaceParams::NORMAL ||
361             params_.kind == InsetSpaceParams::PROTECTED ||
362             params_.kind == InsetSpaceParams::VISIBLE) {
363                 xp[1] = x + 1;     yp[1] = y;
364                 xp[2] = x + w - 2; yp[2] = y;
365         } else {
366                 xp[1] = x + 1;     yp[1] = y + max(h / 4, 1);
367                 xp[2] = x + w - 2; yp[2] = y + max(h / 4, 1);
368         }
369         xp[3] = x + w - 2;
370         yp[3] = y - max(h / 4, 1);
371
372         Color col = Color_special;
373         if (params_.kind == InsetSpaceParams::PROTECTED ||
374             params_.kind == InsetSpaceParams::ENSPACE ||
375             params_.kind == InsetSpaceParams::NEGTHIN ||
376             params_.kind == InsetSpaceParams::NEGMEDIUM ||
377             params_.kind == InsetSpaceParams::NEGTHICK ||
378             params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
379                 col = Color_latex;
380         else if (params_.kind == InsetSpaceParams::VISIBLE)
381                 col =  Color_foreground;
382
383         pi.pain.lines(xp, yp, 4, col);
384 }
385
386
387 void InsetSpaceParams::write(ostream & os) const
388 {
389         switch (kind) {
390         case InsetSpaceParams::NORMAL:
391                 os << "\\space{}";
392                 break;
393         case InsetSpaceParams::PROTECTED:
394                 os <<  "~";
395                 break;
396         case InsetSpaceParams::VISIBLE:
397                 os <<  "\\textvisiblespace{}";
398                 break;
399         case InsetSpaceParams::THIN:
400                 os <<  "\\thinspace{}";
401                 break;
402         case InsetSpaceParams::MEDIUM:
403                 os <<  "\\medspace{}";
404                 break;
405         case InsetSpaceParams::THICK:
406                 os <<  "\\thickspace{}";
407                 break;
408         case InsetSpaceParams::QUAD:
409                 os <<  "\\quad{}";
410                 break;
411         case InsetSpaceParams::QQUAD:
412                 os <<  "\\qquad{}";
413                 break;
414         case InsetSpaceParams::ENSPACE:
415                 os <<  "\\enspace{}";
416                 break;
417         case InsetSpaceParams::ENSKIP:
418                 os <<  "\\enskip{}";
419                 break;
420         case InsetSpaceParams::NEGTHIN:
421                 os <<  "\\negthinspace{}";
422                 break;
423         case InsetSpaceParams::NEGMEDIUM:
424                 os <<  "\\negmedspace{}";
425                 break;
426         case InsetSpaceParams::NEGTHICK:
427                 os <<  "\\negthickspace{}";
428                 break;
429         case InsetSpaceParams::HFILL:
430                 os <<  "\\hfill{}";
431                 break;
432         case InsetSpaceParams::HFILL_PROTECTED:
433                 os <<  "\\hspace*{\\fill}";
434                 break;
435         case InsetSpaceParams::DOTFILL:
436                 os <<  "\\dotfill{}";
437                 break;
438         case InsetSpaceParams::HRULEFILL:
439                 os <<  "\\hrulefill{}";
440                 break;
441         case InsetSpaceParams::LEFTARROWFILL:
442                 os <<  "\\leftarrowfill{}";
443                 break;
444         case InsetSpaceParams::RIGHTARROWFILL:
445                 os <<  "\\rightarrowfill{}";
446                 break;
447         case InsetSpaceParams::UPBRACEFILL:
448                 os <<  "\\upbracefill{}";
449                 break;
450         case InsetSpaceParams::DOWNBRACEFILL:
451                 os <<  "\\downbracefill{}";
452                 break;
453         case InsetSpaceParams::CUSTOM:
454                 os <<  "\\hspace{}";
455                 break;
456         case InsetSpaceParams::CUSTOM_PROTECTED:
457                 os <<  "\\hspace*{}";
458                 break;
459         }
460
461         if (!length.len().empty())
462                 os << "\n\\length " << length.asString();
463 }
464
465
466 void InsetSpaceParams::read(Lexer & lex)
467 {
468         lex.setContext("InsetSpaceParams::read");
469         string command;
470         lex >> command;
471
472         // The tests for math might be disabled after a file format change
473         if (command == "\\space{}")
474                 kind = InsetSpaceParams::NORMAL;
475         else if (command == "~")
476                 kind = InsetSpaceParams::PROTECTED;
477         else if (command == "\\textvisiblespace{}")
478                 kind = InsetSpaceParams::VISIBLE;
479         else if (command == "\\thinspace{}")
480                 kind = InsetSpaceParams::THIN;
481         else if (math && command == "\\medspace{}")
482                 kind = InsetSpaceParams::MEDIUM;
483         else if (math && command == "\\thickspace{}")
484                 kind = InsetSpaceParams::THICK;
485         else if (command == "\\quad{}")
486                 kind = InsetSpaceParams::QUAD;
487         else if (command == "\\qquad{}")
488                 kind = InsetSpaceParams::QQUAD;
489         else if (command == "\\enspace{}")
490                 kind = InsetSpaceParams::ENSPACE;
491         else if (command == "\\enskip{}")
492                 kind = InsetSpaceParams::ENSKIP;
493         else if (command == "\\negthinspace{}")
494                 kind = InsetSpaceParams::NEGTHIN;
495         else if (command == "\\negmedspace{}")
496                 kind = InsetSpaceParams::NEGMEDIUM;
497         else if (command == "\\negthickspace{}")
498                 kind = InsetSpaceParams::NEGTHICK;
499         else if (command == "\\hfill{}")
500                 kind = InsetSpaceParams::HFILL;
501         else if (command == "\\hspace*{\\fill}")
502                 kind = InsetSpaceParams::HFILL_PROTECTED;
503         else if (command == "\\dotfill{}")
504                 kind = InsetSpaceParams::DOTFILL;
505         else if (command == "\\hrulefill{}")
506                 kind = InsetSpaceParams::HRULEFILL;
507         else if (command == "\\hspace{}")
508                 kind = InsetSpaceParams::CUSTOM;
509         else if (command == "\\leftarrowfill{}")
510                 kind = InsetSpaceParams::LEFTARROWFILL;
511         else if (command == "\\rightarrowfill{}")
512                 kind = InsetSpaceParams::RIGHTARROWFILL;
513         else if (command == "\\upbracefill{}")
514                 kind = InsetSpaceParams::UPBRACEFILL;
515         else if (command == "\\downbracefill{}")
516                 kind = InsetSpaceParams::DOWNBRACEFILL;
517         else if (command == "\\hspace*{}")
518                 kind = InsetSpaceParams::CUSTOM_PROTECTED;
519         else
520                 lex.printError("InsetSpace: Unknown kind: `$$Token'");
521
522         if (lex.checkFor("\\length"))
523                 lex >> length;
524 }
525
526
527 void InsetSpace::write(ostream & os) const
528 {
529         os << "space ";
530         params_.write(os);
531 }
532
533
534 void InsetSpace::read(Lexer & lex)
535 {
536         params_.read(lex);
537         lex >> "\\end_inset";
538 }
539
540
541 void InsetSpace::latex(otexstream & os, OutputParams const & runparams) const
542 {
543         switch (params_.kind) {
544         case InsetSpaceParams::NORMAL:
545                 os << (runparams.free_spacing ? " " : "\\ ");
546                 break;
547         case InsetSpaceParams::PROTECTED:
548                 if (runparams.local_font &&
549                     runparams.local_font->language()->lang() == "polutonikogreek")
550                         // in babel's polutonikogreek, ~ is active
551                         os << (runparams.free_spacing ? " " : "\\nobreakspace{}");
552                 else
553                         os << (runparams.free_spacing ? ' ' : '~');
554                 break;
555         case InsetSpaceParams::VISIBLE:
556                 os << (runparams.free_spacing ? " " : "\\textvisiblespace{}");
557                 break;
558         case InsetSpaceParams::THIN:
559                 os << (runparams.free_spacing ? " " : "\\,");
560                 break;
561         case InsetSpaceParams::MEDIUM:
562                 os << (runparams.free_spacing ? " " : "\\:");
563                 break;
564         case InsetSpaceParams::THICK:
565                 os << (runparams.free_spacing ? " " : "\\;");
566                 break;
567         case InsetSpaceParams::QUAD:
568                 os << (runparams.free_spacing ? " " : "\\quad{}");
569                 break;
570         case InsetSpaceParams::QQUAD:
571                 os << (runparams.free_spacing ? " " : "\\qquad{}");
572                 break;
573         case InsetSpaceParams::ENSPACE:
574                 os << (runparams.free_spacing ? " " : "\\enspace{}");
575                 break;
576         case InsetSpaceParams::ENSKIP:
577                 os << (runparams.free_spacing ? " " : "\\enskip{}");
578                 break;
579         case InsetSpaceParams::NEGTHIN:
580                 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
581                 break;
582         case InsetSpaceParams::NEGMEDIUM:
583                 os << (runparams.free_spacing ? " " : "\\negmedspace{}");
584                 break;
585         case InsetSpaceParams::NEGTHICK:
586                 os << (runparams.free_spacing ? " " : "\\negthickspace{}");
587                 break;
588         case InsetSpaceParams::HFILL:
589                 os << (runparams.free_spacing ? " " : "\\hfill{}");
590                 break;
591         case InsetSpaceParams::HFILL_PROTECTED:
592                 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
593                 break;
594         case InsetSpaceParams::DOTFILL:
595                 os << (runparams.free_spacing ? " " : "\\dotfill{}");
596                 break;
597         case InsetSpaceParams::HRULEFILL:
598                 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
599                 break;
600         case InsetSpaceParams::LEFTARROWFILL:
601                 os << (runparams.free_spacing ? " " : "\\leftarrowfill{}");
602                 break;
603         case InsetSpaceParams::RIGHTARROWFILL:
604                 os << (runparams.free_spacing ? " " : "\\rightarrowfill{}");
605                 break;
606         case InsetSpaceParams::UPBRACEFILL:
607                 os << (runparams.free_spacing ? " " : "\\upbracefill{}");
608                 break;
609         case InsetSpaceParams::DOWNBRACEFILL:
610                 os << (runparams.free_spacing ? " " : "\\downbracefill{}");
611                 break;
612         case InsetSpaceParams::CUSTOM:
613                 if (runparams.free_spacing)
614                         os << " ";
615                 else
616                         os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
617                 break;
618         case InsetSpaceParams::CUSTOM_PROTECTED:
619                 if (runparams.free_spacing)
620                         os << " ";
621                 else
622                         os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
623                 break;
624         }
625 }
626
627
628 int InsetSpace::plaintext(odocstringstream & os,
629         OutputParams const &, size_t) const
630 {
631         switch (params_.kind) {
632         case InsetSpaceParams::HFILL:
633         case InsetSpaceParams::HFILL_PROTECTED:
634                 os << "     ";
635                 return 5;
636         case InsetSpaceParams::DOTFILL:
637                 os << ".....";
638                 return 5;
639         case InsetSpaceParams::HRULEFILL:
640                 os << "_____";
641                 return 5;
642         case InsetSpaceParams::LEFTARROWFILL:
643                 os << "<----";
644                 return 5;
645         case InsetSpaceParams::RIGHTARROWFILL:
646                 os << "---->";
647                 return 5;
648         case InsetSpaceParams::UPBRACEFILL:
649                 os << "\\-v-/";
650                 return 5;
651         case InsetSpaceParams::DOWNBRACEFILL:
652                 os << "/-^-\\";
653                 return 5;
654         case InsetSpaceParams::VISIBLE:
655                 os.put(0x2423);
656                 return 1;
657         case InsetSpaceParams::ENSKIP:
658                 os.put(0x2002);
659                 return 1;
660         case InsetSpaceParams::ENSPACE:
661                 os.put(0x2060); // WORD JOINER, makes the breakable en space unbreakable
662                 os.put(0x2002);
663                 os.put(0x2060); // WORD JOINER, makes the breakable en space unbreakable
664                 return 3;
665         case InsetSpaceParams::QUAD:
666                 os.put(0x2003);
667                 return 1;
668         case InsetSpaceParams::QQUAD:
669                 os.put(0x2003);
670                 os.put(0x2003);
671                 return 2;
672         case InsetSpaceParams::THIN:
673                 os.put(0x202f);
674                 return 1;
675         case InsetSpaceParams::MEDIUM:
676                 os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable medium space breakable
677                 os.put(0x2005);
678                 os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable medium space breakable
679                 return 1;
680         case InsetSpaceParams::THICK:
681                 os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable thick space breakable
682                 os.put(0x2004);
683                 os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable thick space breakable
684                 return 1;
685         case InsetSpaceParams::PROTECTED:
686         case InsetSpaceParams::CUSTOM_PROTECTED:
687                 os.put(0x00a0);
688                 return 1;
689         case InsetSpaceParams::NEGTHIN:
690         case InsetSpaceParams::NEGMEDIUM:
691         case InsetSpaceParams::NEGTHICK:
692                 return 0;
693         default:
694                 os << ' ';
695                 return 1;
696         }
697 }
698
699
700 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
701 {
702         switch (params_.kind) {
703         case InsetSpaceParams::NORMAL:
704                 os << " ";
705                 break;
706         case InsetSpaceParams::QUAD:
707                 os << "&emsp;";
708                 break;
709         case InsetSpaceParams::QQUAD:
710                 os << "&emsp;&emsp;";
711                 break;
712         case InsetSpaceParams::ENSKIP:
713                 os << "&ensp;";
714                 break;
715         case InsetSpaceParams::PROTECTED:
716                 os << "&nbsp;";
717                 break;
718         case InsetSpaceParams::VISIBLE:
719                 os << "&#x2423;";
720                 break;
721         case InsetSpaceParams::ENSPACE:
722                 os << "&#x2060;&ensp;&#x2060;";
723                 break;
724         case InsetSpaceParams::THIN:
725                 os << "&thinsp;";
726                 break;
727         case InsetSpaceParams::MEDIUM:
728                 os << "&emsp14;";
729                 break;
730         case InsetSpaceParams::THICK:
731                 os << "&emsp13;";
732                 break;
733         case InsetSpaceParams::NEGTHIN:
734         case InsetSpaceParams::NEGMEDIUM:
735         case InsetSpaceParams::NEGTHICK:
736                 // FIXME
737                 os << "&nbsp;";
738                 break;
739         case InsetSpaceParams::HFILL:
740         case InsetSpaceParams::HFILL_PROTECTED:
741                 os << '\n';
742                 break;
743         case InsetSpaceParams::DOTFILL:
744                 // FIXME
745                 os << '\n';
746                 break;
747         case InsetSpaceParams::HRULEFILL:
748                 // FIXME
749                 os << '\n';
750                 break;
751         case InsetSpaceParams::LEFTARROWFILL:
752         case InsetSpaceParams::RIGHTARROWFILL:
753         case InsetSpaceParams::UPBRACEFILL:
754         case InsetSpaceParams::DOWNBRACEFILL:
755         case InsetSpaceParams::CUSTOM:
756         case InsetSpaceParams::CUSTOM_PROTECTED:
757                 // FIXME
758                 os << '\n';
759                 break;
760         }
761         return 0;
762 }
763
764
765 docstring InsetSpace::xhtml(XHTMLStream & xs, OutputParams const &) const
766 {
767         string output;
768         switch (params_.kind) {
769         case InsetSpaceParams::NORMAL:
770                 output = " ";
771                 break;
772         case InsetSpaceParams::ENSKIP:
773                 output ="&ensp;";
774                 break;
775         case InsetSpaceParams::ENSPACE:
776                 output ="&#x2060;&ensp;&#x2060;";
777                 break;
778         case InsetSpaceParams::QQUAD:
779                 output ="&emsp;&emsp;";
780                 break;
781         case InsetSpaceParams::THICK:
782                 output ="&#x2004;";
783                 break;
784         case InsetSpaceParams::QUAD:
785                 output ="&emsp;";
786                 break;
787         case InsetSpaceParams::MEDIUM:
788                 output ="&#x2005;";
789                 break;
790         case InsetSpaceParams::THIN:
791                 output ="&thinsp;";
792                 break;
793         case InsetSpaceParams::PROTECTED:
794         case InsetSpaceParams::NEGTHIN:
795         case InsetSpaceParams::NEGMEDIUM:
796         case InsetSpaceParams::NEGTHICK:
797                 output ="&nbsp;";
798                 break;
799         // no XHTML entity, only unicode code for space character exists
800         case InsetSpaceParams::VISIBLE:
801                 output ="&#x2423;";
802                 break;
803         case InsetSpaceParams::HFILL:
804         case InsetSpaceParams::HFILL_PROTECTED:
805         case InsetSpaceParams::DOTFILL:
806         case InsetSpaceParams::HRULEFILL:
807         case InsetSpaceParams::LEFTARROWFILL:
808         case InsetSpaceParams::RIGHTARROWFILL:
809         case InsetSpaceParams::UPBRACEFILL:
810         case InsetSpaceParams::DOWNBRACEFILL:
811                 // FIXME XHTML
812                 // Can we do anything with those in HTML?
813                 break;
814         case InsetSpaceParams::CUSTOM:
815                 // FIXME XHTML
816                 // Probably we could do some sort of blank span?
817                 break;
818         case InsetSpaceParams::CUSTOM_PROTECTED:
819                 // FIXME XHTML
820                 // Probably we could do some sort of blank span?
821                 output ="&nbsp;";
822                 break;
823         }
824         // don't escape the entities!
825         xs << XHTMLStream::ESCAPE_NONE << from_ascii(output);
826         return docstring();
827 }
828
829
830 void InsetSpace::validate(LaTeXFeatures & features) const
831 {
832         if (params_.kind == InsetSpaceParams::NEGMEDIUM ||
833             params_.kind == InsetSpaceParams::NEGTHICK)
834                 features.require("amsmath");
835 }
836
837
838 void InsetSpace::toString(odocstream & os) const
839 {
840         odocstringstream ods;
841         plaintext(ods, OutputParams(0));
842         os << ods.str();
843 }
844
845
846 void InsetSpace::forOutliner(docstring & os, size_t const, bool const) const
847 {
848         // There's no need to be cute here.
849         os += " ";
850 }
851
852
853 bool InsetSpace::isHfill() const
854 {
855         return params_.kind == InsetSpaceParams::HFILL
856                 || params_.kind == InsetSpaceParams::HFILL_PROTECTED
857                 || params_.kind == InsetSpaceParams::DOTFILL
858                 || params_.kind == InsetSpaceParams::HRULEFILL
859                 || params_.kind == InsetSpaceParams::LEFTARROWFILL
860                 || params_.kind == InsetSpaceParams::RIGHTARROWFILL
861                 || params_.kind == InsetSpaceParams::UPBRACEFILL
862                 || params_.kind == InsetSpaceParams::DOWNBRACEFILL;
863 }
864
865
866 string InsetSpace::contextMenuName() const
867 {
868         return "context-space";
869 }
870
871
872 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
873 {
874         params = InsetSpaceParams();
875         if (in.empty())
876                 return;
877
878         istringstream data(in);
879         Lexer lex;
880         lex.setStream(data);
881         lex.setContext("InsetSpace::string2params");
882         lex.next();
883         string const name = lex.getString();
884         if (name == "mathspace")
885                 params.math = true;
886         else {
887                 params.math = false;
888                 // we can try to read this even if the name is wrong
889                 LATTEST(name == "space");
890         }
891
892         // There are cases, such as when we are called via getStatus() from
893         // Dialog::canApply(), where we are just called with "space" rather
894         // than a full "space \type{}\n\\end_inset".
895         if (lex.isOK())
896                 params.read(lex);
897 }
898
899
900 string InsetSpace::params2string(InsetSpaceParams const & params)
901 {
902         ostringstream data;
903         if (params.math)
904                 data << "math";
905         data << "space" << ' ';
906         params.write(data);
907         return data.str();
908 }
909
910
911 } // namespace lyx