wxCoord wxGridCellAutoWrapStringRenderer::BreakWord(wxDC& dc, const wxString& word, wxCoord maxWidth, wxArrayString& lines, wxString& line) { wxArrayInt widths; dc.GetPartialTextExtents(word, widths); // TODO: Use binary search to find the first element > maxWidth. const unsigned count = widths.size(); unsigned n; for ( n = 0; n < count; n++ ) { if ( widths[n] > maxWidth ) break; } if ( n == 0 ) { // This is a degenerate case: the first character of the word is // already wider than the available space, so we just can't show it // completely and have to put the first character in this line. n = 1; } lines.push_back(word.substr(0, n)); // Check if the remainder of the string fits in one line. // // Unfortunately we can't use the existing partial text extents as the // extent of the remainder may be different when it's rendered in a // separate line instead of as part of the same one, so we have to // recompute it. const wxString rest = word.substr(n); const wxCoord restWidth = dc.GetTextExtent(rest).x; if ( restWidth <= maxWidth ) { line = rest; return restWidth; } // Break the rest of the word into lines. // // TODO: Perhaps avoid recursion? The code is simpler like this but using a // loop in this function would probably be more efficient. return BreakWord(dc, rest, maxWidth, lines, line); }
void SurfaceImpl::MeasureWidths(Font &font, const char *s, int len, XYPOSITION *positions) { wxString str = stc2wx(s, len); wxArrayInt tpos; SetFont(font); hdc->GetPartialTextExtents(str, tpos); #if wxUSE_UNICODE // Map the widths for UCS-2 characters back to the UTF-8 input string // NOTE: I don't think this is right for when sizeof(wxChar) > 2, ie wxGTK2 // so figure it out and fix it! size_t i = 0; size_t ui = 0; while ((int)i < len) { unsigned char uch = (unsigned char)s[i]; positions[i++] = tpos[ui]; if (uch >= 0x80) { if (uch < (0x80 + 0x40 + 0x20)) { positions[i++] = tpos[ui]; } else { positions[i++] = tpos[ui]; positions[i++] = tpos[ui]; } } ui++; } #else // !wxUSE_UNICODE // If not unicode then just use the widths we have for (int i = 0; i < len; i++) { positions[i] = tpos[i]; } #endif // wxUSE_UNICODE/!wxUSE_UNICODE }
// ----------------------------------------------------------------------------- // Draws function description text [desc] at [left,top]. // Returns a rect of the bounds of the drawn text // ----------------------------------------------------------------------------- wxRect SCallTip::drawFunctionDescription(wxDC& dc, const wxString& desc, int left, int top) const { auto italic = font_.Italic(); wxRect rect(left, top, 0, 0); dc.SetFont(italic); int max_right = 0; if (dc.GetTextExtent(desc).x > MAX_WIDTH) { // Description is too long, split into multiple lines vector<wxString> desc_lines; wxString line = desc; wxArrayInt extents; while (true) { dc.GetPartialTextExtents(line, extents); bool split = false; for (unsigned a = 0; a < extents.size(); a++) { if (extents[a] > MAX_WIDTH) { // Try to split in phrases first. size_t eol = (size_t)line.SubString(0, a).Last('.') + 1; eol = line[eol] == ' ' ? eol : -1; if (eol <= 0 || eol > MAX_WIDTH) eol = (size_t)line.SubString(0, a).Last(',') + 1; if (eol <= 0 || eol > MAX_WIDTH) eol = (size_t)line.SubString(0, a).Last(' '); desc_lines.push_back(line.SubString(0, eol)); line = line.SubString(eol + 1, line.Length()); split = true; break; } } if (!split) { desc_lines.push_back(line); break; } } int bottom = rect.GetBottom() + UI::scalePx(font_.GetPixelSize().GetHeight()); for (const auto& desc_line : desc_lines) { drawText(dc, desc_line, 0, bottom, &rect); bottom = rect.GetBottom(); if (rect.GetRight() > max_right) max_right = rect.GetRight(); } } else { drawText(dc, desc, 0, rect.GetBottom() + UI::scalePx(font_.GetPixelSize().GetHeight()), &rect); if (rect.GetRight() > max_right) max_right = rect.GetRight(); } return { left, top, max_right - left, rect.GetBottom() - top }; }
/* 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!) wxASSERT(excessPixels>0); // 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"); return curLine; } 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.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 and protected */ wxString wxControlBase::DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc, wxEllipsizeMode mode, int maxFinalWidthPx, int replacementWidthPx, int marginWidthPx) { wxASSERT_MSG(replacementWidthPx > 0 && marginWidthPx > 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; wxArrayInt charOffsetsPx; size_t len = curLine.length(); if (len == 0 || !dc.GetPartialTextExtents(curLine, charOffsetsPx)) return curLine; wxASSERT(charOffsetsPx.GetCount() == len); // NOTE: charOffsetsPx[n] is the width in pixels of the first n characters (with the last one INCLUDED) // thus charOffsetsPx[len-1] is the total width of the string size_t totalWidthPx = charOffsetsPx.Last(); if ( totalWidthPx <= (size_t)maxFinalWidthPx ) return curLine; // we don't need to do any ellipsization! int excessPx = wxMin(totalWidthPx - maxFinalWidthPx + replacementWidthPx + marginWidthPx, // security margin totalWidthPx); wxASSERT(excessPx>0); // excessPx should be in the [1;totalWidthPx] range // 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) size_t initialCharToRemove, // index of first character to erase, valid range is [0;len-1] nCharsToRemove; // how many chars do we need to erase? valid range is [1;len-initialCharToRemove] // let's compute the range of characters to remove depending on the ellipsization mode: switch (mode) { case wxELLIPSIZE_START: initialCharToRemove = 0; for ( nCharsToRemove = 1; nCharsToRemove < len && charOffsetsPx[nCharsToRemove-1] < excessPx; nCharsToRemove++ ) ; break; case wxELLIPSIZE_MIDDLE: { // NOTE: the following piece of code works also when len == 1 // start the removal process from the middle of the string // i.e. separe the string in three parts: // - the first one to preserve, valid range [0;initialCharToRemove-1] or the empty range if initialCharToRemove==0 // - the second one to remove, valid range [initialCharToRemove;endCharToRemove] // - the third one to preserve, valid range [endCharToRemove+1;len-1] or the empty range if endCharToRemove==len-1 // NOTE: empty range != range [0;0] since the range [0;0] contains 1 character (the zero-th one)! initialCharToRemove = len/2; size_t endCharToRemove = len/2; // index of the last character to remove; valid range is [0;len-1] int removedPx = 0; for ( ; removedPx < excessPx; ) { // try to remove the last character of the first part of the string if (initialCharToRemove > 0) { // width of the (initialCharToRemove-1)-th character int widthPx; if (initialCharToRemove >= 2) widthPx = charOffsetsPx[initialCharToRemove-1] - charOffsetsPx[initialCharToRemove-2]; else widthPx = charOffsetsPx[initialCharToRemove-1]; // the (initialCharToRemove-1)-th character is the first char of the string wxASSERT(widthPx >= 0); // widthPx is zero for e.g. tab characters // mark the (initialCharToRemove-1)-th character as removable initialCharToRemove--; removedPx += widthPx; } // try to remove the first character of the last part of the string if (endCharToRemove < len - 1 && removedPx < excessPx) { // width of the (endCharToRemove+1)-th character int widthPx = charOffsetsPx[endCharToRemove+1] - charOffsetsPx[endCharToRemove]; wxASSERT(widthPx >= 0); // widthPx is zero for e.g. tab characters // mark the (endCharToRemove+1)-th character as removable endCharToRemove++; removedPx += widthPx; } if (initialCharToRemove == 0 && endCharToRemove == len-1) { // we need to remove all the characters of the string! break; } } nCharsToRemove = endCharToRemove - initialCharToRemove + 1; } break; case wxELLIPSIZE_END: { int maxWidthPx = totalWidthPx - excessPx; // go backward from the end of the string toward the start for ( initialCharToRemove = len-1; initialCharToRemove > 0 && charOffsetsPx[initialCharToRemove-1] > maxWidthPx; initialCharToRemove-- ) ; nCharsToRemove = len - initialCharToRemove; } break; case wxELLIPSIZE_NONE: default: wxFAIL_MSG("invalid ellipsize mode"); return curLine; } #ifdef __VMS #pragma message disable unscomzer // suppress warnings on comparison of unsigned numbers #endif wxASSERT(initialCharToRemove >= 0 && initialCharToRemove <= len-1); // see valid range for initialCharToRemove above #ifdef __VMS #pragma message enable unscomzer // suppress warnings on comparison of unsigned numbers #endif wxASSERT(nCharsToRemove >= 1 && nCharsToRemove <= len-initialCharToRemove); // see valid range for nCharsToRemove above // erase nCharsToRemove characters after initialCharToRemove (included); // e.g. if we have the string "foobar" (len = 6) // ^ // \--- initialCharToRemove = 2 // and nCharsToRemove = 2, then we get "foar" wxString ret(curLine); ret.erase(initialCharToRemove, nCharsToRemove); int removedPx; if (initialCharToRemove >= 1) removedPx = charOffsetsPx[initialCharToRemove+nCharsToRemove-1] - charOffsetsPx[initialCharToRemove-1]; else removedPx = charOffsetsPx[initialCharToRemove+nCharsToRemove-1]; wxASSERT(removedPx >= excessPx); // if there is space for the replacement dots, add them if ((int)totalWidthPx-removedPx+replacementWidthPx < maxFinalWidthPx) ret.insert(initialCharToRemove, wxELLIPSE_REPLACEMENT); // if everything was ok, we should have shortened this line // enough to make it fit in maxFinalWidthPx: wxASSERT_LEVEL_2(dc.GetTextExtent(ret).GetWidth() <= maxFinalWidthPx); return ret; }
// Splits m_Word into up to three parts according to selection, returns // substring before, in and after selection and the points (in relative coords) // where s2 and s3 start: void wxHtmlWordCell::Split(const wxDC& dc, const wxPoint& selFrom, const wxPoint& selTo, unsigned& pos1, unsigned& pos2) const { wxPoint pt1 = (selFrom == wxDefaultPosition) ? wxDefaultPosition : selFrom - GetAbsPos(); wxPoint pt2 = (selTo == wxDefaultPosition) ? wxPoint(m_Width, wxDefaultCoord) : selTo - GetAbsPos(); // if the selection is entirely within this cell, make sure pt1 < pt2 in // order to make the rest of this function simpler: if ( selFrom != wxDefaultPosition && selTo != wxDefaultPosition && selFrom.x > selTo.x ) { wxPoint tmp = pt1; pt1 = pt2; pt2 = tmp; } unsigned len = m_Word.length(); unsigned i = 0; pos1 = 0; // adjust for cases when the start/end position is completely // outside the cell: if ( pt1.y < 0 ) pt1.x = 0; if ( pt2.y >= m_Height ) pt2.x = m_Width; // before selection: // (include character under caret only if in first half of width) #ifdef __WXMAC__ // implementation using PartialExtents to support fractional widths wxArrayInt widths ; dc.GetPartialTextExtents(m_Word,widths) ; while( i < len && pt1.x >= widths[i] ) i++ ; if ( i < len ) { int charW = (i > 0) ? widths[i] - widths[i-1] : widths[i]; if ( widths[i] - pt1.x < charW/2 ) i++; } #else // !__WXMAC__ wxCoord charW, charH; while ( pt1.x > 0 && i < len ) { dc.GetTextExtent(m_Word[i], &charW, &charH); pt1.x -= charW; if ( pt1.x >= -charW/2 ) { pos1 += charW; i++; } } #endif // __WXMAC__/!__WXMAC__ // in selection: // (include character under caret only if in first half of width) unsigned j = i; #ifdef __WXMAC__ while( j < len && pt2.x >= widths[j] ) j++ ; if ( j < len ) { int charW = (j > 0) ? widths[j] - widths[j-1] : widths[j]; if ( widths[j] - pt2.x < charW/2 ) j++; } #else // !__WXMAC__ pos2 = pos1; pt2.x -= pos2; while ( pt2.x > 0 && j < len ) { dc.GetTextExtent(m_Word[j], &charW, &charH); pt2.x -= charW; if ( pt2.x >= -charW/2 ) { pos2 += charW; j++; } } #endif // __WXMAC__/!__WXMAC__ pos1 = i; pos2 = j; wxASSERT( pos2 >= pos1 ); }
void SurfaceImpl::MeasureWidths(Font &font, const char *s, int len, int *positions) { wxString str = sci2wx(s, len); SetFont(font); #if !wxCHECK_VERSION(2, 5, 0) #ifndef __WXMAC__ // Calculate the position of each character based on the widths of // the previous characters int* tpos = new int[len+1]; int totalWidth = 0; size_t i; for (i=0; i<str.Length(); i++) { int w, h; hdc->GetTextExtent(str[i], &w, &h); totalWidth += w; tpos[i] = totalWidth; } #else // Instead of a running total, remeasure from the begining of the // text for each character's position. This is because with AA fonts // on OS X widths can be fractions of pixels wide when more than one // are drawn together, so the sum of all character widths is not necessarily // (and probably not) the same as the whole string width. int* tpos = new int[len+1]; size_t i; for (i=0; i<str.Length(); i++) { int w, h; hdc->GetTextExtent(str.Left(i+1), &w, &h); tpos[i] = w; } #endif #else wxArrayInt tpos; hdc->GetPartialTextExtents(str, tpos); #endif #if wxUSE_UNICODE // Map the widths for UCS-2 characters back to the UTF-8 input string // NOTE: I don't think this is right for when sizeof(wxChar) > 2, ie wxGTK2 // so figure it out and fix it! int j = 0; size_t ui = 0; while ((int)j < len) { unsigned char uch = (unsigned char)s[j]; positions[j++] = tpos[ui]; if (uch >= 0x80) { if (uch < (0x80 + 0x40 + 0x20)) { positions[j++] = tpos[ui]; } else { positions[j++] = tpos[ui]; positions[j++] = tpos[ui]; } } ui++; } #else // If not unicode then just use the widths we have #if !wxCHECK_VERSION(2, 5, 0) memcpy(positions, tpos, len * sizeof(*tpos)); #else #if wxUSE_STL std::copy(tpos.begin(), tpos.end(), positions); #else memcpy(positions, tpos.begin(), len * sizeof(int)); #endif #endif #endif #if !wxCHECK_VERSION(2, 5, 0) delete [] tpos; #endif }