]> git.lyx.org Git - features.git/blob - src/insets/InsetSpace.cpp
InsetQuote works again.
[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
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 InsetSpace::~InsetSpace()
64 {
65         hideDialogs("space", this);
66 }
67
68
69 docstring InsetSpace::toolTip(BufferView const &, int, int) const
70 {
71         docstring message;
72         switch (params_.kind) {
73         case InsetSpaceParams::NORMAL:
74                 message = _("Interword Space");
75                 break;
76         case InsetSpaceParams::PROTECTED:
77                 message = _("Protected Space");
78                 break;
79         case InsetSpaceParams::THIN:
80                 message = _("Thin Space");
81                 break;
82         case InsetSpaceParams::MEDIUM:
83                 message = _("Medium Space");
84                 break;
85         case InsetSpaceParams::THICK:
86                 message = _("Thick Space");
87                 break;
88         case InsetSpaceParams::QUAD:
89                 message = _("Quad Space");
90                 break;
91         case InsetSpaceParams::QQUAD:
92                 message = _("QQuad Space");
93                 break;
94         case InsetSpaceParams::ENSPACE:
95                 message = _("Enspace");
96                 break;
97         case InsetSpaceParams::ENSKIP:
98                 message = _("Enskip");
99                 break;
100         case InsetSpaceParams::NEGTHIN:
101                 message = _("Negative Thin Space");
102                 break;
103         case InsetSpaceParams::NEGMEDIUM:
104                 message = _("Negative Medium Space");
105                 break;
106         case InsetSpaceParams::NEGTHICK:
107                 message = _("Negative Thick Space");
108                 break;
109         case InsetSpaceParams::HFILL:
110                 message = _("Horizontal Fill");
111                 break;
112         case InsetSpaceParams::HFILL_PROTECTED:
113                 message = _("Protected Horizontal Fill");
114                 break;
115         case InsetSpaceParams::DOTFILL:
116                 message = _("Horizontal Fill (Dots)");
117                 break;
118         case InsetSpaceParams::HRULEFILL:
119                 message = _("Horizontal Fill (Rule)");
120                 break;
121         case InsetSpaceParams::LEFTARROWFILL:
122                 message = _("Horizontal Fill (Left Arrow)");
123                 break;
124         case InsetSpaceParams::RIGHTARROWFILL:
125                 message = _("Horizontal Fill (Right Arrow)");
126                 break;
127         case InsetSpaceParams::UPBRACEFILL:
128                 message = _("Horizontal Fill (Up Brace)");
129                 break;
130         case InsetSpaceParams::DOWNBRACEFILL:
131                 message = _("Horizontal Fill (Down Brace)");
132                 break;
133         case InsetSpaceParams::CUSTOM:
134                 // FIXME unicode
135                 message = support::bformat(_("Horizontal Space (%1$s)"),
136                                 from_ascii(params_.length.asString()));
137                 break;
138         case InsetSpaceParams::CUSTOM_PROTECTED:
139                 // FIXME unicode
140                 message = support::bformat(_("Protected Horizontal Space (%1$s)"),
141                                 from_ascii(params_.length.asString()));
142                 break;
143         }
144         return message;
145 }
146
147
148 void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
149 {
150         switch (cmd.action) {
151
152         case LFUN_INSET_MODIFY:
153                 string2params(to_utf8(cmd.argument()), params_);
154                 break;
155
156         case LFUN_INSET_DIALOG_UPDATE:
157                 cur.bv().updateDialog("space", params2string(params()));
158                 break;
159
160         default:
161                 Inset::doDispatch(cur, cmd);
162                 break;
163         }
164 }
165
166
167 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
168         FuncStatus & status) const
169 {
170         switch (cmd.action) {
171         // we handle these
172         case LFUN_INSET_MODIFY:
173                 if (cmd.getArg(0) == "space") {
174                         InsetSpaceParams params;
175                         string2params(to_utf8(cmd.argument()), params);
176                         status.setOnOff(params_.kind == params.kind);
177                 }
178                 // fall through
179         case LFUN_INSET_DIALOG_UPDATE:
180                 status.setEnabled(true);
181                 return true;
182         default:
183                 return Inset::getStatus(cur, cmd, status);
184         }
185 }
186
187
188 bool InsetSpace::showInsetDialog(BufferView * bv) const
189 {
190         bv->showDialog("space", params2string(params()),
191                 const_cast<InsetSpace *>(this));
192         return true;
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 (isStretchableSpace()) {
204                 // The metrics for this kinds are calculated externally in
205                 // \c TextMetrics::computeRowMetrics. Those are dummy value:
206                 dim = Dimension(10, 10, 10);
207                 return;
208         }
209
210         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
211         dim.asc = fm.maxAscent();
212         dim.des = fm.maxDescent();
213
214         switch (params_.kind) {
215                 case InsetSpaceParams::THIN:
216                 case InsetSpaceParams::NEGTHIN:
217                         dim.wid = fm.width(char_type('M')) / 6;
218                         break;
219                 case InsetSpaceParams::MEDIUM:
220                 case InsetSpaceParams::NEGMEDIUM:
221                         dim.wid = fm.width(char_type('M')) / 4;
222                         break;
223                 case InsetSpaceParams::THICK:
224                 case InsetSpaceParams::NEGTHICK:
225                         dim.wid = fm.width(char_type('M')) / 2;
226                         break;
227                 case InsetSpaceParams::PROTECTED:
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                 xp[1] = x;     yp[1] = y;
364                 xp[2] = x + w; yp[2] = y;
365         } else {
366                 xp[1] = x;     yp[1] = y + max(h / 4, 1);
367                 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
368         }
369         xp[3] = x + w;
370         yp[3] = y - max(h / 4, 1);
371
372         if (params_.kind == InsetSpaceParams::PROTECTED ||
373             params_.kind == InsetSpaceParams::ENSPACE ||
374             params_.kind == InsetSpaceParams::NEGTHIN ||
375             params_.kind == InsetSpaceParams::NEGMEDIUM ||
376             params_.kind == InsetSpaceParams::NEGTHICK ||
377             params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
378                 pi.pain.lines(xp, yp, 4, Color_latex);
379         else
380                 pi.pain.lines(xp, yp, 4, Color_special);
381 }
382
383
384 void InsetSpaceParams::write(ostream & os) const
385 {
386         string command;
387         switch (kind) {
388         case InsetSpaceParams::NORMAL:
389                 os << "\\space{}";
390                 break;
391         case InsetSpaceParams::PROTECTED:
392                 os <<  "~";
393                 break;
394         case InsetSpaceParams::THIN:
395                 os <<  "\\thinspace{}";
396                 break;
397         case InsetSpaceParams::MEDIUM:
398                 os <<  "\\medspace{}";
399                 break;
400         case InsetSpaceParams::THICK:
401                 os <<  "\\thickspace{}";
402                 break;
403         case InsetSpaceParams::QUAD:
404                 os <<  "\\quad{}";
405                 break;
406         case InsetSpaceParams::QQUAD:
407                 os <<  "\\qquad{}";
408                 break;
409         case InsetSpaceParams::ENSPACE:
410                 os <<  "\\enspace{}";
411                 break;
412         case InsetSpaceParams::ENSKIP:
413                 os <<  "\\enskip{}";
414                 break;
415         case InsetSpaceParams::NEGTHIN:
416                 os <<  "\\negthinspace{}";
417                 break;
418         case InsetSpaceParams::NEGMEDIUM:
419                 os <<  "\\negmedspace{}";
420                 break;
421         case InsetSpaceParams::NEGTHICK:
422                 os <<  "\\negthickspace{}";
423                 break;
424         case InsetSpaceParams::HFILL:
425                 os <<  "\\hfill{}";
426                 break;
427         case InsetSpaceParams::HFILL_PROTECTED:
428                 os <<  "\\hspace*{\\fill}";
429                 break;
430         case InsetSpaceParams::DOTFILL:
431                 os <<  "\\dotfill{}";
432                 break;
433         case InsetSpaceParams::HRULEFILL:
434                 os <<  "\\hrulefill{}";
435                 break;
436         case InsetSpaceParams::LEFTARROWFILL:
437                 os <<  "\\leftarrowfill{}";
438                 break;
439         case InsetSpaceParams::RIGHTARROWFILL:
440                 os <<  "\\rightarrowfill{}";
441                 break;
442         case InsetSpaceParams::UPBRACEFILL:
443                 os <<  "\\upbracefill{}";
444                 break;
445         case InsetSpaceParams::DOWNBRACEFILL:
446                 os <<  "\\downbracefill{}";
447                 break;
448         case InsetSpaceParams::CUSTOM:
449                 os <<  "\\hspace{}";
450                 break;
451         case InsetSpaceParams::CUSTOM_PROTECTED:
452                 os <<  "\\hspace*{}";
453                 break;
454         }
455         
456         if (!length.len().empty())
457                 os << "\n\\length " << length.asString();
458 }
459
460
461 void InsetSpaceParams::read(Lexer & lex)
462 {
463         lex.setContext("InsetSpaceParams::read");
464         string command;
465         lex >> command;
466
467         // The tests for math might be disabled after a file format change
468         if (command == "\\space{}")
469                 kind = InsetSpaceParams::NORMAL;
470         else if (command == "~")
471                 kind = InsetSpaceParams::PROTECTED;
472         else if (command == "\\thinspace{}")
473                 kind = InsetSpaceParams::THIN;
474         else if (math && command == "\\medspace{}")
475                 kind = InsetSpaceParams::MEDIUM;
476         else if (math && command == "\\thickspace{}")
477                 kind = InsetSpaceParams::THICK;
478         else if (command == "\\quad{}")
479                 kind = InsetSpaceParams::QUAD;
480         else if (command == "\\qquad{}")
481                 kind = InsetSpaceParams::QQUAD;
482         else if (command == "\\enspace{}")
483                 kind = InsetSpaceParams::ENSPACE;
484         else if (command == "\\enskip{}")
485                 kind = InsetSpaceParams::ENSKIP;
486         else if (command == "\\negthinspace{}")
487                 kind = InsetSpaceParams::NEGTHIN;
488         else if (math && command == "\\negmedspace{}")
489                 kind = InsetSpaceParams::NEGMEDIUM;
490         else if (math && command == "\\negthickspace{}")
491                 kind = InsetSpaceParams::NEGTHICK;
492         else if (command == "\\hfill{}")
493                 kind = InsetSpaceParams::HFILL;
494         else if (command == "\\hspace*{\\fill}")
495                 kind = InsetSpaceParams::HFILL_PROTECTED;
496         else if (command == "\\dotfill{}")
497                 kind = InsetSpaceParams::DOTFILL;
498         else if (command == "\\hrulefill{}")
499                 kind = InsetSpaceParams::HRULEFILL;
500         else if (command == "\\hspace{}")
501                 kind = InsetSpaceParams::CUSTOM;
502         else if (command == "\\leftarrowfill{}")
503                 kind = InsetSpaceParams::LEFTARROWFILL;
504         else if (command == "\\rightarrowfill{}")
505                 kind = InsetSpaceParams::RIGHTARROWFILL;
506         else if (command == "\\upbracefill{}")
507                 kind = InsetSpaceParams::UPBRACEFILL;
508         else if (command == "\\downbracefill{}")
509                 kind = InsetSpaceParams::DOWNBRACEFILL;
510         else if (command == "\\hspace*{}")
511                 kind = InsetSpaceParams::CUSTOM_PROTECTED;
512         else
513                 lex.printError("InsetSpace: Unknown kind: `$$Token'");
514
515         if (lex.checkFor("\\length"))
516                 lex >> length;
517 }
518
519
520 void InsetSpace::write(ostream & os) const
521 {
522         os << "space ";
523         params_.write(os);
524 }
525
526
527 void InsetSpace::read(Lexer & lex)
528 {
529         params_.read(lex);
530         lex >> "\\end_inset";
531 }
532
533
534 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
535 {
536         switch (params_.kind) {
537         case InsetSpaceParams::NORMAL:
538                 os << (runparams.free_spacing ? " " : "\\ ");
539                 break;
540         case InsetSpaceParams::PROTECTED:
541                 if (runparams.local_font &&
542                     runparams.local_font->language()->lang() == "polutonikogreek")
543                         // in babel's polutonikogreek, ~ is active
544                         os << (runparams.free_spacing ? " " : "\\nobreakspace{}");
545                 else
546                         os << (runparams.free_spacing ? ' ' : '~');
547                 break;
548         case InsetSpaceParams::THIN:
549                 os << (runparams.free_spacing ? " " : "\\,");
550                 break;
551         case InsetSpaceParams::MEDIUM:
552                 os << (runparams.free_spacing ? " " : "\\:");
553                 break;
554         case InsetSpaceParams::THICK:
555                 os << (runparams.free_spacing ? " " : "\\;");
556                 break;
557         case InsetSpaceParams::QUAD:
558                 os << (runparams.free_spacing ? " " : "\\quad{}");
559                 break;
560         case InsetSpaceParams::QQUAD:
561                 os << (runparams.free_spacing ? " " : "\\qquad{}");
562                 break;
563         case InsetSpaceParams::ENSPACE:
564                 os << (runparams.free_spacing ? " " : "\\enspace{}");
565                 break;
566         case InsetSpaceParams::ENSKIP:
567                 os << (runparams.free_spacing ? " " : "\\enskip{}");
568                 break;
569         case InsetSpaceParams::NEGTHIN:
570                 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
571                 break;
572         case InsetSpaceParams::NEGMEDIUM:
573                 os << (runparams.free_spacing ? " " : "\\negmedspace{}");
574                 break;
575         case InsetSpaceParams::NEGTHICK:
576                 os << (runparams.free_spacing ? " " : "\\negthickspace{}");
577                 break;
578         case InsetSpaceParams::HFILL:
579                 os << (runparams.free_spacing ? " " : "\\hfill{}");
580                 break;
581         case InsetSpaceParams::HFILL_PROTECTED:
582                 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
583                 break;
584         case InsetSpaceParams::DOTFILL:
585                 os << (runparams.free_spacing ? " " : "\\dotfill{}");
586                 break;
587         case InsetSpaceParams::HRULEFILL:
588                 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
589                 break;
590         case InsetSpaceParams::LEFTARROWFILL:
591                 os << (runparams.free_spacing ? " " : "\\leftarrowfill{}");
592                 break;
593         case InsetSpaceParams::RIGHTARROWFILL:
594                 os << (runparams.free_spacing ? " " : "\\rightarrowfill{}");
595                 break;
596         case InsetSpaceParams::UPBRACEFILL:
597                 os << (runparams.free_spacing ? " " : "\\upbracefill{}");
598                 break;
599         case InsetSpaceParams::DOWNBRACEFILL:
600                 os << (runparams.free_spacing ? " " : "\\downbracefill{}");
601                 break;
602         case InsetSpaceParams::CUSTOM:
603                 if (runparams.free_spacing)
604                         os << " ";
605                 else
606                         os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
607                 break;
608         case InsetSpaceParams::CUSTOM_PROTECTED:
609                 if (runparams.free_spacing)
610                         os << " ";
611                 else
612                         os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
613                 break;
614         }
615         return 0;
616 }
617
618
619 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
620 {
621         switch (params_.kind) {
622         case InsetSpaceParams::HFILL:
623         case InsetSpaceParams::HFILL_PROTECTED:
624                 os << "     ";
625                 return 5;
626         case InsetSpaceParams::DOTFILL:
627                 os << ".....";
628                 return 5;
629         case InsetSpaceParams::HRULEFILL:
630                 os << "_____";
631                 return 5;
632         case InsetSpaceParams::LEFTARROWFILL:
633                 os << "<----";
634                 return 5;
635         case InsetSpaceParams::RIGHTARROWFILL:
636                 os << "---->";
637                 return 5;
638         case InsetSpaceParams::UPBRACEFILL:
639                 os << "\\-v-/";
640                 return 5;
641         case InsetSpaceParams::DOWNBRACEFILL:
642                 os << "/-^-\\";
643                 return 5;
644         default:
645                 os << ' ';
646                 return 1;
647         }
648 }
649
650
651 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
652 {
653         switch (params_.kind) {
654         case InsetSpaceParams::NORMAL:
655         case InsetSpaceParams::QUAD:
656         case InsetSpaceParams::QQUAD:
657         case InsetSpaceParams::ENSKIP:
658                 os << " ";
659                 break;
660         case InsetSpaceParams::PROTECTED:
661         case InsetSpaceParams::ENSPACE:
662         case InsetSpaceParams::THIN:
663         case InsetSpaceParams::MEDIUM:
664         case InsetSpaceParams::THICK:
665         case InsetSpaceParams::NEGTHIN:
666         case InsetSpaceParams::NEGMEDIUM:
667         case InsetSpaceParams::NEGTHICK:
668                 os << "&nbsp;";
669                 break;
670         case InsetSpaceParams::HFILL:
671         case InsetSpaceParams::HFILL_PROTECTED:
672                 os << '\n';
673         case InsetSpaceParams::DOTFILL:
674                 // FIXME
675                 os << '\n';
676         case InsetSpaceParams::HRULEFILL:
677                 // FIXME
678                 os << '\n';
679         case InsetSpaceParams::LEFTARROWFILL:
680         case InsetSpaceParams::RIGHTARROWFILL:
681         case InsetSpaceParams::UPBRACEFILL:
682         case InsetSpaceParams::DOWNBRACEFILL:
683         case InsetSpaceParams::CUSTOM:
684         case InsetSpaceParams::CUSTOM_PROTECTED:
685                 // FIXME
686                 os << '\n';
687         }
688         return 0;
689 }
690
691
692 docstring InsetSpace::xhtml(XHTMLStream & xs, OutputParams const &) const
693 {
694         string output;
695         switch (params_.kind) {
696         case InsetSpaceParams::NORMAL:
697                 output = " ";
698                 break;
699         case InsetSpaceParams::ENSKIP:
700         case InsetSpaceParams::ENSPACE:
701                 output ="&ensp;";
702                 break;
703         case InsetSpaceParams::QQUAD:
704                 output ="&emsp;";
705         case InsetSpaceParams::THICK:
706         case InsetSpaceParams::QUAD:
707                 output ="&emsp;";
708                 break;
709         case InsetSpaceParams::THIN:
710                 output ="&thinsp;";
711                 break;
712         case InsetSpaceParams::PROTECTED:
713         case InsetSpaceParams::MEDIUM:
714         case InsetSpaceParams::NEGTHIN:
715         case InsetSpaceParams::NEGMEDIUM:
716         case InsetSpaceParams::NEGTHICK:
717                 output ="&nbsp;";
718                 break;
719         case InsetSpaceParams::HFILL:
720         case InsetSpaceParams::HFILL_PROTECTED:
721         case InsetSpaceParams::DOTFILL:
722         case InsetSpaceParams::HRULEFILL:
723         case InsetSpaceParams::LEFTARROWFILL:
724         case InsetSpaceParams::RIGHTARROWFILL:
725         case InsetSpaceParams::UPBRACEFILL:
726         case InsetSpaceParams::DOWNBRACEFILL:
727                 // FIXME XHTML
728                 // Can we do anything with those in HTML?
729                 break;
730         case InsetSpaceParams::CUSTOM:
731         case InsetSpaceParams::CUSTOM_PROTECTED:
732                 // FIXME XHTML
733                 // Probably we could do some sort of blank span?
734                 break;
735         }
736         // don't escape the entities!
737         xs << XHTMLStream::NextRaw() << from_ascii(output);
738         return docstring();
739 }
740
741
742 void InsetSpace::validate(LaTeXFeatures & features) const
743 {
744         if (params_.kind == InsetSpaceParams::NEGMEDIUM ||
745             params_.kind == InsetSpaceParams::NEGTHICK) 
746                 features.require("amsmath");
747 }
748
749
750 void InsetSpace::tocString(odocstream & os) const
751 {
752         plaintext(os, OutputParams(0));
753 }
754
755
756 bool InsetSpace::isStretchableSpace() const
757 {
758         return params_.kind == InsetSpaceParams::HFILL
759                 || params_.kind == InsetSpaceParams::HFILL_PROTECTED
760                 || params_.kind == InsetSpaceParams::DOTFILL
761                 || params_.kind == InsetSpaceParams::HRULEFILL
762                 || params_.kind == InsetSpaceParams::LEFTARROWFILL
763                 || params_.kind == InsetSpaceParams::RIGHTARROWFILL
764                 || params_.kind == InsetSpaceParams::UPBRACEFILL
765                 || params_.kind == InsetSpaceParams::DOWNBRACEFILL;
766 }
767
768
769 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
770 {
771         return from_ascii("context-space");
772 }
773
774
775 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
776 {
777         params = InsetSpaceParams();
778         if (in.empty())
779                 return;
780
781         istringstream data(in);
782         Lexer lex;
783         lex.setStream(data);
784         lex.setContext("InsetSpace::string2params");
785         lex.next();
786         string const name = lex.getString();
787         if (name == "mathspace")
788                 params.math = true;
789         else {
790                 params.math = false;
791                 LASSERT(name == "space", /**/);
792         }
793
794         // There are cases, such as when we are called via getStatus() from
795         // Dialog::canApply(), where we are just called with "space" rather
796         // than a full "space \type{}\n\\end_inset".
797         if (lex.isOK())
798                 params.read(lex);
799 }
800
801
802 string InsetSpace::params2string(InsetSpaceParams const & params)
803 {
804         ostringstream data;
805         if (params.math)
806                 data << "math";
807         data << "space" << ' ';
808         params.write(data);
809         return data.str();
810 }
811
812
813 } // namespace lyx