commit bb344452c8eafaba4f0e7a51f8e4d99176570d9b
Author: Guillaume Munch <[email protected]>
Date:   Sun Oct 4 19:38:47 2015 +0100

    Consistency of ellipses across the UI
    
    Use the function support:truncateWithEllipsis() to shorten a docstring with
    ... at the end. Actually we use U+2026 HORIZONTAL ELLIPSIS instead of "..." 
when
    automatically shortening strings. This is to be consistent with Qt's own
    truncation and is much nicer on the screen.
    
    This includes the bugs #9575 and #9572 regarding broken text elision in the
    outliner.
    
    Known issues (non-regressions):
    
    * TocBackend::updateItem() should be rewritten to update all TOCs. (#8386)
    
    * "..." should be replaced with … everywhere else on the interface 
(including
      translation strings).
    
    * We should prefer to rely on QFontMetrics::elidedText() to truncate strings
      with an ellipsis whenever possible, or an equivalent for the buffer view
      dependent on the font metrics. See the warning in src/support/lstrings.h.

diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp
index d306c87..f2bf332 100644
--- a/src/BiblioInfo.cpp
+++ b/src/BiblioInfo.cpp
@@ -756,8 +756,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, 
Buffer const & buf,
                ret = html::cleanAttr(ret);
 
        // make sure it is not too big
-       if (ret.size() > maxsize)
-               ret = ret.substr(0, maxsize - 3) + from_ascii("...");
+       support::truncateWithEllipsis(ret, maxsize);
        return ret;
 }
 
@@ -928,14 +927,9 @@ docstring const BiblioInfo::getLabel(vector<docstring> 
keys,
                        before, after, dialog, key + 1 != ken);
        }
 
-       if (ret.size() > max_size) {
-               ret.resize(max_size - 3);
-               ret += "...";
-       } else if (too_many_keys) {
-               if (ret.size() > max_size - 3)
-                       ret.resize(max_size - 3);
-               ret += "...";
-       }
+       if (too_many_keys)
+               ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
+       support::truncateWithEllipsis(ret, max_size);
        return ret;
 }
 
diff --git a/src/CutAndPaste.cpp b/src/CutAndPaste.cpp
index 0bfd3ad..a521e14 100644
--- a/src/CutAndPaste.cpp
+++ b/src/CutAndPaste.cpp
@@ -811,22 +811,20 @@ vector<docstring> availableSelections(Buffer const * buf)
                // we do not use cit-> here because gcc 2.9x does not
                // like it (JMarc)
                ParagraphList const & pars = (*cit).first;
-               docstring asciiSel;
+               docstring textSel;
                ParagraphList::const_iterator pit = pars.begin();
                ParagraphList::const_iterator pend = pars.end();
                for (; pit != pend; ++pit) {
-                       Paragraph par(*pit, 0, 26);
+                       Paragraph par(*pit, 0, 46);
                        // adapt paragraph to current buffer.
                        par.setBuffer(const_cast<Buffer &>(*buf));
-                       asciiSel += par.asString(AS_STR_INSETS);
-                       if (asciiSel.size() > 25) {
-                               asciiSel.replace(22, docstring::npos,
-                                                from_ascii("..."));
+                       textSel += par.asString(AS_STR_INSETS);
+                       if (textSel.size() > 45) {
+                               support::truncateWithEllipsis(textSel,45);
                                break;
                        }
                }
-
-               selList.push_back(asciiSel);
+               selList.push_back(textSel);
        }
 
        return selList;
diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp
index 5bbc16a..37067ac 100644
--- a/src/Paragraph.cpp
+++ b/src/Paragraph.cpp
@@ -3257,11 +3257,13 @@ docstring Paragraph::asString(pos_type beg, pos_type 
end, int options, const Out
 }
 
 
-void Paragraph::forOutliner(docstring & os, size_t maxlen) const
+void Paragraph::forOutliner(docstring & os, size_t const maxlen,
+                                                       bool const shorten) 
const
 {
+       size_t tmplen = shorten ? maxlen + 1 : maxlen;
        if (!d->params_.labelString().empty())
                os += d->params_.labelString() + ' ';
-       for (pos_type i = 0; i < size() && os.length() < maxlen; ++i) {
+       for (pos_type i = 0; i < size() && os.length() < tmplen; ++i) {
                if (isDeleted(i))
                        continue;
                char_type const c = d->text_[i];
@@ -3270,8 +3272,10 @@ void Paragraph::forOutliner(docstring & os, size_t 
maxlen) const
                else if (c == '\t' || c == '\n')
                        os += ' ';
                else if (c == META_INSET)
-                       getInset(i)->forOutliner(os, maxlen);
+                       getInset(i)->forOutliner(os, tmplen, false);
        }
+       if (shorten)
+               Text::shortenForOutliner(os, maxlen);
 }
 
 
diff --git a/src/Paragraph.h b/src/Paragraph.h
index 444ce26..9f19f1d 100644
--- a/src/Paragraph.h
+++ b/src/Paragraph.h
@@ -181,7 +181,8 @@ public:
                           int options = AS_STR_NONE,
                           const OutputParams *runparams = 0) const;
        ///
