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