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