-       void forOutliner(docstring &, size_t maxlen) const;
+       void forOutliner(docstring &, size_t const maxlen,
+                                        bool const shorten = true) const;
 
        ///
        void write(std::ostream &, BufferParams const &,
diff --git a/src/Text.cpp b/src/Text.cpp
index bedf228..67776a0 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -2043,13 +2043,25 @@ docstring Text::asString(pit_type beg, pit_type end, 
int options) const
 }
 
 
-void Text::forOutliner(docstring & os, size_t maxlen, bool shorten) const
+void Text::shortenForOutliner(docstring & str, size_t const maxlen)
 {
-       LASSERT(maxlen >= 8, maxlen = TOC_ENTRY_LENGTH);
-       for (size_t i = 0; i != pars_.size() && os.length() < maxlen; ++i)
-               pars_[i].forOutliner(os, maxlen);
-       if (shorten && os.length() >= maxlen)
-               os = os.substr(0, maxlen - 3) + from_ascii("...");
+       support::truncateWithEllipsis(str, maxlen);
+       docstring::iterator it = str.begin();
+       docstring::iterator end = str.end();
+       for (; it != end; ++it)
+               if ((*it) == L'\n' || (*it) == L'\t')
+                       (*it) = L' ';   
+}
+
+
+void Text::forOutliner(docstring & os, size_t const maxlen,
+                                          bool const shorten) const
+{
+       size_t tmplen = shorten ? maxlen + 1 : maxlen;
+       for (size_t i = 0; i != pars_.size() && os.length() < tmplen; ++i)
+               pars_[i].forOutliner(os, tmplen, false);
+       if (shorten)
+               shortenForOutliner(os, maxlen);
 }
 
 
diff --git a/src/Text.h b/src/Text.h
index d7777e3..2d855ec 100644
--- a/src/Text.h
+++ b/src/Text.h
@@ -123,14 +123,16 @@ public:
        ///
        docstring asString(pit_type beg, pit_type end,
                int options = AS_STR_NONE) const;
-       /// Appends a possibly abbreviated representation of our text
-       /// to \param os, where \param maxlen defines the maximum size
-       /// of \param os. If \param shorten is true, then we will shorten
-       /// \param os to maxlen chars and replace the final three by "...,
-       /// if \param os is longer than maxlen chars.
-       /// if \param maxlen is passed as 0, then it is ignored. (In fact,
-       /// it is reset to the maximum value for size_t.)
-       void forOutliner(docstring & os, size_t maxlen, bool shorten = true) 
const;
+
+       /// truncates str to maxlenwith an ellipsis and replaces the characters 
'\n'
+       /// and '\t' with spaces
+       static void shortenForOutliner(docstring & str, size_t const maxlen);
+               
+       /// Appends a possibly abbreviated representation of our text to \param 
os,
+       /// where \param maxlen defines the maximum size of \param os. If \param
+       /// shorten is true, then os is shortened as above
+       void forOutliner(docstring & os, size_t const maxlen,
+                                        bool const shorten = true) const;
 
        /// insert a character at cursor position
        /// FIXME: replace Cursor with DocIterator.
diff --git a/src/TocBackend.cpp b/src/TocBackend.cpp
index f3511be..145a23b 100644
--- a/src/TocBackend.cpp
+++ b/src/TocBackend.cpp
@@ -31,8 +31,8 @@
 #include "support/convert.h"
 #include "support/debug.h"
 #include "support/docstream.h"
-
 #include "support/lassert.h"
+#include "support/lstrings.h"
 
 using namespace std;
 
@@ -259,6 +259,11 @@ shared_ptr<TocBuilder> TocBackend::builder(string const & 
type)
 }
 
 
