]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetSpace.cpp
Unify QQaud strings.
[lyx.git] / src / insets / InsetSpace.cpp
index bbab7f278e73cabf0b3c9a2d37abd0bb44973516..a419a684648477620ed6dbccd0a6f846ebe5f9f1 100644 (file)
@@ -5,8 +5,8 @@
  *
  * \author Asger Alstrup Nielsen
  * \author Jean-Marc Lasgouttes
- * \author Lars Gullik Bjønnes
- * \author Jürgen Spitzmüller
+ * \author Lars Gullik Bjønnes
+ * \author Jürgen Spitzmüller
  *
  * Full author contact details are available in file CREDITS.
  */
 
 #include "InsetSpace.h"
 
-#include "debug.h"
-#include "Color.h"
+#include "BufferView.h"
+#include "Cursor.h"
+#include "Dimension.h"
+#include "FuncRequest.h"
+#include "FuncStatus.h"
+#include "Language.h"
+#include "LaTeXFeatures.h"
+#include "Length.h"
 #include "Lexer.h"
 #include "MetricsInfo.h"
 #include "OutputParams.h"
+#include "output_xhtml.h"
 
+#include "support/debug.h"
+#include "support/docstream.h"
+#include "support/gettext.h"
+#include "support/lassert.h"
+#include "support/lstrings.h"
+
+#include "frontends/Application.h"
 #include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 
