]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetSpace.cpp
InsetInfo: enable inset dissolve
[lyx.git] / src / insets / InsetSpace.cpp
index 7ece382c4338e61e5a0b07609d26b5ba588ddee7..ddb85b71ea6b4ca69d69a50f06383fc7400a4599 100644 (file)
@@ -27,6 +27,7 @@
 #include "MetricsInfo.h"
 #include "OutputParams.h"
 #include "output_xhtml.h"
+#include "texstream.h"
 
 #include "support/debug.h"
 #include "support/docstream.h"
@@ -70,6 +71,9 @@ docstring InsetSpace::toolTip(BufferView const &, int, int) const
        case InsetSpaceParams::PROTECTED:
                message = _("Protected Space");
                break;
+       case InsetSpaceParams::VISIBLE:
+               message = _("Visible Space");
+               break;
        case InsetSpaceParams::THIN:
                message = _("Thin Space");
                break;
@@ -143,10 +147,16 @@ void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
        switch (cmd.action()) {
 
-       case LFUN_INSET_MODIFY:
+       case LFUN_INSET_MODIFY: {
                cur.recordUndo();
-               string2params(to_utf8(cmd.argument()), params_);
+               string arg = to_utf8(cmd.argument());
+               if (arg == "space \\hspace{}")
+                       arg += params_.length.len().empty()
+                               ? " \\length 1" + string(stringFromUnit(Length::defaultUnit()))
+                               : " \\length " + params_.length.asString();
+               string2params(arg, params_);
                break;
+       }
 
        case LFUN_INSET_DIALOG_UPDATE:
                cur.bv().updateDialog("space", params2string(params()));
@@ -169,8 +179,11 @@ bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
                        InsetSpaceParams params;
                        string2params(to_utf8(cmd.argument()), params);
                        status.setOnOff(params_.kind == params.kind);
-               }
-               // fall through
+                       status.setEnabled(true);
+               } else
+                       status.setEnabled(false);
+               return true;
+
        case LFUN_INSET_DIALOG_UPDATE:
                status.setEnabled(true);
                return true;