+// FIXME: This function duplicates functionality from InsetText::iterateForToc.
+// Both have their own way of computing the TocItem for "tableofcontents". The
+// TocItem creation and update should be made in a dedicated function and
+// updateItem should be rewritten to uniformly update the matching items from
+// all TOCs.
 bool TocBackend::updateItem(DocIterator const & dit)
 {
        if (dit.text()->getTocLevel(dit.pit()) == Layout::NOT_IN_TOC)
@@ -280,28 +285,30 @@ bool TocBackend::updateItem(DocIterator const & dit)
 
        // For each paragraph, traverse its insets and let them add
        // their toc items
+       //
+       // FIXME: This is supposed to accomplish the same as the body of
+       // InsetText::iterateForToc(), probably
        Paragraph & par = toc_item->dit_.paragraph();
        InsetList::const_iterator it = par.insetList().begin();
        InsetList::const_iterator end = par.insetList().end();
        for (; it != end; ++it) {
                Inset & inset = *it->inset;
                if (inset.lyxCode() == ARG_CODE) {
+                       tocstring = par.labelString();
                        if (!tocstring.empty())
-                               break;
-                       Paragraph const & inset_par =
-                               
*static_cast<InsetArgument&>(inset).paragraphs().begin();
-                       if (!par.labelString().empty())
-                               tocstring = par.labelString() + ' ';
-                       tocstring += inset_par.asString(AS_STR_INSETS);
+                               tocstring += ' ';
+                       
inset.asInsetText()->text().forOutliner(tocstring,TOC_ENTRY_LENGTH);
                        break;
                }
        }
 
-       int const toclevel = 
toc_item->dit_.text()->getTocLevel(toc_item->dit_.pit());
+       int const toclevel = toc_item->dit_.text()->
+               getTocLevel(toc_item->dit_.pit());
        if (toclevel != Layout::NOT_IN_TOC && toclevel >= min_toclevel
                && tocstring.empty())
-                       tocstring = par.asString(AS_STR_LABEL | AS_STR_INSETS);
+               par.forOutliner(tocstring, TOC_ENTRY_LENGTH);
 
+       support::truncateWithEllipsis(tocstring, TOC_ENTRY_LENGTH);
        const_cast<TocItem &>(*toc_item).str(tocstring);
 
        buffer_->updateTocItem("tableofcontents", dit);
diff --git a/src/frontends/qt4/GuiCompleter.cpp 
b/src/frontends/qt4/GuiCompleter.cpp
index 7c3c2ff..0f974f1 100644
--- a/src/frontends/qt4/GuiCompleter.cpp
+++ b/src/frontends/qt4/GuiCompleter.cpp
@@ -26,6 +26,7 @@
 #include "version.h"
 
 #include "support/lassert.h"
+#include "support/lstrings.h"
 #include "support/debug.h"
 
 #include <QApplication>
@@ -394,9 +395,9 @@ void GuiCompleter::updateInline(Cursor const & cur, QString 
const & completion)
        docstring postfix = qstring_to_ucs4(completion.mid(prefix.length()));
        
        // shorten it if necessary
-       if (lyxrc.completion_inline_dots != -1
-           && postfix.size() > unsigned(lyxrc.completion_inline_dots))
-               postfix = postfix.substr(0, lyxrc.completion_inline_dots - 1) + 
"...";
+       if (lyxrc.completion_inline_dots != -1)
+               support::truncateWithEllipsis(postfix,
+                                                                         
unsigned(lyxrc.completion_inline_dots));
 
        // set inline completion at cursor position
        size_t uniqueTo = max(longestUniqueCompletion().size(), prefix.size());
diff --git a/src/frontends/qt4/GuiWorkArea.cpp 
b/src/frontends/qt4/GuiWorkArea.cpp
index 2411206..11596d5 100644
--- a/src/frontends/qt4/GuiWorkArea.cpp
+++ b/src/frontends/qt4/GuiWorkArea.cpp
@@ -1852,7 +1852,7 @@ public:
 
                if (!dotted) {
                        if (dottedPrefix_ && !prefix_.isEmpty())
-                               prefix_ += ".../";
+                               prefix_ += ellipsisSlash_;
                        prefix_ += postfix_.front() + "/";
                }
                dottedPrefix_ = dotted && !prefix_.isEmpty();
@@ -1865,7 +1865,7 @@ public:
                        return filename_;
 
                bool dots = dottedPrefix_ || !postfix_.isEmpty();
-               return prefix_ + (dots ? ".../" : "") + filename_;
+               return prefix_ + (dots ? ellipsisSlash_ : "") + filename_;
        }
        ///
        QString forecastPathString() const
@@ -1874,7 +1874,7 @@ public:
                        return displayString();
 
                return prefix_
-                       + (dottedPrefix_ ? ".../" : "")
+                       + (dottedPrefix_ ? ellipsisSlash_ : "")
                        + postfix_.front() + "/";
        }
        ///
@@ -1883,6 +1883,8 @@ public:
        int tab() const { return tab_; }
 
 private:
+       /// ".../"
+       static QString const ellipsisSlash_;
        ///
        QString prefix_;
        ///
@@ -1898,6 +1900,9 @@ private:
 };
 
 
+QString const DisplayPath::ellipsisSlash_ = QString(QChar(0x2026)) + "/";
+
+
 ///
 bool operator<(DisplayPath const & a, DisplayPath const & b)
 {
diff --git a/src/frontends/qt4/Menus.cpp b/src/frontends/qt4/Menus.cpp
index 592a66a..2507ae3 100644
--- a/src/frontends/qt4/Menus.cpp
+++ b/src/frontends/qt4/Menus.cpp
@@ -768,11 +768,9 @@ bool MenuDefinition::searchMenu(FuncRequest const & func, 
docstring_list & names
 QString limitStringLength(docstring const & str)
 {
        size_t const max_item_length = 45;
-
-       if (str.size() > max_item_length)
-               return toqstr(str.substr(0, max_item_length - 3) + "...");
-
-       return toqstr(str);
+       docstring ret = str.substr(0, max_item_length + 1);
+       support::truncateWithEllipsis(ret, max_item_length);
+       return toqstr(ret);
 }
 
 
diff --git a/src/insets/Inset.cpp b/src/insets/Inset.cpp
index cce0f32..3d9b5e6 100644
--- a/src/insets/Inset.cpp
+++ b/src/insets/Inset.cpp
@@ -258,7 +258,7 @@ docstring Inset::toolTip(BufferView const &, int, int) const
 }
 
 
-void Inset::forOutliner(docstring &, size_t) const
+void Inset::forOutliner(docstring &, size_t const, bool const) const
 {
 }
 
diff --git a/src/insets/Inset.h b/src/insets/Inset.h
index b0fab8e..f928830 100644
--- a/src/insets/Inset.h
+++ b/src/insets/Inset.h
@@ -328,7 +328,8 @@ public:
        /// Appends a potentially abbreviated version of the inset to
        /// \param str. Intended for use by the TOC.
        virtual void forOutliner(docstring & str,
-                           size_t maxlen = TOC_ENTRY_LENGTH) const;
+                                                        size_t const maxlen = 
TOC_ENTRY_LENGTH,
+                                                        bool const shorten = 
true) const;
 
        /// can the contents of the inset be edited on screen ?
        // true for InsetCollapsables (not ButtonOnly) (not InsetInfo), 
InsetText
diff --git a/src/insets/InsetBranch.cpp b/src/insets/InsetBranch.cpp
index 0ed3bc4..5a278f9 100644
--- a/src/insets/InsetBranch.cpp
+++ b/src/insets/InsetBranch.cpp
@@ -299,10 +299,11 @@ void InsetBranch::toString(odocstream & os) const
 }
 
 
-void InsetBranch::forOutliner(docstring & os, size_t maxlen) const
+void InsetBranch::forOutliner(docstring & os, size_t const maxlen,
+                                                         bool const shorten) 
const
 {
        if (isBranchSelected())
-               InsetCollapsable::forOutliner(os, maxlen);
+               InsetCollapsable::forOutliner(os, maxlen, shorten);
 }
 
 
diff --git a/src/insets/InsetBranch.h b/src/insets/InsetBranch.h
index f4ba3f5..e005bce 100644
--- a/src/insets/InsetBranch.h
+++ b/src/insets/InsetBranch.h
@@ -76,7 +76,7 @@ private:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        void validate(LaTeXFeatures &) const;
        ///
diff --git a/src/insets/InsetCaptionable.cpp b/src/insets/InsetCaptionable.cpp
index cbdcc0a..0434c2e 100644
--- a/src/insets/InsetCaptionable.cpp
+++ b/src/insets/InsetCaptionable.cpp
@@ -40,8 +40,7 @@ docstring InsetCaptionable::floatName(string const & type) 
const
        BufferParams const & bp = buffer().params();
        FloatList const & floats = bp.documentClass().floats();
        FloatList::const_iterator it = floats[type];
-       // FIXME UNICODE
-       return (it == floats.end()) ? from_ascii(type) : 
bp.B_(it->second.name());
+       return (it == floats.end()) ? from_utf8(type) : 
bp.B_(it->second.name());
 }
 
 
diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp
index b455c86..4dcec94 100644
--- a/src/insets/InsetCitation.cpp
+++ b/src/insets/InsetCitation.cpp
@@ -313,21 +313,13 @@ void InsetCitation::updateBuffer(ParIterator const &, 
UpdateType)
 {
        if (!cache.recalculate && buffer().citeLabelsValid())
                return;
-
        // The label may have changed, so we have to re-create it.
        docstring const glabel = generateLabel();
-
-       unsigned int const maxLabelChars = 45;
-
-       docstring label = glabel;
-       if (label.size() > maxLabelChars) {
-               label.erase(maxLabelChars - 3);
-               label += "...";
-       }
-
        cache.recalculate = false;
        cache.generated_label = glabel;
-       cache.screen_label = label;
+       unsigned int const maxLabelChars = 45;
+       cache.screen_label = glabel.substr(0, maxLabelChars);
+       support::truncateWithEllipsis(cache.screen_label, maxLabelChars);
 }
 
 
@@ -406,7 +398,7 @@ void InsetCitation::toString(odocstream & os) const
 }
 
 
-void InsetCitation::forOutliner(docstring & os, size_t) const
+void InsetCitation::forOutliner(docstring & os, size_t const, bool const) const
 {
        os += screenLabel();
 }
diff --git a/src/insets/InsetCitation.h b/src/insets/InsetCitation.h
index 4b4b5ed..417b46f 100644
--- a/src/insets/InsetCitation.h
+++ b/src/insets/InsetCitation.h
@@ -59,7 +59,7 @@ public:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        void validate(LaTeXFeatures &) const {}
        ///
diff --git a/src/insets/InsetHyperlink.cpp b/src/insets/InsetHyperlink.cpp
index cbcf613..54f1f2c 100644
--- a/src/insets/InsetHyperlink.cpp
+++ b/src/insets/InsetHyperlink.cpp
@@ -56,7 +56,7 @@ ParamInfo const & InsetHyperlink::findInfo(string const & /* 
cmdName */)
 
 docstring InsetHyperlink::screenLabel() const
 {
-       docstring const temp = from_ascii("Hyperlink: ");
+       docstring const temp = _("Hyperlink: ");
 
        docstring url;
 
@@ -66,8 +66,9 @@ docstring InsetHyperlink::screenLabel() const
 
        // elide if long
        if (url.length() > 30) {
-               url = url.substr(0, 10) + "..."
-                       + url.substr(url.length() - 17, url.length());
+               docstring end = url.substr(url.length() - 17, url.length());
+               support::truncateWithEllipsis(url, 13);
+               url += end;
        }
        return temp + url;
 }
@@ -257,7 +258,7 @@ void InsetHyperlink::toString(odocstream & os) const
 }
 
 
-void InsetHyperlink::forOutliner(docstring & os, size_t) const
+void InsetHyperlink::forOutliner(docstring & os, size_t const, bool const) 
const
 {
        docstring const & n = getParam("name");
        if (!n.empty()) {
diff --git a/src/insets/InsetHyperlink.h b/src/insets/InsetHyperlink.h
index 6bc0c64..1dd3e08 100644
--- a/src/insets/InsetHyperlink.h
+++ b/src/insets/InsetHyperlink.h
@@ -38,7 +38,7 @@ public:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        docstring toolTip(BufferView const & bv, int x, int y) const;
        ///
diff --git a/src/insets/InsetIPAMacro.cpp b/src/insets/InsetIPAMacro.cpp
index 7d5290d..fd96a19 100644
--- a/src/insets/InsetIPAMacro.cpp
+++ b/src/insets/InsetIPAMacro.cpp
@@ -596,7 +596,7 @@ void InsetIPAChar::toString(odocstream & os) const
 }
 
 
-void InsetIPAChar::forOutliner(docstring & os, size_t) const
+void InsetIPAChar::forOutliner(docstring & os, size_t const, bool const) const
 {
        odocstringstream ods;
        plaintext(ods, OutputParams(0));
diff --git a/src/insets/InsetIPAMacro.h b/src/insets/InsetIPAMacro.h
index 9f2184b..c29fc49 100644
--- a/src/insets/InsetIPAMacro.h
+++ b/src/insets/InsetIPAMacro.h
@@ -156,7 +156,7 @@ public:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        InsetCode lyxCode() const { return IPACHAR_CODE; }
        /// We don't need \begin_inset and \end_inset
diff --git a/src/insets/InsetNomencl.cpp b/src/insets/InsetNomencl.cpp
index 2279766..7ebfe4a 100644
--- a/src/insets/InsetNomencl.cpp
+++ b/src/insets/InsetNomencl.cpp
@@ -75,12 +75,8 @@ ParamInfo const & InsetNomencl::findInfo(string const & /* 
cmdName */)
 docstring InsetNomencl::screenLabel() const
 {
        size_t const maxLabelChars = 25;
-
        docstring label = _("Nom: ") + getParam("symbol");
-       if (label.size() > maxLabelChars) {
-               label.erase(maxLabelChars - 3);
-               label += "...";
-       }
+       support::truncateWithEllipsis(label, maxLabelChars);
        return label;
 }
 
diff --git a/src/insets/InsetQuotes.cpp b/src/insets/InsetQuotes.cpp
index 01776a3..039465d 100644
--- a/src/insets/InsetQuotes.cpp
+++ b/src/insets/InsetQuotes.cpp
@@ -331,7 +331,7 @@ void InsetQuotes::toString(odocstream & os) const
 }
 
 
-void InsetQuotes::forOutliner(docstring & os, size_t) const
+void InsetQuotes::forOutliner(docstring & os, size_t const, bool const) const
 {
        os += displayString();
 }
diff --git a/src/insets/InsetQuotes.h b/src/insets/InsetQuotes.h
index 0e4f46c..fec7fbc 100644
--- a/src/insets/InsetQuotes.h
+++ b/src/insets/InsetQuotes.h
@@ -88,7 +88,7 @@ public:
        /// 
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t maxlen) const;
+       void forOutliner(docstring &, size_t const maxlen, bool const) const;
 
        ///
        void validate(LaTeXFeatures &) const;