+using namespace std;
 
 namespace lyx {
 
-using std::string;
-using std::max;
-using std::ostream;
-
 
-InsetSpace::InsetSpace()
+InsetSpace::InsetSpace(InsetSpaceParams const & params)
+       : Inset(0), params_(params)
 {}
 
 
-InsetSpace::InsetSpace(Kind k)
-       : kind_(k)
-{}
+InsetSpaceParams::Kind InsetSpace::kind() const
+{
+       return params_.kind;
+}
 
 
-InsetSpace::Kind InsetSpace::kind() const
+GlueLength InsetSpace::length() const
 {
-       return kind_;
+       return params_.length;
+}
+
+
+docstring InsetSpace::toolTip(BufferView const &, int, int) const
+{
+       docstring message;
+       switch (params_.kind) {
+       case InsetSpaceParams::NORMAL:
+               message = _("Interword Space");
+               break;
+       case InsetSpaceParams::PROTECTED:
+               message = _("Protected Space");
+               break;
+       case InsetSpaceParams::THIN:
+               message = _("Thin Space");
+               break;
+       case InsetSpaceParams::MEDIUM:
+               message = _("Medium Space");
+               break;
+       case InsetSpaceParams::THICK:
+               message = _("Thick Space");
+               break;
+       case InsetSpaceParams::QUAD:
+               message = _("Quad Space");
+               break;
+       case InsetSpaceParams::QQUAD:
+               message = _(" Double Quad Space");
+               break;
+       case InsetSpaceParams::ENSPACE:
+               message = _("Enspace");
+               break;
+       case InsetSpaceParams::ENSKIP:
+               message = _("Enskip");
+               break;
+       case InsetSpaceParams::NEGTHIN:
+               message = _("Negative Thin Space");
+               break;
+       case InsetSpaceParams::NEGMEDIUM:
+               message = _("Negative Medium Space");
+               break;
+       case InsetSpaceParams::NEGTHICK:
+               message = _("Negative Thick Space");
+               break;
+       case InsetSpaceParams::HFILL:
+               message = _("Horizontal Fill");
+               break;
+       case InsetSpaceParams::HFILL_PROTECTED:
+               message = _("Protected Horizontal Fill");
+               break;
+       case InsetSpaceParams::DOTFILL:
+               message = _("Horizontal Fill (Dots)");
+               break;
+       case InsetSpaceParams::HRULEFILL:
+               message = _("Horizontal Fill (Rule)");
+               break;
+       case InsetSpaceParams::LEFTARROWFILL:
+               message = _("Horizontal Fill (Left Arrow)");
+               break;
+       case InsetSpaceParams::RIGHTARROWFILL:
+               message = _("Horizontal Fill (Right Arrow)");
+               break;
+       case InsetSpaceParams::UPBRACEFILL:
+               message = _("Horizontal Fill (Up Brace)");
+               break;
+       case InsetSpaceParams::DOWNBRACEFILL:
+               message = _("Horizontal Fill (Down Brace)");
+               break;
+       case InsetSpaceParams::CUSTOM:
+               // FIXME unicode
+               message = support::bformat(_("Horizontal Space (%1$s)"),
+                               from_ascii(params_.length.asString()));
+               break;
+       case InsetSpaceParams::CUSTOM_PROTECTED:
+               // FIXME unicode
+               message = support::bformat(_("Protected Horizontal Space (%1$s)"),
+                               from_ascii(params_.length.asString()));
+               break;
+       }
+       return message;
+}
+
+
+void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
+{
+       switch (cmd.action()) {
+
+       case LFUN_INSET_MODIFY:
+               string2params(to_utf8(cmd.argument()), params_);
+               break;
+
+       case LFUN_INSET_DIALOG_UPDATE:
+               cur.bv().updateDialog("space", params2string(params()));
+               break;
+
+       default:
+               Inset::doDispatch(cur, cmd);
+               break;
+       }
+}
+
+
+bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
+       FuncStatus & status) const
+{
+       switch (cmd.action()) {
+       // we handle these
+       case LFUN_INSET_MODIFY:
+               if (cmd.getArg(0) == "space") {
+                       InsetSpaceParams params;
+                       string2params(to_utf8(cmd.argument()), params);
+                       status.setOnOff(params_.kind == params.kind);
+               }
+               // fall through
+       case LFUN_INSET_DIALOG_UPDATE:
+               status.setEnabled(true);
+               return true;
+       default:
+               return Inset::getStatus(cur, cmd, status);
+       }
+}
+
+
+namespace {
+int const arrow_size = 8;
 }
 
 
 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
 {
-       frontend::FontMetrics const & fm =
-               theFontMetrics(mi.base.font);
+       if (isStretchableSpace()) {
+               // The metrics for this kinds are calculated externally in
+               // \c TextMetrics::computeRowMetrics. Those are dummy value:
+               dim = Dimension(10, 10, 10);
+               return;
+       }
+
+       frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
        dim.asc = fm.maxAscent();
        dim.des = fm.maxDescent();
 
-       switch (kind_) {
-               case THIN:
-               case NEGTHIN:
-                   dim.wid = fm.width(char_type('x')) / 3;
+       switch (params_.kind) {
+               case InsetSpaceParams::THIN:
+               case InsetSpaceParams::NEGTHIN:
+                       dim.wid = fm.width(char_type('M')) / 6;
+                       break;
+               case InsetSpaceParams::MEDIUM:
+               case InsetSpaceParams::NEGMEDIUM:
+                       dim.wid = fm.width(char_type('M')) / 4;
                        break;
-               case PROTECTED:
-               case NORMAL:
-                   dim.wid = fm.width(char_type('x'));
+               case InsetSpaceParams::THICK:
+               case InsetSpaceParams::NEGTHICK:
+                       dim.wid = fm.width(char_type('M')) / 2;
                        break;
-               case QUAD:
-                       dim.wid = 20;
+               case InsetSpaceParams::PROTECTED:
+               case InsetSpaceParams::NORMAL:
+                       dim.wid = fm.width(char_type(' '));
                        break;
-               case QQUAD:
-                       dim.wid = 40;
+               case InsetSpaceParams::QUAD:
+                       dim.wid = fm.width(char_type('M'));
                        break;
-               case ENSPACE:
-               case ENSKIP:
-                       dim.wid = 10;
+               case InsetSpaceParams::QQUAD:
+                       dim.wid = 2 * fm.width(char_type('M'));
+                       break;
+               case InsetSpaceParams::ENSPACE:
+               case InsetSpaceParams::ENSKIP:
+                       dim.wid = int(0.5 * fm.width(char_type('M')));
+                       break;
+               case InsetSpaceParams::CUSTOM:
+               case InsetSpaceParams::CUSTOM_PROTECTED: {
+                       int const w = 
+                               params_.length.len().inPixels(mi.base.textwidth,
+                                                       fm.width(char_type('M')));
+                       int const minw = (w < 0) ? 3 * arrow_size : 4;
+                       dim.wid = max(minw, abs(w));
+                       break;
+               }
+               case InsetSpaceParams::HFILL:
+               case InsetSpaceParams::HFILL_PROTECTED:
+               case InsetSpaceParams::DOTFILL:
+               case InsetSpaceParams::HRULEFILL:
+               case InsetSpaceParams::LEFTARROWFILL:
+               case InsetSpaceParams::RIGHTARROWFILL:
+               case InsetSpaceParams::UPBRACEFILL:
+               case InsetSpaceParams::DOWNBRACEFILL:
+                       // shut up compiler
                        break;
        }
-       // Cache the inset dimension. 
+       // Cache the inset dimension.
        setDimCache(mi, dim);
 }
 
@@ -82,14 +252,100 @@ void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
 {
        Dimension const dim = dimension(*pi.base.bv);
+
+       if (isStretchableSpace() || params_.length.len().value() < 0) {
+               int const asc = theFontMetrics(pi.base.font).ascent('M');
+               int const desc = theFontMetrics(pi.base.font).descent('M');
+               // Pixel height divisible by 2 for prettier fill graphics:
+               int const oddheight = (asc ^ desc) & 1;
+               int const x0 = x + 1;
+               int const x1 = x + dim.wid - 2;
+               int const y0 = y + desc - 1;
+               int const y1 = y - asc + oddheight - 1;
+               int const y2 = (y0 + y1) / 2;
+               int xoffset = (y0 - y1) / 2;
+
+               // Two tests for very narrow insets
+               if (xoffset > x1 - x0
+                    && (params_.kind == InsetSpaceParams::LEFTARROWFILL
+                        || params_.kind == InsetSpaceParams::RIGHTARROWFILL))
+                               xoffset = x1 - x0;
+               if (xoffset * 6 > (x1 - x0)
+                    && (params_.kind == InsetSpaceParams::UPBRACEFILL
+                        || params_.kind == InsetSpaceParams::DOWNBRACEFILL))
+                               xoffset = (x1 - x0) / 6;
+
+               int const x2 = x0 + xoffset;
+               int const x3 = x1 - xoffset;
+               int const xm = (x0 + x1) / 2;
+               int const xml = xm - xoffset;
+               int const xmr = xm + xoffset;
+
+               if (params_.kind == InsetSpaceParams::HFILL) {
+                       pi.pain.line(x0, y1, x0, y0, Color_added_space);
+                       pi.pain.line(x0, y2, x1, y2, Color_added_space,
+                               frontend::Painter::line_onoffdash);
+                       pi.pain.line(x1, y1, x1, y0, Color_added_space);
+               } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
+                       pi.pain.line(x0, y1, x0, y0, Color_latex);
+                       pi.pain.line(x0, y2, x1, y2, Color_latex,
+                               frontend::Painter::line_onoffdash);
+                       pi.pain.line(x1, y1, x1, y0, Color_latex);
+               } else if (params_.kind == InsetSpaceParams::DOTFILL) {
+                       pi.pain.line(x0, y1, x0, y0, Color_special);
+                       pi.pain.line(x0, y0, x1, y0, Color_special,
+                               frontend::Painter::line_onoffdash);
+                       pi.pain.line(x1, y1, x1, y0, Color_special);
+               } else if (params_.kind == InsetSpaceParams::HRULEFILL) {
+                       pi.pain.line(x0, y1, x0, y0, Color_special);
+                       pi.pain.line(x0, y0, x1, y0, Color_special);
+                       pi.pain.line(x1, y1, x1, y0, Color_special);
+               } else if (params_.kind == InsetSpaceParams::LEFTARROWFILL) {
+                       pi.pain.line(x2, y1 + 1 , x0 + 1, y2, Color_special);
+                       pi.pain.line(x0 + 1, y2 + 1 , x2, y0, Color_special);
+                       pi.pain.line(x0, y2 , x1, y2, Color_special);
+               } else if (params_.kind == InsetSpaceParams::RIGHTARROWFILL) {
+                       pi.pain.line(x3 + 1, y1 + 1 , x1, y2, Color_special);
+                       pi.pain.line(x1, y2 + 1 , x3 + 1, y0, Color_special);
+                       pi.pain.line(x0, y2 , x1, y2, Color_special);
+               } else if (params_.kind == InsetSpaceParams::UPBRACEFILL) {
+                       pi.pain.line(x0 + 1, y1 + 1 , x2, y2, Color_special);
+                       pi.pain.line(x2, y2 , xml, y2, Color_special);
+                       pi.pain.line(xml + 1, y2 + 1 , xm, y0, Color_special);
+                       pi.pain.line(xm + 1, y0 , xmr, y2 + 1, Color_special);
+                       pi.pain.line(xmr, y2 , x3, y2, Color_special);
+                       pi.pain.line(x3 + 1, y2 , x1, y1 + 1, Color_special);
+               } else if (params_.kind == InsetSpaceParams::DOWNBRACEFILL) {
+                       pi.pain.line(x0 + 1, y0 , x2, y2 + 1, Color_special);
+                       pi.pain.line(x2, y2 , xml, y2, Color_special);
+                       pi.pain.line(xml + 1, y2 , xm, y1 + 1, Color_special);
+                       pi.pain.line(xm + 1, y1 + 1 , xmr, y2, Color_special);
+                       pi.pain.line(xmr, y2 , x3, y2, Color_special);
+                       pi.pain.line(x3 + 1, y2 + 1 , x1, y0, Color_special);
+               } else if (params_.kind == InsetSpaceParams::CUSTOM) {
+                       pi.pain.line(x0, y1 + 1 , x2 + 1, y2, Color_special);
+                       pi.pain.line(x2 + 1, y2 + 1 , x0, y0, Color_special);
+                       pi.pain.line(x1 + 1, y1 + 1 , x3, y2, Color_special);
+                       pi.pain.line(x3, y2 + 1 , x1 + 1, y0, Color_special);
+                       pi.pain.line(x2, y2 , x3, y2, Color_special);
+               } else if (params_.kind == InsetSpaceParams::CUSTOM_PROTECTED) {
+                       pi.pain.line(x0, y1 + 1 , x2 + 1, y2, Color_latex);
+                       pi.pain.line(x2 + 1, y2 + 1 , x0, y0, Color_latex);
+                       pi.pain.line(x1 + 1, y1 + 1 , x3, y2, Color_latex);
+                       pi.pain.line(x3, y2 + 1 , x1 + 1, y0, Color_latex);
+                       pi.pain.line(x2, y2 , x3, y2, Color_latex);
+               }
+               return;
+       }
+
        int const w = dim.wid;
-       int const h = theFontMetrics(pi.base.font)
-               .ascent('x');
+       int const h = theFontMetrics(pi.base.font).ascent('x');
        int xp[4], yp[4];
 
        xp[0] = x;
        yp[0] = y - max(h / 4, 1);
-       if (kind_ == NORMAL) {
+       if (params_.kind == InsetSpaceParams::NORMAL ||
+           params_.kind == InsetSpaceParams::PROTECTED) {
                xp[1] = x;     yp[1] = y;
                xp[2] = x + w; yp[2] = y;
        } else {
@@ -99,160 +355,444 @@ void InsetSpace::draw(PainterInfo & pi, int x, int y) const
        xp[3] = x + w;
        yp[3] = y - max(h / 4, 1);
 
-       if (kind_ == PROTECTED || kind_ == ENSPACE || kind_ == NEGTHIN)
-               pi.pain.lines(xp, yp, 4, Color::latex);
+       if (params_.kind == InsetSpaceParams::PROTECTED ||
+           params_.kind == InsetSpaceParams::ENSPACE ||
+           params_.kind == InsetSpaceParams::NEGTHIN ||
+           params_.kind == InsetSpaceParams::NEGMEDIUM ||
+           params_.kind == InsetSpaceParams::NEGTHICK ||
+           params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
+               pi.pain.lines(xp, yp, 4, Color_latex);
        else
-               pi.pain.lines(xp, yp, 4, Color::special);
+               pi.pain.lines(xp, yp, 4, Color_special);
 }
 
 
-void InsetSpace::write(Buffer const &, ostream & os) const
+void InsetSpaceParams::write(ostream & os) const
 {
        string command;
-       switch (kind_) {
-       case NORMAL:
-               command = "\\space{}";
+       switch (kind) {
+       case InsetSpaceParams::NORMAL:
+               os << "\\space{}";
+               break;
+       case InsetSpaceParams::PROTECTED:
+               os <<  "~";
                break;
-       case PROTECTED:
-               command = "~";
+       case InsetSpaceParams::THIN:
+               os <<  "\\thinspace{}";
                break;
-       case THIN:
-               command = "\\thinspace{}";
+       case InsetSpaceParams::MEDIUM:
+               os <<  "\\medspace{}";
                break;
-       case QUAD:
-               command = "\\quad{}";
+       case InsetSpaceParams::THICK:
+               os <<  "\\thickspace{}";
                break;
-       case QQUAD:
-               command = "\\qquad{}";
+       case InsetSpaceParams::QUAD:
+               os <<  "\\quad{}";
                break;
-       case ENSPACE:
-               command = "\\enspace{}";
+       case InsetSpaceParams::QQUAD:
+               os <<  "\\qquad{}";
                break;
-       case ENSKIP:
-               command = "\\enskip{}";
+       case InsetSpaceParams::ENSPACE:
+               os <<  "\\enspace{}";
                break;
-       case NEGTHIN:
-               command = "\\negthinspace{}";
+       case InsetSpaceParams::ENSKIP:
+               os <<  "\\enskip{}";
+               break;
+       case InsetSpaceParams::NEGTHIN:
+               os <<  "\\negthinspace{}";
+               break;
+       case InsetSpaceParams::NEGMEDIUM:
+               os <<  "\\negmedspace{}";
+               break;
+       case InsetSpaceParams::NEGTHICK:
+               os <<  "\\negthickspace{}";
+               break;
+       case InsetSpaceParams::HFILL:
+               os <<  "\\hfill{}";
+               break;
+       case InsetSpaceParams::HFILL_PROTECTED:
+               os <<  "\\hspace*{\\fill}";
+               break;
+       case InsetSpaceParams::DOTFILL:
+               os <<  "\\dotfill{}";
+               break;
+       case InsetSpaceParams::HRULEFILL:
+               os <<  "\\hrulefill{}";
+               break;
+       case InsetSpaceParams::LEFTARROWFILL:
+               os <<  "\\leftarrowfill{}";
+               break;
+       case InsetSpaceParams::RIGHTARROWFILL:
+               os <<  "\\rightarrowfill{}";
+               break;
+       case InsetSpaceParams::UPBRACEFILL:
+               os <<  "\\upbracefill{}";
+               break;
+       case InsetSpaceParams::DOWNBRACEFILL:
+               os <<  "\\downbracefill{}";
+               break;
+       case InsetSpaceParams::CUSTOM:
+               os <<  "\\hspace{}";
+               break;
+       case InsetSpaceParams::CUSTOM_PROTECTED:
+               os <<  "\\hspace*{}";
                break;
        }
-       os << "\\InsetSpace " << command << "\n";
+       
+       if (!length.len().empty())
+               os << "\n\\length " << length.asString();
 }
 
 
-void InsetSpace::read(Buffer const &, Lexer & lex)
+void InsetSpaceParams::read(Lexer & lex)
 {
-       lex.next();
-       string const command = lex.getString();
+       lex.setContext("InsetSpaceParams::read");
+       string command;
+       lex >> command;
 
+       // The tests for math might be disabled after a file format change
        if (command == "\\space{}")
-               kind_ = NORMAL;
+               kind = InsetSpaceParams::NORMAL;
        else if (command == "~")
-               kind_ = PROTECTED;
+               kind = InsetSpaceParams::PROTECTED;
        else if (command == "\\thinspace{}")
-               kind_ = THIN;
+               kind = InsetSpaceParams::THIN;
+       else if (math && command == "\\medspace{}")
+               kind = InsetSpaceParams::MEDIUM;
+       else if (math && command == "\\thickspace{}")
+               kind = InsetSpaceParams::THICK;
        else if (command == "\\quad{}")
-               kind_ = QUAD;
+               kind = InsetSpaceParams::QUAD;
        else if (command == "\\qquad{}")
-               kind_ = QQUAD;
+               kind = InsetSpaceParams::QQUAD;
        else if (command == "\\enspace{}")
-               kind_ = ENSPACE;
+               kind = InsetSpaceParams::ENSPACE;
        else if (command == "\\enskip{}")
-               kind_ = ENSKIP;
+               kind = InsetSpaceParams::ENSKIP;
        else if (command == "\\negthinspace{}")
-               kind_ = NEGTHIN;
+               kind = InsetSpaceParams::NEGTHIN;
+       else if (math && command == "\\negmedspace{}")
+               kind = InsetSpaceParams::NEGMEDIUM;
+       else if (math && command == "\\negthickspace{}")
+               kind = InsetSpaceParams::NEGTHICK;
+       else if (command == "\\hfill{}")
+               kind = InsetSpaceParams::HFILL;
+       else if (command == "\\hspace*{\\fill}")
+               kind = InsetSpaceParams::HFILL_PROTECTED;
+       else if (command == "\\dotfill{}")
+               kind = InsetSpaceParams::DOTFILL;
+       else if (command == "\\hrulefill{}")
+               kind = InsetSpaceParams::HRULEFILL;
+       else if (command == "\\hspace{}")
+               kind = InsetSpaceParams::CUSTOM;
+       else if (command == "\\leftarrowfill{}")
+               kind = InsetSpaceParams::LEFTARROWFILL;
+       else if (command == "\\rightarrowfill{}")
+               kind = InsetSpaceParams::RIGHTARROWFILL;
+       else if (command == "\\upbracefill{}")
+               kind = InsetSpaceParams::UPBRACEFILL;
+       else if (command == "\\downbracefill{}")
+               kind = InsetSpaceParams::DOWNBRACEFILL;
+       else if (command == "\\hspace*{}")
+               kind = InsetSpaceParams::CUSTOM_PROTECTED;
        else
                lex.printError("InsetSpace: Unknown kind: `$$Token'");
+
+       if (lex.checkFor("\\length"))
+               lex >> length;
+}
+
+
+void InsetSpace::write(ostream & os) const
+{
+       os << "space ";
+       params_.write(os);
 }
 
 
-int InsetSpace::latex(Buffer const &, odocstream & os,
-                     OutputParams const & runparams) const
+void InsetSpace::read(Lexer & lex)
 {
-       switch (kind_) {
-       case NORMAL:
+       params_.read(lex);
+       lex >> "\\end_inset";
+}
+
+
+int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
+{
+       switch (params_.kind) {
+       case InsetSpaceParams::NORMAL:
                os << (runparams.free_spacing ? " " : "\\ ");
                break;
-       case PROTECTED:
-               os << (runparams.free_spacing ? ' ' : '~');
+       case InsetSpaceParams::PROTECTED:
+               if (runparams.local_font &&
+                   runparams.local_font->language()->lang() == "polutonikogreek")
+                       // in babel's polutonikogreek, ~ is active
+                       os << (runparams.free_spacing ? " " : "\\nobreakspace{}");
+               else
+                       os << (runparams.free_spacing ? ' ' : '~');
                break;
-       case THIN:
+       case InsetSpaceParams::THIN:
                os << (runparams.free_spacing ? " " : "\\,");
                break;
-       case QUAD:
+       case InsetSpaceParams::MEDIUM:
+               os << (runparams.free_spacing ? " " : "\\:");
+               break;
+       case InsetSpaceParams::THICK:
+               os << (runparams.free_spacing ? " " : "\\;");
+               break;
+       case InsetSpaceParams::QUAD:
                os << (runparams.free_spacing ? " " : "\\quad{}");
                break;
-       case QQUAD:
+       case InsetSpaceParams::QQUAD:
                os << (runparams.free_spacing ? " " : "\\qquad{}");
                break;
-       case ENSPACE:
+       case InsetSpaceParams::ENSPACE:
                os << (runparams.free_spacing ? " " : "\\enspace{}");
                break;
-       case ENSKIP:
+       case InsetSpaceParams::ENSKIP:
                os << (runparams.free_spacing ? " " : "\\enskip{}");
                break;
-       case NEGTHIN:
+       case InsetSpaceParams::NEGTHIN:
                os << (runparams.free_spacing ? " " : "\\negthinspace{}");
                break;
+       case InsetSpaceParams::NEGMEDIUM:
+               os << (runparams.free_spacing ? " " : "\\negmedspace{}");
+               break;
+       case InsetSpaceParams::NEGTHICK:
+               os << (runparams.free_spacing ? " " : "\\negthickspace{}");
+               break;
+       case InsetSpaceParams::HFILL:
+               os << (runparams.free_spacing ? " " : "\\hfill{}");
+               break;
+       case InsetSpaceParams::HFILL_PROTECTED:
+               os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
+               break;
+       case InsetSpaceParams::DOTFILL:
+               os << (runparams.free_spacing ? " " : "\\dotfill{}");
+               break;
+       case InsetSpaceParams::HRULEFILL:
+               os << (runparams.free_spacing ? " " : "\\hrulefill{}");
+               break;
+       case InsetSpaceParams::LEFTARROWFILL:
+               os << (runparams.free_spacing ? " " : "\\leftarrowfill{}");
+               break;
+       case InsetSpaceParams::RIGHTARROWFILL:
+               os << (runparams.free_spacing ? " " : "\\rightarrowfill{}");
+               break;
+       case InsetSpaceParams::UPBRACEFILL:
+               os << (runparams.free_spacing ? " " : "\\upbracefill{}");
+               break;
+       case InsetSpaceParams::DOWNBRACEFILL:
+               os << (runparams.free_spacing ? " " : "\\downbracefill{}");
+               break;
+       case InsetSpaceParams::CUSTOM:
+               if (runparams.free_spacing)
+                       os << " ";
+               else
+                       os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
+               break;
+       case InsetSpaceParams::CUSTOM_PROTECTED:
+               if (runparams.free_spacing)
+                       os << " ";
+               else
+                       os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
+               break;
        }
        return 0;
 }
 
 
-int InsetSpace::plaintext(Buffer const &, odocstream & os,
-                         OutputParams const &) const
+int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
 {
-       os << ' ';
-       return 1;
+       switch (params_.kind) {
+       case InsetSpaceParams::HFILL:
+       case InsetSpaceParams::HFILL_PROTECTED:
+               os << "     ";
+               return 5;
+       case InsetSpaceParams::DOTFILL:
+               os << ".....";
+               return 5;
+       case InsetSpaceParams::HRULEFILL:
+               os << "_____";
+               return 5;
+       case InsetSpaceParams::LEFTARROWFILL:
+               os << "<----";
+               return 5;
+       case InsetSpaceParams::RIGHTARROWFILL:
+               os << "---->";
+               return 5;
+       case InsetSpaceParams::UPBRACEFILL:
+               os << "\\-v-/";
+               return 5;
+       case InsetSpaceParams::DOWNBRACEFILL:
+               os << "/-^-\\";
+               return 5;
+       default:
+               os << ' ';
+               return 1;
+       }
 }
 
 
-int InsetSpace::docbook(Buffer const &, odocstream & os,
-                       OutputParams const &) const
+int InsetSpace::docbook(odocstream & os, OutputParams const &) const
 {
-       switch (kind_) {
-       case NORMAL:
-       case QUAD:
-       case QQUAD:
-       case ENSKIP:
+       switch (params_.kind) {
+       case InsetSpaceParams::NORMAL:
+       case InsetSpaceParams::QUAD:
+       case InsetSpaceParams::QQUAD:
+       case InsetSpaceParams::ENSKIP:
                os << " ";
                break;
-       case PROTECTED:
-       case ENSPACE:
-       case THIN:
-       case NEGTHIN:
+       case InsetSpaceParams::PROTECTED:
+       case InsetSpaceParams::ENSPACE:
+       case InsetSpaceParams::THIN:
+       case InsetSpaceParams::MEDIUM:
+       case InsetSpaceParams::THICK:
+       case InsetSpaceParams::NEGTHIN:
+       case InsetSpaceParams::NEGMEDIUM:
+       case InsetSpaceParams::NEGTHICK:
                os << "&nbsp;";
                break;
+       case InsetSpaceParams::HFILL:
+       case InsetSpaceParams::HFILL_PROTECTED:
+               os << '\n';
+       case InsetSpaceParams::DOTFILL:
+               // FIXME
+               os << '\n';
+       case InsetSpaceParams::HRULEFILL:
+               // FIXME
+               os << '\n';
+       case InsetSpaceParams::LEFTARROWFILL:
+       case InsetSpaceParams::RIGHTARROWFILL:
+       case InsetSpaceParams::UPBRACEFILL:
+       case InsetSpaceParams::DOWNBRACEFILL:
+       case InsetSpaceParams::CUSTOM:
+       case InsetSpaceParams::CUSTOM_PROTECTED:
+               // FIXME
+               os << '\n';
        }
        return 0;
 }
 
 
-int InsetSpace::textString(Buffer const & buf, odocstream & os,
-                      OutputParams const & op) const
+docstring InsetSpace::xhtml(XHTMLStream & xs, OutputParams const &) const
+{
+       string output;
+       switch (params_.kind) {
+       case InsetSpaceParams::NORMAL:
+               output = " ";
+               break;
+       case InsetSpaceParams::ENSKIP:
+       case InsetSpaceParams::ENSPACE:
+               output ="&ensp;";
+               break;
+       case InsetSpaceParams::QQUAD:
+               output ="&emsp;";
+       case InsetSpaceParams::THICK:
+       case InsetSpaceParams::QUAD:
+               output ="&emsp;";
+               break;
+       case InsetSpaceParams::THIN:
+               output ="&thinsp;";
+               break;
+       case InsetSpaceParams::PROTECTED:
+       case InsetSpaceParams::MEDIUM:
+       case InsetSpaceParams::NEGTHIN:
+       case InsetSpaceParams::NEGMEDIUM:
+       case InsetSpaceParams::NEGTHICK:
+               output ="&nbsp;";
+               break;
+       case InsetSpaceParams::HFILL:
+       case InsetSpaceParams::HFILL_PROTECTED:
+       case InsetSpaceParams::DOTFILL:
+       case InsetSpaceParams::HRULEFILL:
+       case InsetSpaceParams::LEFTARROWFILL:
+       case InsetSpaceParams::RIGHTARROWFILL:
+       case InsetSpaceParams::UPBRACEFILL:
+       case InsetSpaceParams::DOWNBRACEFILL:
+               // FIXME XHTML
+               // Can we do anything with those in HTML?
+               break;
+       case InsetSpaceParams::CUSTOM:
+       case InsetSpaceParams::CUSTOM_PROTECTED:
+               // FIXME XHTML
+               // Probably we could do some sort of blank span?
+               break;
+       }
+       // don't escape the entities!
+       xs << XHTMLStream::NextRaw() << from_ascii(output);
+       return docstring();
+}
+
+
+void InsetSpace::validate(LaTeXFeatures & features) const
 {
-       return plaintext(buf, os, op);
+       if (params_.kind == InsetSpaceParams::NEGMEDIUM ||
+           params_.kind == InsetSpaceParams::NEGTHICK) 
+               features.require("amsmath");
 }
 
 
-Inset * InsetSpace::clone() const
+void InsetSpace::tocString(odocstream & os) const
 {
-       return new InsetSpace(kind_);
+       plaintext(os, OutputParams(0));
 }
 
 
-bool InsetSpace::isChar() const
+bool InsetSpace::isStretchableSpace() const
 {
-       return true;
+       return params_.kind == InsetSpaceParams::HFILL
+               || params_.kind == InsetSpaceParams::HFILL_PROTECTED
+               || params_.kind == InsetSpaceParams::DOTFILL
+               || params_.kind == InsetSpaceParams::HRULEFILL
+               || params_.kind == InsetSpaceParams::LEFTARROWFILL
+               || params_.kind == InsetSpaceParams::RIGHTARROWFILL
+               || params_.kind == InsetSpaceParams::UPBRACEFILL
+               || params_.kind == InsetSpaceParams::DOWNBRACEFILL;
 }
 
-bool InsetSpace::isLetter() const
+
+docstring InsetSpace::contextMenu(BufferView const &, int, int) const
 {
-       return false;
+       return from_ascii("context-space");
 }
 
-bool InsetSpace::isSpace() const
+
+void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
+{
+       params = InsetSpaceParams();
+       if (in.empty())
+               return;
+
+       istringstream data(in);
+       Lexer lex;
+       lex.setStream(data);
+       lex.setContext("InsetSpace::string2params");
+       lex.next();
+       string const name = lex.getString();
+       if (name == "mathspace")
+               params.math = true;
+       else {
+               params.math = false;
+               LASSERT(name == "space", /**/);
+       }
+
+       // There are cases, such as when we are called via getStatus() from
+       // Dialog::canApply(), where we are just called with "space" rather
+       // than a full "space \type{}\n\\end_inset".
+       if (lex.isOK())
+               params.read(lex);
+}
+
+
+string InsetSpace::params2string(InsetSpaceParams const & params)
 {
-       return true;
+       ostringstream data;
+       if (params.math)
+               data << "math";
+       data << "space" << ' ';
+       params.write(data);
+       return data.str();
 }