@@ -187,49 +200,50 @@ int const arrow_size = 8;
 
 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
 {
-       if (isStretchableSpace()) {
-               // The metrics for this kinds are calculated externally in
-               // \c TextMetrics::computeRowMetrics. Those are dummy value:
-               dim = Dimension(10, 10, 10);
+       if (isHfill()) {
+               // The width for hfills is calculated externally in
+               // TextMetrics::computeRowMetrics. The value of 5 is the
+               // minimal value when the hfill is not active.
+               dim = Dimension(5, 10, 10);
                return;
        }
 
        frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
        dim.asc = fm.maxAscent();
        dim.des = fm.maxDescent();
+       int const em = fm.em();
 
        switch (params_.kind) {
                case InsetSpaceParams::THIN:
                case InsetSpaceParams::NEGTHIN:
-                       dim.wid = fm.width(char_type('M')) / 6;
+                       dim.wid = em / 6;
                        break;
                case InsetSpaceParams::MEDIUM:
                case InsetSpaceParams::NEGMEDIUM:
-                       dim.wid = fm.width(char_type('M')) / 4;
+                       dim.wid = em / 4;
                        break;
                case InsetSpaceParams::THICK:
                case InsetSpaceParams::NEGTHICK:
-                       dim.wid = fm.width(char_type('M')) / 2;
+                       dim.wid = em / 2;
                        break;
                case InsetSpaceParams::PROTECTED:
+               case InsetSpaceParams::VISIBLE:
                case InsetSpaceParams::NORMAL:
                        dim.wid = fm.width(char_type(' '));
                        break;
                case InsetSpaceParams::QUAD:
-                       dim.wid = fm.width(char_type('M'));
+                       dim.wid = em;
                        break;
                case InsetSpaceParams::QQUAD:
-                       dim.wid = 2 * fm.width(char_type('M'));
+                       dim.wid = 2 * em;
                        break;
                case InsetSpaceParams::ENSPACE:
                case InsetSpaceParams::ENSKIP:
-                       dim.wid = int(0.5 * fm.width(char_type('M')));
+                       dim.wid = int(0.5 * em);
                        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 w = mi.base.inPixels(params_.length.len());
                        int const minw = (w < 0) ? 3 * arrow_size : 4;
                        dim.wid = max(minw, abs(w));
                        break;
@@ -245,8 +259,6 @@ void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
                        // shut up compiler
                        break;
        }
-       // Cache the inset dimension.
-       setDimCache(mi, dim);
 }
 
 
@@ -254,7 +266,7 @@ void InsetSpace::draw(PainterInfo & pi, int x, int y) const
 {
        Dimension const dim = dimension(*pi.base.bv);
 
-       if (isStretchableSpace() || params_.length.len().value() < 0) {
+       if (isHfill() || 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:
@@ -340,37 +352,40 @@ void InsetSpace::draw(PainterInfo & pi, int x, int y) const
        }
 
        int const w = dim.wid;
-       int const h = theFontMetrics(pi.base.font).ascent('x');
+       int const h = theFontMetrics(pi.base.font).xHeight();
        int xp[4], yp[4];
 
-       xp[0] = x;
+       xp[0] = x + 1;
        yp[0] = y - max(h / 4, 1);
        if (params_.kind == InsetSpaceParams::NORMAL ||
-           params_.kind == InsetSpaceParams::PROTECTED) {
-               xp[1] = x;     yp[1] = y;
-               xp[2] = x + w; yp[2] = y;
+           params_.kind == InsetSpaceParams::PROTECTED ||
+           params_.kind == InsetSpaceParams::VISIBLE) {
+               xp[1] = x + 1;     yp[1] = y;
+               xp[2] = x + w - 2; yp[2] = y;
        } else {
-               xp[1] = x;     yp[1] = y + max(h / 4, 1);
-               xp[2] = x + w; yp[2] = y + max(h / 4, 1);
+               xp[1] = x + 1;     yp[1] = y + max(h / 4, 1);
+               xp[2] = x + w - 2; yp[2] = y + max(h / 4, 1);
        }
-       xp[3] = x + w;
+       xp[3] = x + w - 2;
        yp[3] = y - max(h / 4, 1);
 
+       Color col = Color_special;
        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);
+               col = Color_latex;
+       else if (params_.kind == InsetSpaceParams::VISIBLE)
+               col =  Color_foreground;
+
+       pi.pain.lines(xp, yp, 4, col);
 }
 
 
 void InsetSpaceParams::write(ostream & os) const
 {
-       string command;
        switch (kind) {
        case InsetSpaceParams::NORMAL:
                os << "\\space{}";
@@ -378,6 +393,9 @@ void InsetSpaceParams::write(ostream & os) const
        case InsetSpaceParams::PROTECTED:
                os <<  "~";
                break;
+       case InsetSpaceParams::VISIBLE:
+               os <<  "\\textvisiblespace{}";
+               break;
        case InsetSpaceParams::THIN:
                os <<  "\\thinspace{}";
                break;
@@ -439,7 +457,7 @@ void InsetSpaceParams::write(ostream & os) const
                os <<  "\\hspace*{}";
                break;
        }
-       
+
        if (!length.len().empty())
                os << "\n\\length " << length.asString();
 }
@@ -456,6 +474,8 @@ void InsetSpaceParams::read(Lexer & lex)
                kind = InsetSpaceParams::NORMAL;
        else if (command == "~")
                kind = InsetSpaceParams::PROTECTED;
+       else if (command == "\\textvisiblespace{}")
+               kind = InsetSpaceParams::VISIBLE;
        else if (command == "\\thinspace{}")
                kind = InsetSpaceParams::THIN;
        else if (math && command == "\\medspace{}")
@@ -472,9 +492,9 @@ void InsetSpaceParams::read(Lexer & lex)
                kind = InsetSpaceParams::ENSKIP;
        else if (command == "\\negthinspace{}")
                kind = InsetSpaceParams::NEGTHIN;
-       else if (math && command == "\\negmedspace{}")
+       else if (command == "\\negmedspace{}")
                kind = InsetSpaceParams::NEGMEDIUM;
-       else if (math && command == "\\negthickspace{}")
+       else if (command == "\\negthickspace{}")
                kind = InsetSpaceParams::NEGTHICK;
        else if (command == "\\hfill{}")
                kind = InsetSpaceParams::HFILL;
@@ -518,7 +538,7 @@ void InsetSpace::read(Lexer & lex)
 }
 
 
