+ wxASSERT(m_initialCharToRemove <= m_str.length() - 1); // see valid range for initialCharToRemove above
+ wxASSERT(m_nCharsToRemove >= 1 && m_nCharsToRemove <= m_str.length() - m_initialCharToRemove); // see valid range for nCharsToRemove above
+
+ // erase m_nCharsToRemove characters after m_initialCharToRemove (included);
+ // e.g. if we have the string "foobar" (len = 6)
+ // ^
+ // \--- m_initialCharToRemove = 2
+ // and m_nCharsToRemove = 2, then we get "foar"
+ m_output = m_str;
+ m_output.replace(m_initialCharToRemove, m_nCharsToRemove, wxELLIPSE_REPLACEMENT);
+ }
+
+ return m_output;
+ }
+
+ bool IsShortEnough()
+ {
+ if ( m_nCharsToRemove == m_str.length() )
+ return true; // that's the best we could do
+
+ // Width calculation using partial extents is just an inaccurate
+ // estimate: partial extents have sub-pixel precision and are rounded
+ // by GetPartialTextExtents(); replacing part of the string with "..."
+ // may change them too thanks to changes in ligatures, kerning etc.
+ //
+ // The correct algorithm would be to call GetTextExtent() in every step
+ // of ellipsization, but that would be too expensive, especially when
+ // the difference is just a few pixels. So we use partial extents to
+ // estimate string width and only verify it with GetTextExtent() when
+ // it looks good.
+
+ int estimatedWidth = m_replacementWidthPx; // length of "..."
+
+ // length of text before the removed part:
+ if ( m_initialCharToRemove > 0 )
+ estimatedWidth += m_charOffsetsPx[m_initialCharToRemove - 1];
+
+ // length of text after the removed part:
+
+ if ( GetLastRemoved() < m_str.length() )
+ estimatedWidth += m_charOffsetsPx.Last() - m_charOffsetsPx[GetLastRemoved()];
+
+ if ( estimatedWidth > m_maxFinalWidthPx )
+ return false;
+
+ return m_dc.GetTextExtent(GetEllipsizedText()).GetWidth() <= m_maxFinalWidthPx;
+ }
+
+ // calculation state:
+
+ // REMEMBER: indexes inside the string have a valid range of [0;len-1] if not otherwise constrained
+ // lengths/counts of characters (e.g. nCharsToRemove) have a
+ // valid range of [0;len] if not otherwise constrained
+ // NOTE: since this point we know we have for sure a non-empty string from which we need
+ // to remove _at least_ one character (thus nCharsToRemove below is constrained to be >= 1)
+
+ // index of first character to erase, valid range is [0;len-1]:
+ size_t m_initialCharToRemove;
+ // how many chars do we need to erase? valid range is [0;len-m_initialCharToRemove]
+ size_t m_nCharsToRemove;
+
+ wxString m_output;
+ bool m_outputNeedsUpdate;
+
+ // inputs:
+ wxString m_str;
+ const wxDC& m_dc;
+ int m_maxFinalWidthPx;
+ int m_replacementWidthPx;
+ wxArrayInt m_charOffsetsPx;
+
+ bool m_isOk;
+};
+
+} // anonymous namespace
+
+/* static and protected */
+wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
+ wxEllipsizeMode mode, int maxFinalWidthPx,
+ int replacementWidthPx)
+{
+ wxASSERT_MSG(replacementWidthPx > 0, "Invalid parameters");
+ wxASSERT_LEVEL_2_MSG(!curLine.Contains('\n'),
+ "Use Ellipsize() instead!");
+
+ wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" );
+
+ // NOTE: this function assumes that any mnemonic/tab character has already
+ // been handled if it was necessary to handle them (see Ellipsize())
+
+ if (maxFinalWidthPx <= 0)
+ return wxEmptyString;
+
+ size_t len = curLine.length();
+ if (len <= 1 )
+ return curLine;
+
+ EllipsizeCalculator calc(curLine, dc, maxFinalWidthPx, replacementWidthPx);
+
+ if ( !calc.IsOk() )
+ return curLine;
+
+ if ( calc.EllipsizationNotNeeded() )
+ return curLine;
+
+ // let's compute the range of characters to remove depending on the ellipsization mode:
+ switch (mode)
+ {
+ case wxELLIPSIZE_START:
+ {
+ calc.Init(0, 1);
+ while ( !calc.IsShortEnough() )
+ calc.RemoveFromEnd();
+
+ // always show at least one character of the string:
+ if ( calc.m_nCharsToRemove == len )
+ return wxString(wxELLIPSE_REPLACEMENT) + curLine[len-1];
+
+ break;
+ }
+
+ case wxELLIPSIZE_MIDDLE: