void Styler_Syntax::Insert(unsigned int pos, unsigned int length) { #ifdef __WXDEBUG__ Verify(); const unsigned int docLen = m_doc.GetLength(); wxASSERT(pos >= 0 && pos < docLen); wxASSERT(length >= 0 && pos+length <= docLen); #endif // Adjust end if (m_syntax_end > pos) m_syntax_end += length; //else return; // Change outside search area unsigned int change_start; cxLOCKDOC_READ(m_doc) change_start = doc.GetLineStart(pos); cxENDLOCK const unsigned int adj_end = AdjustForInsertion(pos, length, m_topMatches, 0, change_start); unsigned int change_end = wxMax(adj_end, pos + length); if (!m_lines.IsLineEnd(change_end) || change_end == change_start) { change_end = m_lines.GetLineEndFromPos(change_end); } // After changes we want the lines to be updated with the new height m_updateLineHeight = true; // In case the change invalidates the whole rest of the doc // we don't want to parse it all. There can also come other // changes before next redraw, so we set the limit to change_end. DoSearch(change_start, change_end, change_end); m_updateLineHeight = false; }
void Styler_Syntax::ReInitSpan(span_matcher& sm, unsigned int start, const SearchInfo& si, int rc, int* ovector) { if (!sm.HasEndCaptures()) return; match_matcher& spanstarter = *sm.GetStartMatcher(); unsigned int lineStart; unsigned int lineEnd; unsigned int lineLen; vector<char> line; // TODO: make member variable const char* ptrLine = NULL; bool usingSi; // Check if we can reuse current line info if (start >= si.lineStart && start < si.lineEnd) { usingSi = true; lineStart = si.lineStart; lineEnd = si.lineEnd; lineLen = si.lineLen; ptrLine = &*si.line.begin(); } else { usingSi = false; lineStart = start; // TODO: set to start-of-line cxLOCKDOC_READ(m_doc) lineEnd = doc.GetLine(lineStart, line); cxENDLOCK lineLen = lineEnd - lineStart; ptrLine = &*line.begin(); } if (!ovector) { // Re-search for the starter to get captures // (only needed if we are not in a current search) pcre* subRe = spanstarter.GetMatchPattern(); const int OVECCOUNT = 30; int ov[OVECCOUNT]; ovector = ov; const int search_options = PCRE_NO_UTF8_CHECK; rc = pcre_exec( subRe, // the compiled pattern NULL, // extra data - if we study the pattern ptrLine, // the subject string lineLen, // the length of the subject start - lineStart, // start at offset in the subject search_options, // options ovector, // output vector for substring information OVECCOUNT); // number of elements in the output vector } // ReInit span_matcher to get an updated ender if (rc > 0) { const vector<char>& lineref = (usingSi ? si.line : line); sm.ReInit(lineref, ovector, rc); } }
bool Styler_Syntax::OnIdle() { if (!HaveActiveSyntax()) return false; // Extend syntax a bit longer if (m_syntax_end < m_doc.GetLength()) { // Make sure the extended position is valid and extends to end-of-line unsigned int ext = wxMin(m_syntax_end+EXTSIZE, m_doc.GetLength()); cxLOCKDOC_READ(m_doc) ext = doc.GetLineEnd(ext); cxENDLOCK DoSearch(m_syntax_end, ext, ext); } return m_syntax_end != m_doc.GetLength(); // true if we want more idle events }
void Styler_Syntax::Delete(unsigned int start_pos, unsigned int end_pos) { const unsigned int docLen = m_doc.GetLength(); wxASSERT(start_pos >= 0 && start_pos <= docLen); if (start_pos == end_pos) return; wxASSERT(end_pos > start_pos); // Check if we have deleted the entire document if (docLen == 0) { Invalidate(); return; } // Adjust end unsigned int length = end_pos - start_pos; if (m_syntax_end > start_pos) { if (m_syntax_end > end_pos) m_syntax_end -= length; else m_syntax_end = start_pos; } else return; // Change after search area, no need to re-search unsigned int change_start; cxLOCKDOC_READ(m_doc) change_start = doc.GetLineStart(start_pos); cxENDLOCK unsigned int change_end = AdjustForDeletion(start_pos, end_pos, m_topMatches, 0, change_start); if (!m_lines.IsLineEnd(change_end) || change_end == change_start) { change_end = m_lines.GetLineEndFromPos(change_end); } // Do a new search starting from end of first match before pos if (change_start != m_syntax_end) { wxASSERT(change_start < m_syntax_end); // After changes we want the lines to be updated with the new height m_updateLineHeight = true; // In case the change invalidates the whole rest of the doc // we don't want to parse it all. There can also come other // changes before next redraw, so we set the limit to change_end. DoSearch(change_start, change_end, change_end); m_updateLineHeight = false; } }
unsigned int FixedLine::GetQuickLineWidth(unsigned int startpos, unsigned int endpos) { // This function gives a quick approximation of the total width of the line (without breaking). // It ignores elements like tabs, bold chars and asian extra-wide chars. if (m_isFontFixedWidth) { const unsigned int len = endpos - startpos; return len * charwidth; } // Proportional width font wxString text; cxLOCKDOC_READ(m_doc) text = doc.GetTextPart(startpos, endpos); cxENDLOCK const wxSize extent = dc.GetTextExtent(text); return extent.x; }
void Styler_Syntax::DoSearch(unsigned int start, unsigned int end, unsigned int limit) { wxASSERT(start >= 0 && start < m_doc.GetLength()); wxASSERT(end > start && end <= m_doc.GetLength()); wxASSERT(limit <= m_doc.GetLength()); wxASSERT(m_topMatches.subMatcher); // Don't try to parse if there is no valid parser if (!m_topMatches.subMatcher) { m_syntax_end = m_lines.GetLength(); return; } wxLogDebug(wxT("DoSearch %u-%u %u"), start, end, limit); // Make sure we don't get gaps in the parsing if (start > m_syntax_end) start = m_syntax_end; // Initialize SearchInfo SearchInfo si; si.pos = start; si.line_id = m_lines.GetLineFromCharPos(start); si.lineStart = m_lines.GetLineStartpos(si.line_id); si.lineEnd = m_lines.GetLineEndpos(si.line_id, false); cxLOCKDOC_READ(m_doc) doc.GetTextPart(si.lineStart, si.lineEnd, si.line); cxENDLOCK si.lineLen = si.lineEnd - si.lineStart; si.changeEnd = end; si.limit = limit; si.hitLimit = false; si.done = false; //wxLogDebug(wxT(" si %u-%u-%u,%u"), si.pos,si.line_id, si.lineStart, si.lineEnd); // Do the search m_syntax_end = Search(m_topMatches, si, 0, m_syntax_end, NULL); #ifdef __WXDEBUG__ Verify(); #endif //__WXDEBUG__ }
void Styler_Syntax::Style(StyleRun& sr) { if (!HaveActiveSyntax()) return; unsigned int sr_end = sr.GetRunEnd(); // Check if we need to do a new search if (sr_end > m_syntax_end) { // Make sure the extended position is valid and extends // from start-of-line to end-of-line unsigned int sr_start; cxLOCKDOC_READ(m_doc) sr_start = doc.GetLineStart(m_syntax_end); cxENDLOCK // Extend stylerun to get better search results (round up to whole EXTSIZEs) const unsigned int ext = ((sr_end / EXTSIZE) + 1) * EXTSIZE; sr_end = ext < m_lines.GetLength() ? ext : m_lines.GetLength(); sr_end = m_lines.GetLineEndFromPos(sr_end); DoSearch(sr_start, sr_end, sr_end); } // Apply base style if (m_topStyle) { const unsigned int start = sr.GetRunStart(); const unsigned int end = sr.GetRunEnd(); if (m_topStyle->foregroundcolor != wxNullColour) sr.SetForegroundColor(start, end, m_topStyle->foregroundcolor); if (m_topStyle->backgroundcolor != wxNullColour) { sr.SetBackgroundColor(start, end, m_topStyle->backgroundcolor); sr.SetExtendBgColor(m_topStyle->backgroundcolor); } if (m_topStyle->fontflags != wxFONTFLAG_DEFAULT) sr.SetFontStyle(start, end, m_topStyle->fontflags); } // Style the run DoStyle(sr, 0, m_topMatches.matches); }
unsigned int FixedLine::GetQuickLineHeight(unsigned int startpos, unsigned int endpos) { wxASSERT(m_wrapMode != cxWRAP_NONE); // This function gives a quick (and very rough) approximation of the total height of the line. // It ignores elements like tabs, bold chars and asian extra-wide chars. if (m_isFontFixedWidth) { const unsigned int len = endpos - startpos; const unsigned int linewidth = len * charwidth; unsigned int breaklines = linewidth / width; if (linewidth % width) ++breaklines; return breaklines * charheight; } // Proportional width font wxString text; cxLOCKDOC_READ(m_doc) text = doc.GetTextPart(startpos, endpos); cxENDLOCK wxArrayInt widths; dc.GetPartialTextExtents(text, widths); if (widths.Last() <= width) return charheight; unsigned int breaklines = 1; unsigned int margin = width; for (size_t i = 0; i < widths.Count(); ++i) { if (widths[i] > (int)margin) { ++breaklines; margin += width; } } return breaklines * charheight; }
unsigned int Styler_Syntax::Search(submatch& submatches, SearchInfo& si, unsigned int scopeStart, unsigned int scopeEnd, stxmatch* scope) { const unsigned int adjPos = si.pos - scopeStart; //const unsigned int adjEnd = si.changeEnd - scopeStart; // Find the first match after or containing start stxmatch m(wxEmptyString, NULL, 0, adjPos, 0, 0, NULL); auto_vector<stxmatch>& matches = submatches.matches; auto_vector<stxmatch>::iterator next_match = lower_bound(matches.begin(), matches.end(), &m, stxmatch_end_less()); // Check if we are the end scope // (if we contain changeEnd and has no submatches containing it) bool isEndScope = false; if (scopeStart < si.changeEnd && scopeEnd > si.changeEnd) { m.end = si.changeEnd - scopeStart; auto_vector<stxmatch>::iterator end_match = lower_bound(next_match, matches.end(), &m, stxmatch_end_less()); // We are only the endscope if changeEnd is free and there is no open span // bordering up to it. if (end_match == matches.end() || (*end_match)->start >= m.end || ((*end_match)->end == m.end && (!(*end_match)->subMatch.get() || (*end_match)->subMatch->flags & cxSPAN_IS_CLOSED))) { isEndScope = true; } } // Check if we should enter and search inside match if (next_match != matches.end()) { // In spans with contentName, we have to enter the content if (scope && scope->subMatch->flags & cxSPAN_HAS_CONTENT) { if (scope->subMatch->flags & cxSPAN_HAS_STARTER && next_match == matches.begin() && (*next_match)->end == adjPos) { ++next_match; wxASSERT(next_match != matches.end()); // content span has been deleted } // Verify that we are in starter, middle or ender wxASSERT(next_match == matches.begin() || (*next_match)->subMatch->flags & cxSPAN_IS_CONTENT || matches.size() == 3); // in ender } stxmatch* m = *next_match; const bool isSpan = m->subMatch.get() && m->subMatch->subMatcher; // Pos will always point to start-of-line or end of a match, so if pos // is contained in a match it has to be a span which we have to enter. if (isSpan) { const bool isContentSpan = (m->subMatch->flags & cxSPAN_IS_CONTENT) != 0; if (m->start < adjPos || (isContentSpan && m->start == adjPos)) { const bool spanClosed = m->subMatch->flags & cxSPAN_IS_CLOSED; if (spanClosed && m->end == adjPos) { // Don't enter closed matches if we are at end-of-match ++next_match; } else { const unsigned int subScopeStart = scopeStart + m->start; // Re-init span (content spans have been reinited by parent) if (!(m->subMatch->flags & cxSPAN_IS_CONTENT)) { ReInitSpan(*((span_matcher*)m->subMatch->subMatcher), subScopeStart, si); } // Search inside span m->end = Search(*m->subMatch, si, subScopeStart, scopeStart + m->end, m) - scopeStart; // Delete any following matches we might have overwritten auto_vector<stxmatch>::iterator p = next_match+1; while ( p != matches.end() && m->end > (*p)->start) { p = matches.erase(p); // remove overlapped match } next_match = p; } } else if (m->end == adjPos) ++next_match; } else if (m->end == adjPos) ++next_match; } // Search related vars static const int OVECCOUNT = 30; int ovector[OVECCOUNT]; int zeromatch = -1; // avoid looping on zero-length matches // Get matcher matcher& subMatcher = *submatches.subMatcher; if (!subMatcher.IsInitialized()) subMatcher.Init(); // Search from start until we hit a previous match (or end) for(;;) { // Check if we can end search // 1. At end of last changed line and still in same scope. // 2. Hit limit if (si.done) return wxMax(scopeEnd, si.pos); // The syntax highlighting may have changed the height of // the line, so if we are done with line, update it. /*if (m_updateLineHeight && si.pos == si.lineEnd) { if (m_syntax_end < si.lineEnd) m_syntax_end = si.lineEnd; // avoid search loop m_lines.UpdateParsedLine(si.line_id); }*/ // Check if we can end this search if (!si.hitLimit && isEndScope && (si.pos == si.changeEnd)) { si.done = true; return wxMax(scopeEnd, si.pos); } else if (si.pos >= si.limit) { // If we hit limit before closing a span we have to keep it open // and remove all following matches si.hitLimit = true; submatches.flags &= ~cxSPAN_IS_CLOSED; if (next_match != matches.end()) matches.erase(next_match, matches.end()); return si.limit; } else if (si.pos == si.lineEnd) { // Advance to next line ++si.line_id; si.lineStart = si.lineEnd; si.lineEnd = m_lines.GetLineEndpos(si.line_id, false); cxLOCKDOC_READ(m_doc) doc.GetTextPart(si.lineStart, si.lineEnd, si.line); cxENDLOCK si.lineLen = si.lineEnd - si.lineStart; zeromatch = -1; } // Do the search const unsigned int offset = si.pos - si.lineStart; unsigned int callout_id; const int rc = subMatcher.Match(&*si.line.begin(), offset, si.lineLen, callout_id, ovector, OVECCOUNT, zeromatch); zeromatch = -1; if (rc < 0) { // Remove any old matches between pos and end-of-line while (next_match != matches.end() && scopeStart + (*next_match)->start < si.lineEnd) next_match = matches.erase(next_match); if (rc == PCRE_ERROR_NULL) { // Invalid pattern si.done = true; return si.limit; } // Go to end-of-line si.pos = si.lineEnd; continue; } else { // Get match interval const unsigned int callout_start = si.lineStart + ovector[0]; const unsigned int callout_end = si.lineStart + ovector[1]; // Move pos to end of match si.pos = callout_end; // Only span start & end is allowed to be an empty match // otherwise we have to move one char ahead to avoid eternal loop /*if (callout_start == callout_end && !(subMatcher.IsSpanStart(callout_id) || subMatcher.IsSpanEnd(callout_id))) { wxASSERT(false); ++si.pos; continue; }*/ const bool isSpanStart = subMatcher.IsSpanStart(callout_id); matcher& m = subMatcher.GetCallout(callout_id); const unsigned int matchStart = callout_start - scopeStart; const unsigned int matchEnd = si.pos - scopeStart; // Check if we have start overlapping with following match if (next_match != matches.end() && callout_start == scopeStart + (*next_match)->start) { // If we are both spans and the starters seem equivalent // we just enter and continue stxmatch& sm = *(*next_match); if (isSpanStart && sm.m_matcher == &m) { auto_vector<stxmatch>& submatches = sm.subMatch->matches; if (!submatches.empty()) { const stxmatch* starter = submatches[0]; if (starter->start == 0 && starter->end == matchEnd-matchStart) { ReInitSpan(*((span_matcher*)sm.subMatch->subMatcher), callout_start, si); sm.end = Search(*sm.subMatch, si, callout_start, scopeStart + sm.end, *next_match) - scopeStart; ++next_match; // Check if we have overwritten following matches while (next_match != matches.end() && si.pos > scopeStart + (*next_match)->start) next_match = matches.erase(next_match); continue; } } } } const bool isSpanEnd = subMatcher.IsSpanEnd(callout_id); if (callout_start != callout_end || isSpanStart) { if (isSpanEnd && submatches.flags & cxSPAN_IS_CONTENT) { // If we are in a content span, the ender belongs to the parent // So we ignore it and let the parent find it. si.pos = callout_start; } else { // Create the new match auto_ptr<stxmatch> iv(new stxmatch(m.GetName(), &m, matchStart, matchEnd, NULL, NULL, scope)); // Style It iv->st = GetStyle(*iv); // Check if the match is span or has any captures if (isSpanStart) { CreateSpan(callout_start, callout_end, subMatcher, callout_id, si, iv.get(), rc, ovector); iv->end = si.pos - scopeStart; // Avoid eternal loop with zero-length spans if (iv->start == iv->end) zeromatch = callout_id; } else if (m.HasCaptures()) { AddCaptures(m, *iv, scopeStart, si, rc, ovector); } // Add new match to list next_match = matches.insert(next_match, iv); ++next_match; if (isSpanEnd) submatches.flags |= cxSPAN_HAS_ENDER; } } // Check if we can close the span if (isSpanEnd) { // Close span submatches.flags |= cxSPAN_IS_CLOSED; // Remove any leftover matches if (next_match != matches.end()) matches.erase(next_match, matches.end()); return si.pos; } else { // Check if we have overwritten following matches while (next_match != matches.end() && si.pos > scopeStart + (*next_match)->start) next_match = matches.erase(next_match); } // Avoid eternal loop with faulty regexs if (callout_start == callout_end && callout_end == si.pos) zeromatch = callout_id; } } }
void Styler_Syntax::XmlText(unsigned int offset, const submatch& sm, unsigned int start, unsigned int end, vector<char>& text) const { unsigned int textstart = start; for (auto_vector<stxmatch>::const_iterator p = sm.matches.begin(); p != sm.matches.end(); ++p) { const stxmatch& m = *(*p); // Check if there is overlap if (m.end <= start) continue; if (m.start >= m.end) break; const unsigned int matchstart = wxMax(start, m.start); const unsigned int matchend = wxMin(end, m.end); // Print text before submatch if (textstart < matchstart) { const unsigned int len = matchstart - textstart; text.resize(text.size() + len); cxLOCKDOC_READ(m_doc) doc.GetTextPart(offset + textstart, offset + matchstart, (unsigned char*)(&*text.end() - len)); cxENDLOCK } // Start tag const wxCharBuffer name = m.m_name.mb_str(); const size_t len = strlen(name.data()); if (len) { text.push_back('<'); text.insert(text.end(), name.data(), name.data() + len); text.push_back('>'); } // Subscopes if (m.subMatch.get()) { XmlText(offset + m.start, *m.subMatch, matchstart - m.start, matchend - m.start, text); } else { // Print text contained in submatch if (matchstart < matchend) { const unsigned int len = matchend - matchstart; text.resize(text.size() + len); cxLOCKDOC_READ(m_doc) doc.GetTextPart(offset + matchstart, offset + matchend, (unsigned char*)(&*text.end() - len)); cxENDLOCK } } // End tag if (len) { text.push_back('<'); text.push_back('/'); text.insert(text.end(), name.data(), name.data() + len); text.push_back('>'); } textstart = matchend; } // Print text after last submatch if (textstart < end) { const unsigned int len = end - textstart; text.resize(text.size() + len); cxLOCKDOC_READ(m_doc) doc.GetTextPart(offset + textstart, offset + end, (unsigned char*)(&*text.end() - len)); cxENDLOCK } }
void Styler_SearchHL::Style(StyleRun& sr) { const unsigned int rstart = sr.GetRunStart(); const unsigned int rend = sr.GetRunEnd(); // Style the run with search ranges for (vector<interval>::const_iterator r = m_searchRanges.begin(); r != m_searchRanges.end(); ++r) { if (r->end > rstart && r->start < rend) { unsigned int start = wxMax(rstart, r->start); unsigned int end = wxMin(rend, r->end); sr.SetBackgroundColor(start, end, m_rangeColor); } } // No need for more styling if no search text if (m_text.empty()) return; // Extend stylerun start/end to get better search results (round up to whole EXTSIZEs) unsigned int sr_start = rstart> 100 ? rstart - 100 : 0; const unsigned int ext_end = ((rend/EXTSIZE) * EXTSIZE) + EXTSIZE; unsigned int sr_end = ext_end < m_lines.GetLength() ? ext_end : m_lines.GetLength(); // Make sure the extended positions are valid cxLOCKDOC_READ(m_doc) sr_start = doc.GetValidCharPos(sr_start); if (sr_end != m_lines.GetLength()) sr_end = doc.GetValidCharPos(sr_end); cxENDLOCK //wxLogDebug("Style %u %u", rstart, rend); //wxLogDebug(" %u %u - %u %u", sr_start, sr_end, m_search_start, m_search_end); // Check if we need to do a new search if (sr_start < m_search_start || m_search_end < sr_end) { // Check if there is overlap so we can just extend the search area if (sr_end > m_search_start && sr_start < m_search_end) { sr_start = wxMin(sr_start, m_search_start); sr_end = wxMax(sr_end, m_search_end); } else { // Else we have to move it m_matches.clear(); m_search_start = 0; m_search_end = 0; } // Do the search if (sr_start < m_search_start) { // Search from top DoSearch(sr_start, sr_end); } else if (sr_end > m_search_end) { // Search from bottom DoSearch(sr_start, sr_end, true); } else wxASSERT(false); m_search_start = sr_start; m_search_end = sr_end; } // Style the run with matches for (vector<interval>::iterator p = m_matches.begin(); p != m_matches.end(); ++p) { if (p->start > rend) break; // Check for overlap (or zero-length sel at start-of-line) if ((p->end > rstart && p->start < rend) || (p->start == p->end && p->end == rstart)) { unsigned int start = wxMax(rstart, p->start); unsigned int end = wxMin(rend, p->end); // Only draw it if it is in range if (!m_searchRanges.empty()) { bool inRange = false; for (vector<interval>::const_iterator s = m_searchRanges.begin(); s != m_searchRanges.end(); ++s) { if (start >= s->start && start < s->end) { inRange = true; break; } } if (!inRange) continue; } ApplyStyle(sr, start, end); } } }
unsigned int FixedLine::SetLine(unsigned int startpos, unsigned int endpos, bool cache) { m_docLen = m_doc.GetLength(); #ifdef __WXDEBUG__ wxASSERT(!m_inSetLine); // re-entrancy check m_inSetLine = true; wxASSERT(startpos >= 0 && startpos < m_docLen); wxASSERT(endpos > startpos && endpos <= m_docLen); if (startpos > 0) { wxChar c; cxLOCKDOC_READ(m_doc) c = doc.GetChar(startpos - 1); cxENDLOCK wxASSERT(c == wxT('\n')); } if (endpos < m_docLen) { wxChar c; cxLOCKDOC_READ(m_doc) c = doc.GetChar(endpos - 1); cxENDLOCK wxASSERT(c == wxT('\n')); } #endif if (!cache || startpos != textstart || endpos != textend) { textstart = startpos; textend = endpos; m_lineLen = endpos - startpos; // Check if we need to resize line buffer if (m_lineBufferLen < m_lineLen) { m_lineBufferLen = m_lineLen; m_lineBuffer = wxCharBuffer(m_lineBufferLen); // wxCharBuffer allocs room for & adds nullbyte at len } // Cache the current line in lineBuffer (as UTF-8) cxLOCKDOC_READ(m_doc) doc.GetTextPart(textstart, textend, (unsigned char*)m_lineBuffer.data()); cxENDLOCK // Style the lines m_sr.Init(textstart, textend); for (vector<Styler*>::iterator si = m_stylers.begin(); si != m_stylers.end(); ++si) { (*si)->Style(m_sr); } m_extents.clear(); m_extents.reserve(m_lineLen); unsigned int xpos = 0; unsigned int lastpos = 0; unsigned int style_start = 0; int fontStyle = m_sr.GetFontStyle(0); char* dbi = m_lineBuffer.data(); // Build list of text extends (totalling) compensating for tabs and styles // There is one extent per byte in the text. In utf8 chars composed out of multiple // bytes, they will all have same value. for (unsigned int style_id = 0; style_id < m_sr.GetStyleCount(); ++style_id) { const unsigned int style_end = m_sr.GetStyleEnd(style_id) - textstart; // Ignore zero length styles if (style_start == style_end) continue; // Check for style change if (m_sr.GetFontStyle(style_id) != fontStyle) { if (style_start > lastpos) { m_sr.ApplyFontStyle(fontStyle); // Get extends for current segment m_extsBuf.clear(); const unsigned int seg_len = style_start-lastpos; const wxString text(dbi+lastpos, wxConvUTF8, seg_len); dc.GetPartialTextExtents(text, m_extsBuf); wxASSERT(!text.empty() && m_extsBuf.size() == text.size()); // Add to main list adjusted for offset unsigned int extpos = 0; unsigned int offset = xpos; for (unsigned int i = lastpos; i < style_start; ++i) { if ((dbi[i] & 0xC0) == 0x80) m_extents.push_back(xpos); // Only count first byte of UTF-8 multibyte chars else if (dbi[i] == '\t') { // Add tab extend xpos = ((xpos / tabwidth)+1) * tabwidth; // GetTabPoint(xpos); offset += xpos - (m_extsBuf[extpos] + offset); m_extents.push_back(xpos); ++extpos; } else { xpos = m_extsBuf[extpos] + offset; m_extents.push_back(xpos); ++extpos; } } // Small hack to make lines that end with italics not cut off the edge of the last character if (fontStyle &= wxFONTFLAG_ITALIC) { m_extents.back() += 2; xpos = m_extents.back(); } } lastpos = style_start; fontStyle = m_sr.GetFontStyle(style_id); } style_start = style_end; } // Add extends for last segment if (lastpos < m_lineLen) { m_sr.ApplyFontStyle(fontStyle); // Get extends for current segment m_extsBuf.clear(); const unsigned int seg_len = m_lineLen-lastpos; const wxString text(dbi+lastpos, wxConvUTF8, seg_len); dc.GetPartialTextExtents(text, m_extsBuf); wxASSERT(!text.empty() && m_extsBuf.size() == text.size()); // Add to main list adjusted for offset unsigned int extpos = 0; unsigned int offset = xpos; for (unsigned int i = lastpos; i < m_lineLen; ++i) { if ((dbi[i] & 0xC0) == 0x80) m_extents.push_back(xpos); // Only count first byte of UTF-8 multibyte chars else if (dbi[i] == '\t') { // Add tab extend xpos = ((xpos / tabwidth)+1) * tabwidth; // GetTabPoint(xpos); offset += xpos - (m_extsBuf[extpos] + offset); m_extents.push_back(xpos); ++extpos; } else { xpos = m_extsBuf[extpos] + offset; m_extents.push_back(xpos); ++extpos; } } } wxASSERT(m_extents.size() == m_lineLen); BreakLine(); } else if (width != old_width) { wxASSERT(m_extents.size() == m_lineLen); BreakLine(); } #ifdef __WXDEBUG__ m_inSetLine = false; // re-entrancy check #endif return (m_wrapMode == cxWRAP_NONE) ? m_lineWidth : height; }
void DocHistory::ReBuildTree() { if (m_document_id == -1) { wxASSERT(m_sourceDoc.IsDraft()); // The draft has no parent (and can't have children) // So just add a single entry. m_items.clear(); m_positions.clear(); item new_item; new_item.doc = m_sourceDoc; cxLOCKDOC_READ(m_doc) new_item.date = doc.GetDate(); cxENDLOCK new_item.parent = 0; // root is it's own parent new_item.ypos = 0; m_items.push_back(new_item); m_positions.push_back(0); m_selectedNode = 0; // Update timeline m_pTimeline->Clear(); m_pTimeline->AddItem(new_item.date); // Update the VersionTree m_pTree->Clear(); m_pTree->AddRoot(); } else { m_items.clear(); m_positions.clear(); // Add the root const doc_id root(DOCUMENT, m_document_id, 0); // root is always zero item new_item; new_item.doc = root; cxLOCK_READ(m_catalyst) new_item.date = catalyst.GetDocDate(root); new_item.parent = 0; // root is it's own parent new_item.ypos = 0; m_items.push_back(new_item); if (m_sourceDoc == root) m_selectedNode = 0; else m_selectedNode = -1; // not yet set // Build the tree AddChildren(0, root, m_sourceDoc, catalyst); cxENDLOCK wxASSERT(m_selectedNode >= 0 && m_selectedNode < (int)m_items.size()); // Update Timeline & VersionTree m_pTimeline->Clear(); m_pTree->Clear(); if (!m_items.empty()) { m_pTimeline->AddItem(new_item.date); m_pTree->AddRoot(); } for(unsigned int i = 1; i < m_items.size(); ++i) { const int ypos = m_pTimeline->AddItem(m_items[i].date); m_pTree->AddNode(m_items[i].parent, ypos); m_items[i].ypos = ypos; } } m_pTree->CalculateLayout(); m_treeHeight = m_items.empty() ? 0 : m_items.back().ypos + m_lineHeight; }