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