diff --git a/src/insets/InsetRef.cpp b/src/insets/InsetRef.cpp
index 4489270..db8ca6e 100644
--- a/src/insets/InsetRef.cpp
+++ b/src/insets/InsetRef.cpp
@@ -260,7 +260,7 @@ void InsetRef::toString(odocstream & os) const
 }
 
 
-void InsetRef::forOutliner(docstring & os, size_t) const
+void InsetRef::forOutliner(docstring & os, size_t const, bool const) const
 {
        // There's no need for details in the TOC, and a long label
        // will just get in the way.
@@ -288,18 +288,13 @@ void InsetRef::updateBuffer(ParIterator const & it, 
UpdateType)
                label += getParam("name");
        }
        
-       screen_label_ = label;
-       bool shortened = false;
        unsigned int const maxLabelChars = 24;
        if (screen_label_.size() > maxLabelChars) {
-               screen_label_.erase(maxLabelChars - 3);
-               screen_label_ += "...";
-               shortened = true;
-       }
-       if (shortened)
                tooltip_ = label;
-       else 
+               support::truncateWithEllipsis(label, maxLabelChars);
+       } else
                tooltip_ = from_ascii("");
+       screen_label_ = label;
 }
 
 
diff --git a/src/insets/InsetRef.h b/src/insets/InsetRef.h
index 5836d2e..f885423 100644
--- a/src/insets/InsetRef.h
+++ b/src/insets/InsetRef.h
@@ -64,7 +64,7 @@ public:
        /// 
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        void validate(LaTeXFeatures & features) const;
        ///
diff --git a/src/insets/InsetScript.cpp b/src/insets/InsetScript.cpp
index a854447..3d912d3 100644
--- a/src/insets/InsetScript.cpp
+++ b/src/insets/InsetScript.cpp
@@ -317,8 +317,7 @@ docstring InsetScript::toolTip(BufferView const &, int, 
int) const
        InsetText::plaintext(ods, rp, 200);
        docstring content_tip = ods.str();
        // shorten it if necessary
-       if (content_tip.size() >= 200)
-               content_tip = content_tip.substr(0, 197) + "...";
+       support::truncateWithEllipsis(content_tip, 200);
        docstring res = scripttranslator_loc().find(params_.type);
        if (!content_tip.empty())
                res += from_ascii(": ") + content_tip;
diff --git a/src/insets/InsetSpace.cpp b/src/insets/InsetSpace.cpp
index 839ccc9..3a5161a 100644
--- a/src/insets/InsetSpace.cpp
+++ b/src/insets/InsetSpace.cpp
@@ -845,7 +845,7 @@ void InsetSpace::toString(odocstream & os) const
 }
 
 
-void InsetSpace::forOutliner(docstring & os, size_t) const
+void InsetSpace::forOutliner(docstring & os, size_t const, bool const) const
 {
        // There's no need to be cute here.
        os += " ";
diff --git a/src/insets/InsetSpace.h b/src/insets/InsetSpace.h
index b5a628a..03ca7ae 100644
--- a/src/insets/InsetSpace.h
+++ b/src/insets/InsetSpace.h
@@ -135,7 +135,7 @@ public:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        bool hasSettings() const { return true; }
        ///
diff --git a/src/insets/InsetSpecialChar.cpp b/src/insets/InsetSpecialChar.cpp
index 99ac836..8fa6ead 100644
--- a/src/insets/InsetSpecialChar.cpp
+++ b/src/insets/InsetSpecialChar.cpp
@@ -542,7 +542,8 @@ void InsetSpecialChar::toString(odocstream & os) const
 }
 
 