-int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
+void InsetSpace::latex(otexstream & os, OutputParams const & runparams) const
 {
        switch (params_.kind) {
        case InsetSpaceParams::NORMAL:
@@ -532,6 +552,9 @@ int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
                else
                        os << (runparams.free_spacing ? ' ' : '~');
                break;
+       case InsetSpaceParams::VISIBLE:
+               os << (runparams.free_spacing ? " " : "\\textvisiblespace{}");
+               break;
        case InsetSpaceParams::THIN:
                os << (runparams.free_spacing ? " " : "\\,");
                break;
@@ -599,11 +622,11 @@ int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
                        os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
                break;
        }
-       return 0;
 }
 
 
-int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
+int InsetSpace::plaintext(odocstringstream & os,
+        OutputParams const &, size_t) const
 {
        switch (params_.kind) {
        case InsetSpaceParams::HFILL:
@@ -628,6 +651,45 @@ int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
        case InsetSpaceParams::DOWNBRACEFILL:
                os << "/-^-\\";
                return 5;
+       case InsetSpaceParams::VISIBLE:
+               os.put(0x2423);
+               return 1;
+       case InsetSpaceParams::ENSKIP:
+               os.put(0x2002);
+               return 1;
+       case InsetSpaceParams::ENSPACE:
+               os.put(0x2060); // WORD JOINER, makes the breakable en space unbreakable
+               os.put(0x2002);
+               os.put(0x2060); // WORD JOINER, makes the breakable en space unbreakable
+               return 3;
+       case InsetSpaceParams::QUAD:
+               os.put(0x2003);
+               return 1;
+       case InsetSpaceParams::QQUAD:
+               os.put(0x2003);
+               os.put(0x2003);
+               return 2;
+       case InsetSpaceParams::THIN:
+               os.put(0x202f);
+               return 1;
+       case InsetSpaceParams::MEDIUM:
+               os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable medium space breakable
+               os.put(0x2005);
+               os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable medium space breakable
+               return 1;
+       case InsetSpaceParams::THICK:
+               os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable thick space breakable
+               os.put(0x2004);
+               os.put(0x200b); // ZERO WIDTH SPACE, makes the unbreakable thick space breakable
+               return 1;
+       case InsetSpaceParams::PROTECTED:
+       case InsetSpaceParams::CUSTOM_PROTECTED:
+               os.put(0x00a0);
+               return 1;
+       case InsetSpaceParams::NEGTHIN:
+       case InsetSpaceParams::NEGMEDIUM:
+       case InsetSpaceParams::NEGTHICK:
+               return 0;
        default:
                os << ' ';
                return 1;
@@ -639,30 +701,53 @@ int InsetSpace::docbook(odocstream & os, OutputParams const &) const
 {
        switch (params_.kind) {
        case InsetSpaceParams::NORMAL:
+               os << " ";
+               break;
        case InsetSpaceParams::QUAD:
+               os << "&emsp;";
+               break;
        case InsetSpaceParams::QQUAD:
+               os << "&emsp;&emsp;";
+               break;
        case InsetSpaceParams::ENSKIP:
-               os << " ";
+               os << "&ensp;";
                break;
        case InsetSpaceParams::PROTECTED:
+               os << "&nbsp;";
+               break;
+       case InsetSpaceParams::VISIBLE:
+               os << "&#x2423;";
+               break;
        case InsetSpaceParams::ENSPACE:
+               os << "&#x2060;&ensp;&#x2060;";
+               break;
        case InsetSpaceParams::THIN:
+               os << "&thinsp;";
+               break;
        case InsetSpaceParams::MEDIUM:
+               os << "&emsp14;";
+               break;
        case InsetSpaceParams::THICK:
+               os << "&emsp13;";
+               break;
        case InsetSpaceParams::NEGTHIN:
        case InsetSpaceParams::NEGMEDIUM:
        case InsetSpaceParams::NEGTHICK:
+               // FIXME
                os << "&nbsp;";
                break;
        case InsetSpaceParams::HFILL:
        case InsetSpaceParams::HFILL_PROTECTED:
                os << '\n';
+               break;
        case InsetSpaceParams::DOTFILL:
                // FIXME
                os << '\n';
+               break;
        case InsetSpaceParams::HRULEFILL:
                // FIXME
                os << '\n';
+               break;
        case InsetSpaceParams::LEFTARROWFILL:
        case InsetSpaceParams::RIGHTARROWFILL:
        case InsetSpaceParams::UPBRACEFILL:
@@ -671,6 +756,7 @@ int InsetSpace::docbook(odocstream & os, OutputParams const &) const
        case InsetSpaceParams::CUSTOM_PROTECTED:
                // FIXME
                os << '\n';
+               break;
        }
        return 0;
 }
@@ -684,25 +770,36 @@ docstring InsetSpace::xhtml(XHTMLStream & xs, OutputParams const &) const
                output = " ";
                break;
        case InsetSpaceParams::ENSKIP:
-       case InsetSpaceParams::ENSPACE:
                output ="&ensp;";
                break;
+       case InsetSpaceParams::ENSPACE:
+               output ="&#x2060;&ensp;&#x2060;";
+               break;
        case InsetSpaceParams::QQUAD:
-               output ="&emsp;";
+               output ="&emsp;&emsp;";
+               break;
        case InsetSpaceParams::THICK:
+               output ="&#x2004;";
+               break;
        case InsetSpaceParams::QUAD:
                output ="&emsp;";
                break;
+       case InsetSpaceParams::MEDIUM:
+               output ="&#x2005;";
+               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;
+       // no XHTML entity, only unicode code for space character exists
+       case InsetSpaceParams::VISIBLE:
+               output ="&#x2423;";
+               break;
        case InsetSpaceParams::HFILL:
        case InsetSpaceParams::HFILL_PROTECTED:
        case InsetSpaceParams::DOTFILL:
@@ -715,9 +812,13 @@ docstring InsetSpace::xhtml(XHTMLStream & xs, OutputParams const &) const
                // Can we do anything with those in HTML?
                break;
        case InsetSpaceParams::CUSTOM:
+               // FIXME XHTML
+               // Probably we could do some sort of blank span?
+               break;
        case InsetSpaceParams::CUSTOM_PROTECTED:
                // FIXME XHTML
                // Probably we could do some sort of blank span?
+               output ="&nbsp;";
                break;
        }
        // don't escape the entities!
@@ -729,18 +830,27 @@ docstring InsetSpace::xhtml(XHTMLStream & xs, OutputParams const &) const
 void InsetSpace::validate(LaTeXFeatures & features) const
 {
        if (params_.kind == InsetSpaceParams::NEGMEDIUM ||
-           params_.kind == InsetSpaceParams::NEGTHICK) 
+           params_.kind == InsetSpaceParams::NEGTHICK)
                features.require("amsmath");
 }
 
 
 void InsetSpace::toString(odocstream & os) const
 {
-       plaintext(os, OutputParams(0));
+       odocstringstream ods;
+       plaintext(ods, OutputParams(0));
+       os << ods.str();
+}
+
+
+void InsetSpace::forOutliner(docstring & os, size_t const, bool const) const
+{
+       // There's no need to be cute here.
+       os += " ";
 }
 
 
-bool InsetSpace::isStretchableSpace() const
+bool InsetSpace::isHfill() const
 {
        return params_.kind == InsetSpaceParams::HFILL
                || params_.kind == InsetSpaceParams::HFILL_PROTECTED
@@ -753,9 +863,9 @@ bool InsetSpace::isStretchableSpace() const
 }
 
 
-docstring InsetSpace::contextMenuName() const
+string InsetSpace::contextMenuName() const
 {
-       return from_ascii("context-space");
+       return "context-space";
 }
 
 
@@ -775,7 +885,8 @@ void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
                params.math = true;
        else {
                params.math = false;
-               LASSERT(name == "space", /**/);
+               // we can try to read this even if the name is wrong
+               LATTEST(name == "space");
        }
 
        // There are cases, such as when we are called via getStatus() from