diff --git a/include/wx/control.h b/include/wx/control.h index eaa6221123..5427a2d4aa 100644 --- a/include/wx/control.h +++ b/include/wx/control.h @@ -24,6 +24,19 @@ extern WXDLLIMPEXP_DATA_CORE(const char) wxControlNameStr[]; + +// ---------------------------------------------------------------------------- +// Ellipsize() constants +// ---------------------------------------------------------------------------- + +enum wxEllipsizeFlags +{ + wxELLIPSIZE_PROCESS_MNEMONICS = 1, + wxELLIPSIZE_EXPAND_TAB = 2, + + wxELLIPSIZE_DEFAULT_FLAGS = wxELLIPSIZE_PROCESS_MNEMONICS|wxELLIPSIZE_EXPAND_TAB +}; + enum wxEllipsizeMode { wxELLIPSIZE_START, @@ -77,27 +90,6 @@ public: SetLabel(EscapeMnemonics(text)); } - // static utilities: - - // replaces parts of the string with ellipsis if needed - static wxString Ellipsize(const wxString& label, const wxDC& dc, - wxEllipsizeMode mode, int maxWidth); - - // get the string without mnemonic characters ('&') - static wxString GetLabelText(const wxString& label); - - // removes the mnemonics characters - static wxString RemoveMnemonics(const wxString& str); - - // escapes (by doubling them) the mnemonics - static wxString EscapeMnemonics(const wxString& str); - - // return the accel index in the string or -1 if none and puts the modified - // string into second parameter if non NULL - static int FindAccelIndex(const wxString& label, - wxString *labelOnly = NULL); - - // controls by default inherit the colours of their parents, if a // particular control class doesn't want to do it, it can override // ShouldInheritColours() to return false @@ -115,6 +107,30 @@ public: // wxControl-specific processing after processing the update event virtual void DoUpdateWindowUI(wxUpdateUIEvent& event); + + + // static utilities + // ---------------- + + // replaces parts of the (multiline) string with ellipsis if needed + static wxString Ellipsize(const wxString& label, const wxDC& dc, + wxEllipsizeMode mode, int maxWidth, + int flags = wxELLIPSIZE_DEFAULT_FLAGS); + + // get the string without mnemonic characters ('&') + static wxString GetLabelText(const wxString& label); + + // removes the mnemonics characters + static wxString RemoveMnemonics(const wxString& str); + + // escapes (by doubling them) the mnemonics + static wxString EscapeMnemonics(const wxString& str); + + // return the accel index in the string or -1 if none and puts the modified + // string into second parameter if non NULL + static int FindAccelIndex(const wxString& label, + wxString *labelOnly = NULL); + protected: // choose the default border for this window virtual wxBorder GetDefaultBorder() const; @@ -132,6 +148,11 @@ protected: // initialize the common fields of wxCommandEvent void InitCommandEvent(wxCommandEvent& event) const; + // Ellipsize() helper: + static wxString DoEllipsizeSingleLine(const wxString& label, const wxDC& dc, + wxEllipsizeMode mode, int maxWidth, + int replacementWidth, int marginWidth); + // this field contains the label in wx format, i.e. with '&' mnemonics wxString m_labelOrig; diff --git a/interface/wx/control.h b/interface/wx/control.h index 68f220590f..cba493cbb9 100644 --- a/interface/wx/control.h +++ b/interface/wx/control.h @@ -6,14 +6,42 @@ // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// +/** + Flags used by wxControl::Ellipsize function. +*/ +enum wxEllipsizeFlags +{ + /// With this flag when calculating the size of the passed string, mnemonics + /// characters (see wxControl::SetLabel) will be automatically reduced to a + /// single character. + /// This leads to correct calculations only if the string passed to Ellipsize() + /// will be used with wxControl::SetLabel. If you don't want ampersand to + /// be interpreted as mnemonics (e.g. because you use wxControl::SetLabelText) + /// then don't use this flag. + wxELLIPSIZE_PROCESS_MNEMONICS = 1, + + /// This flag tells wxControl::Ellipsize to calculate the width of tab + /// characters @c '\\t' as 6 spaces. + wxELLIPSIZE_EXPAND_TAB = 2, + + /// The default flags for wxControl::Ellipsize. + wxELLIPSIZE_DEFAULT_FLAGS = wxELLIPSIZE_PROCESS_MNEMONICS|wxELLIPSIZE_EXPAND_TAB +}; + + /** The different ellipsization modes supported by the wxControl::Ellipsize function. */ enum wxEllipsizeMode { + /// Put the ellipsis at the start of the string, if the string needs ellipsization. wxELLIPSIZE_START, + + /// Put the ellipsis in the middle of the string, if the string needs ellipsization. wxELLIPSIZE_MIDDLE, + + /// Put the ellipsis at the end of the string, if the string needs ellipsization. wxELLIPSIZE_END }; @@ -53,9 +81,12 @@ public: The ellipsization modes. See ::wxEllipsizeMode. @param maxWidth The maximum width of the returned string in pixels. + @param flags + One or more of the ::wxEllipsize */ static wxString Ellipsize(const wxString& label, const wxDC& dc, - wxEllipsizeMode mode, int maxWidth); + wxEllipsizeMode mode, int maxWidth, + int flags = wxELLIPSIZE_DEFAULT_FLAGS); /** Returns the control's text. diff --git a/src/common/ctrlcmn.cpp b/src/common/ctrlcmn.cpp index 8cb01a3adb..3b32359f66 100644 --- a/src/common/ctrlcmn.cpp +++ b/src/common/ctrlcmn.cpp @@ -212,137 +212,178 @@ int wxControlBase::FindAccelIndex(const wxString& label, wxString *labelOnly) return indexAccel; } +wxBorder wxControlBase::GetDefaultBorder() const +{ + return wxBORDER_THEME; +} + +// ---------------------------------------------------------------------------- +// wxControlBase - ellipsization code +// ---------------------------------------------------------------------------- + #define wxELLIPSE_REPLACEMENT wxT("...") +/* static and protected */ +wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc, + wxEllipsizeMode mode, int maxFinalWidth, + int replacementWidth, int marginWidth) +{ + wxASSERT_MSG(replacementWidth > 0 && marginWidth > 0, + "Invalid parameters"); + wxASSERT_MSG(!curLine.Contains('\n'), + "Use Ellipsize() instead!"); + + // NOTE: this function assumes that any mnemonic/tab character has already + // been handled if it was necessary to handle them (see Ellipsize()) + + if (maxFinalWidth <= 0) + return wxEmptyString; + + wxArrayInt charOffsets; + size_t len = curLine.length(); + if (len == 0 || + !dc.GetPartialTextExtents(curLine, charOffsets)) + return curLine; + + wxASSERT(charOffsets.GetCount() == len); + + size_t totalWidth = charOffsets.Last(); + if ( totalWidth <= (size_t)maxFinalWidth ) + return curLine; // we don't need to do any ellipsization! + + int excessPixels = totalWidth - maxFinalWidth + + replacementWidth + + marginWidth; // security margin (NEEDED!) + + // remove characters in excess + size_t initialChar, // index of first char to erase + nChars; // how many chars do we need to erase? + + switch (mode) + { + case wxELLIPSIZE_START: + initialChar = 0; + for (nChars=0; + nChars < len && charOffsets[nChars] < excessPixels; + nChars++) + ; + break; + + case wxELLIPSIZE_MIDDLE: + { + // the start & end of the removed span of chars + initialChar = len/2; + size_t endChar = len/2; + + int removed = 0; + for ( ; removed < excessPixels; ) + { + if (initialChar > 0) + { + // width of the initialChar-th character + int width = charOffsets[initialChar] - + charOffsets[initialChar-1]; + + // remove the initialChar-th character + removed += width; + initialChar--; + } + + if (endChar < len - 1 && + removed < excessPixels) + { + // width of the (endChar+1)-th character + int width = charOffsets[endChar+1] - + charOffsets[endChar]; + + // remove the endChar-th character + removed += width; + endChar++; + } + + if (initialChar == 0 && endChar == len-1) + { + nChars = len+1; + break; + } + } + + initialChar++; + nChars = endChar - initialChar + 1; + } + break; + + case wxELLIPSIZE_END: + { + wxASSERT(len > 0); + + int maxWidth = totalWidth - excessPixels; + for (initialChar=0; + initialChar < len && + charOffsets[initialChar] < maxWidth; + initialChar++) + ; + + if (initialChar == 0) + { + nChars = len; + } + else + { + //initialChar--; // go back one character + nChars = len - initialChar; + } + } + break; + + default: + wxFAIL_MSG("invalid ellipsize mode"); + } + + wxString ret(curLine); + if (nChars >= len) + { + // need to remove the entire row! + ret.clear(); + } + else + { + // erase nChars characters after initialChar (included): + ret.erase(initialChar, nChars+1); + + // if there is space for the replacement dots, add them + if (maxFinalWidth > replacementWidth) + ret.append(wxELLIPSE_REPLACEMENT);//.insert(initialChar, wxELLIPSE_REPLACEMENT); + } + + // if everything was ok, we should have shortened this line + // enough to make it fit in maxFinalWidth: + wxASSERT(dc.GetTextExtent(ret).GetWidth() < maxFinalWidth); + + return ret; +} + /* static */ wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc, - wxEllipsizeMode mode, int maxFinalWidth) + wxEllipsizeMode mode, int maxFinalWidth, + int flags) { - wxArrayInt charOffsets; wxString ret; - // these cannot be cached as they can change because of e.g. a font change + // these cannot be cached between different Ellipsize() calls as they can + // change because of e.g. a font change; however we calculate them only once + // when ellipsizing multiline labels: int replacementWidth = dc.GetTextExtent(wxELLIPSE_REPLACEMENT).GetWidth(); - int marginWidth = dc.GetCharWidth()*2; + int marginWidth = dc.GetCharWidth(); // NB: we must handle correctly labels with newlines: wxString curLine; - wxSize reqsize; - size_t len; for ( wxString::const_iterator pc = label.begin(); ; ++pc ) { - if ( pc == label.end() || *pc == _T('\n') ) + if ( pc == label.end() || *pc == wxS('\n') ) { - len = curLine.length(); - if (len > 0 && - dc.GetPartialTextExtents(curLine, charOffsets)) - { - wxASSERT(charOffsets.GetCount() == len); - - size_t totalWidth = charOffsets.Last(); - if ( totalWidth > (size_t)maxFinalWidth ) - { - // we need to ellipsize this row - int excessPixels = totalWidth - maxFinalWidth + - replacementWidth + - marginWidth; // security margin (NEEDED!) - - // remove characters in excess - size_t initialChar, // index of first char to erase - nChars; // how many chars do we need to erase? - if (mode == wxELLIPSIZE_START) - { - initialChar = 0; - for (nChars=0; - nChars < len && charOffsets[nChars] < excessPixels; - nChars++) - ; - } - else if (mode == wxELLIPSIZE_MIDDLE) - { - // the start & end of the removed span of chars - initialChar = len/2; - size_t endChar = len/2; - - int removed = 0; - for ( ; removed < excessPixels; ) - { - if (initialChar > 0) - { - // width of the initialChar-th character - int width = charOffsets[initialChar] - - charOffsets[initialChar-1]; - - // remove the initialChar-th character - removed += width; - initialChar--; - } - - if (endChar < len - 1 && - removed < excessPixels) - { - // width of the (endChar+1)-th character - int width = charOffsets[endChar+1] - - charOffsets[endChar]; - - // remove the endChar-th character - removed += width; - endChar++; - } - - if (initialChar == 0 && endChar == len-1) - { - nChars = len+1; - break; - } - } - - initialChar++; - nChars = endChar - initialChar + 1; - } - else - { - wxASSERT(mode == wxELLIPSIZE_END); - wxASSERT(len > 0); - - int maxWidth = totalWidth - excessPixels; - for (initialChar=0; - initialChar < len && - charOffsets[initialChar] < maxWidth; - initialChar++) - ; - - if (initialChar == 0) - { - nChars = len; - } - else - { - initialChar--; // go back one character - nChars = len - initialChar; - } - } - - if (nChars > len) - { - // need to remove the entire row! - curLine.clear(); - } - else - { - // erase nChars characters after initialChar (included): - curLine.erase(initialChar, nChars+1); - - // if there is space for the replacement dots, add them - if (maxFinalWidth > replacementWidth) - curLine.insert(initialChar, wxELLIPSE_REPLACEMENT); - } - - // if everything was ok, we should have shortened this line - // enough to make it fit in maxFinalWidth: - wxASSERT(dc.GetTextExtent(curLine).GetWidth() < maxFinalWidth); - } - } + curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth, + replacementWidth, marginWidth); // add this (ellipsized) row to the rest of the label ret << curLine; @@ -358,19 +399,19 @@ wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc, } } // we need to remove mnemonics from the label for correct calculations - else if ( *pc == _T('&') ) + else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_PROCESS_MNEMONICS) != 0 ) { // pc+1 is safe: at worst we'll be at end() wxString::const_iterator next = pc + 1; - if ( next != label.end() && *next == _T('&') ) - curLine += _T('&'); // && becomes & + if ( next != label.end() && *next == wxS('&') ) + curLine += wxS('&'); // && becomes & //else: remove this ampersand } // we need also to expand tabs to properly calc their size - else if ( *pc == _T('\t') ) + else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_EXPAND_TAB) != 0 ) { // Windows natively expands the TABs to 6 spaces. Do the same: - curLine += wxT(" "); + curLine += wxS(" "); } else { @@ -384,10 +425,6 @@ wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc, //return ret; } -wxBorder wxControlBase::GetDefaultBorder() const -{ - return wxBORDER_THEME; -} // ----------------------------------------------------------------------------