-void InsetSpecialChar::forOutliner(docstring & os, size_t) const
+void InsetSpecialChar::forOutliner(docstring & os, size_t const,
+                                                                  bool const) 
const
 {
        odocstringstream ods;
        plaintext(ods, OutputParams(0));
diff --git a/src/insets/InsetSpecialChar.h b/src/insets/InsetSpecialChar.h
index 70e2bf3..a576186 100644
--- a/src/insets/InsetSpecialChar.h
+++ b/src/insets/InsetSpecialChar.h
@@ -78,7 +78,7 @@ public:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        InsetCode lyxCode() const { return SPECIALCHAR_CODE; }
        /// We don't need \begin_inset and \end_inset
diff --git a/src/insets/InsetText.cpp b/src/insets/InsetText.cpp
index 204df96..80b0bac 100644
--- a/src/insets/InsetText.cpp
+++ b/src/insets/InsetText.cpp
@@ -789,11 +789,12 @@ void InsetText::toString(odocstream & os) const
 }
 
 
-void InsetText::forOutliner(docstring & os, size_t maxlen) const
+void InsetText::forOutliner(docstring & os, size_t const maxlen,
+                                                       bool const shorten) 
const
 {
        if (!getLayout().isInToc())
                return;
-       text().forOutliner(os, maxlen, false);
+       text().forOutliner(os, maxlen, shorten);
 }
 
 
diff --git a/src/insets/InsetText.h b/src/insets/InsetText.h
index 77dcfa9..c0640a1 100644
--- a/src/insets/InsetText.h
+++ b/src/insets/InsetText.h
@@ -169,7 +169,7 @@ public:
        ///
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
        ///
        void addToToc(DocIterator const & di, bool output_active,
                                  UpdateType utype) const;
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index 92372f0..560c999 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -2381,11 +2381,13 @@ void InsetMathHull::toString(odocstream & os) const
 }
 
 
-void InsetMathHull::forOutliner(docstring & os, size_t) const
+void InsetMathHull::forOutliner(docstring & os, size_t const, bool const) const
 {
        odocstringstream ods;
        OutputParams op(0);
        op.for_toc = true;
+       // FIXME: this results in spilling TeX into the LyXHTML output since the
+       // outliner is used to generate the LyXHTML list of figures/etc.
        plaintext(ods, op);
        os += ods.str();
 }
diff --git a/src/mathed/InsetMathHull.h b/src/mathed/InsetMathHull.h
index b4a5be0..9598ad4 100644
--- a/src/mathed/InsetMathHull.h
+++ b/src/mathed/InsetMathHull.h
@@ -152,7 +152,7 @@ public:
        /// 
        void toString(odocstream &) const;
        ///
-       void forOutliner(docstring &, size_t) const;
+       void forOutliner(docstring &, size_t const, bool const) const;
 
        /// get notification when the cursor leaves this inset
        bool notifyCursorLeaves(Cursor const & old, Cursor & cur);
diff --git a/src/support/filetools.cpp b/src/support/filetools.cpp
index db07d56..6acfdf0 100644
--- a/src/support/filetools.cpp
+++ b/src/support/filetools.cpp
@@ -943,12 +943,10 @@ docstring const makeDisplayPath(string const & path, 
unsigned int threshold)
                // Yes, filename itself is too long.
                // Pick the start and the end of the filename.
                dstr = from_utf8(onlyFileName(path));
-               docstring const head = dstr.substr(0, threshold / 2 - 3);
-
-               docstring::size_type len = dstr.length();
-               docstring const tail =
-                       dstr.substr(len - threshold / 2 - 2, len - 1);
-               dstr = head + from_ascii("...") + tail;
+               docstring::size_type const len = dstr.length();
+               if (len >= threshold)
+                       dstr = support::truncateWithEllipsis(dstr, threshold / 
2) +
+                               dstr.substr(len - threshold / 2 - 2, len - 1);
        }
 
        return from_utf8(os::external_path(prefix + to_utf8(dstr)));
diff --git a/src/support/lstrings.cpp b/src/support/lstrings.cpp
index 5e68b75..d06458b 100644
--- a/src/support/lstrings.cpp
+++ b/src/support/lstrings.cpp
@@ -1201,6 +1201,17 @@ docstring const escape(docstring const & lab)
 }
 
 
+bool truncateWithEllipsis(docstring & str, size_t const len)
+{
+       if (str.size() <= len)
+               return false;
+       str.resize(len);
+       if (len > 0)
+               str[len - 1] = 0x2026;// HORIZONTAL ELLIPSIS
+       return true;
+}
+
+
 namespace {
 
 // this doesn't check whether str is empty, so do that first.
@@ -1224,7 +1235,7 @@ vector<docstring> wrapToVec(docstring const & str, int 
ind,
                size_t const i = s.find_last_of(' ', width - 1);
                if (i == docstring::npos || i <= size_t(ind)) {
                        // no space found
-                       s = s.substr(0, width - 3) + "...";
+                       truncateWithEllipsis(s, width);
                        break;
                }
                retval.push_back(s.substr(0, i));
@@ -1253,7 +1264,6 @@ docstring wrap(docstring const & str, int const ind, 
size_t const width)
 docstring wrapParas(docstring const & str, int const indent,
                    size_t const width, size_t const maxlines)
 {
-       docstring const dots = from_ascii("...");
        if (str.empty())
                return docstring();
 
@@ -1272,15 +1282,15 @@ docstring wrapParas(docstring const & str, int const 
indent,
                        tmp.resize(maxlines - curlines);
                        docstring last = tmp.back();
                        size_t const lsize = last.size();
-                       if (lsize > width - 3) {
-                               size_t const i = last.find_last_of(' ', width - 
3);
+                       if (lsize > width - 1) {
+                               size_t const i = last.find_last_of(' ', width - 
1);
                                if (i == docstring::npos || i <= size_t(indent))
                                        // no space found
-                                       last = last.substr(0, lsize - 3) + dots;
+                                       truncateWithEllipsis(last, lsize);
                                else
-                                       last = last.substr(0, i) + dots;
+                                       truncateWithEllipsis(last, i);
                        } else
-                               last += dots;
+                               last.push_back(0x2026);//HORIZONTAL ELLIPSIS
                        tmp.pop_back();
                        tmp.push_back(last);
                }
diff --git a/src/support/lstrings.h b/src/support/lstrings.h
index 395e18e..269b5a6 100644
--- a/src/support/lstrings.h
+++ b/src/support/lstrings.h
@@ -266,6 +266,29 @@ docstring const rsplit(docstring const & a, char_type 
delim);
 /// problems in latex labels.
 docstring const escape(docstring const & lab);
 
+/// Truncates a string with an ellipsis at the end.  Leaves str unchanged and
+/// returns false if it is shorter than len. Otherwise resizes str to len, with
+/// U+2026 HORIZONTAL ELLIPSIS at the end, and returns true.
+///
+/// Warning (Unicode): The cases where we want to truncate the text and it does
+/// not end up converted into a QString for UI display must be really
+/// rare. Whenever possible, we should prefer calling 
QFontMetrics::elidedText()
+/// instead, which takes into account the actual length on the screen and the
+/// layout direction (RTL or LTR). Or a similar function taking into account 
the
+/// font metrics from the buffer view, which still has to be defined. Or set up
+/// the widgets such that Qt elides the string automatically with the exact
+/// needed width. Recall that not only graphemes vary greatly in width, but 
also
+/// can be made of several code points. See:
+/// <http://utf8everywhere.org/#myth.strlen>
+///
+/// What is acceptable is when we know that the string is probably going to be
+/// elided by Qt anyway, and len is chosen such that our own ellipsis will only
+/// be displayed in worst-case scenarios.
+///
+/// FIXME: apply those principles in the current code.
+/// 
+bool truncateWithEllipsis(docstring & str, size_t const len);
+
 /// Word-wraps the provided docstring, returning a line-broken string
 /// of width no wider than width, with the string broken at spaces. 
 /// If the string cannot be broken appropriately, it returns something 
@@ -274,6 +297,10 @@ docstring const escape(docstring const & lab);
 /// If indent is positive, then the first line is indented that many 
 /// spaces. If it is negative, then successive lines are indented, as
 /// if the first line were "outdented".
+///
+/// Warning (Unicode): uses truncateWithEllipsis() internally. Therefore it is
+/// subject to the same warning and FIXME as above.
+///
 docstring wrap(docstring const & str, int const indent = 0,
                size_t const width = 80);
 
@@ -281,6 +308,10 @@ docstring wrap(docstring const & str, int const indent = 0,
 /// that may contain embedded newlines.
 /// \param numlines Don't return more than numlines lines. If numlines
 ///    is 0, we return everything.
+///
+/// Warning (Unicode): uses truncateWithEllipsis() internally. Therefore it is
+/// subject to the same warning and FIXME as above.
+///
 docstring wrapParas(docstring const & str, int const indent = 0,
                     size_t const width = 80, size_t const maxlines = 10);
 

Reply via email to