wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
bool layoutAll = true;
-
+
// Get invalid range, rounding to paragraph start/end.
wxRichTextRange invalidRange = GetInvalidRange(true);
- if (invalidRange == wxRichTextRange(-1, -1))
- layoutAll = true;
+ if (invalidRange == wxRICHTEXT_NONE)
+ return true;
+
+ if (invalidRange == wxRICHTEXT_ALL)
+ layoutAll = true;
else // If we know what range is affected, start laying out from that point on.
if (invalidRange.GetStart() > GetRange().GetStart())
{
if (firstParagraph)
{
wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
- wxRichTextObjectList::compatibility_iterator previousNode = firstNode ? node->GetPrevious() : (wxRichTextObjectList::compatibility_iterator) NULL;
+ wxRichTextObjectList::compatibility_iterator previousNode = firstNode ? firstNode->GetPrevious() : (wxRichTextObjectList::compatibility_iterator) NULL;
if (firstNode && previousNode)
{
wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
// Now we're going to start iterating from the first affected paragraph.
node = firstNode;
+
+ layoutAll = false;
}
}
}
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
wxASSERT (child != NULL);
- if (child && (layoutAll || !child->GetRange().IsOutside(invalidRange)))
+ // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
+ if (child && (layoutAll || child->GetLines().GetCount() == 0 || !child->GetRange().IsOutside(invalidRange)))
{
child->Layout(dc, availableSpace, style);
SetCachedSize(wxSize(maxWidth, availableSpace.y));
m_dirty = false;
- m_invalidRange = wxRichTextRange(-1, -1);
+ m_invalidRange = wxRICHTEXT_NONE;
return true;
}
{
wxRichTextLine* line = node2->GetData();
- if (line->GetRange().Contains(pos) ||
+ wxRichTextRange range = line->GetAbsoluteRange();
+
+ if (range.Contains(pos) ||
// If the position is end-of-paragraph, then return the last line of
// of the paragraph.
- (line->GetRange().GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
+ (range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
return line;
node2 = node2->GetNext();
/// Get the paragraph for a given line
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
{
- return GetParagraphAtPosition(line->GetRange().GetStart());
+ return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
}
/// Get the line size at the given position
while (node2)
{
wxRichTextLine* line = node2->GetData();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
- if (line->GetRange().Contains(pos))
+ if (lineRange.Contains(pos))
{
// If the caret is displayed at the end of the previous wrapped line,
// we want to return the line it's _displayed_ at (not the actual line
// containing the position).
- if (line->GetRange().GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
+ if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
return lineCount - 1;
else
return lineCount;
{
SetDirty(true);
- if (invalidRange == wxRichTextRange(-1, -1))
+ if (invalidRange == wxRICHTEXT_ALL)
{
- m_invalidRange = invalidRange;
+ m_invalidRange = wxRICHTEXT_ALL;
return;
}
+
+ // Already invalidating everything
+ if (m_invalidRange == wxRICHTEXT_ALL)
+ return;
- if (invalidRange.GetStart() < m_invalidRange.GetStart())
+ if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
m_invalidRange.SetStart(invalidRange.GetStart());
if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
m_invalidRange.SetEnd(invalidRange.GetEnd());
/// Get invalid range, rounding to entire paragraphs if argument is true.
wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
{
- if (m_invalidRange == wxRichTextRange(-1, -1))
+ if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
return m_invalidRange;
wxRichTextRange range = m_invalidRange;
while (node)
{
wxRichTextLine* line = node->GetData();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
int maxDescent = line->GetDescent();
while (node2)
{
wxRichTextObject* child = node2->GetData();
- if (!child->GetRange().IsOutside(line->GetRange()))
+ if (!child->GetRange().IsOutside(lineRange))
{
// Draw this part of the line at the correct position
wxRichTextRange objectRange(child->GetRange());
- objectRange.LimitTo(line->GetRange());
+ objectRange.LimitTo(lineRange);
wxSize objectSize;
int descent = 0;
objectPosition.x += objectSize.x;
}
- else if (child->GetRange().GetStart() > line->GetRange().GetEnd())
+ else if (child->GetRange().GetStart() > lineRange.GetEnd())
// Can break out of inner loop now since we've passed this line's range
break;
/// Lay the item out
bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
{
- ClearLines();
-
// Increase the size of the paragraph due to spacing
int spaceBeforePara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingBefore());
int spaceAfterPara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingAfter());
maxDescent = wxMax(childDescent, maxDescent);
// Add a new line
- wxRichTextLine* line = new wxRichTextLine(this);
- line->SetRange(actualRange);
+ wxRichTextLine* line = AllocateLine(lineCount);
+
+ // Set relative range so we won't have to change line ranges when paragraphs are moved
+ line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
line->SetPosition(currentPosition);
line->SetSize(wxSize(currentWidth, lineHeight));
line->SetDescent(maxDescent);
- m_cachedLines.Append(line);
-
// Now move down a line. TODO: add margins, spacing
currentPosition.y += lineHeight;
currentPosition.y += lineSpacing;
{
currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
- wxRichTextLine* line = new wxRichTextLine(this);
+ wxRichTextLine* line = AllocateLine(lineCount);
+
+ wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
+
+ // Set relative range so we won't have to change line ranges when paragraphs are moved
+ line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
- line->SetRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
line->SetPosition(currentPosition);
if (lineHeight == 0)
currentPosition.y += lineHeight;
currentPosition.y += lineSpacing;
lineCount ++;
-
- m_cachedLines.Append(line);
}
+ // Remove remaining unused line objects, if any
+ ClearUnusedLines(lineCount);
+
// Apply styles to wrapped lines
ApplyParagraphStyle(rect);
while (node)
{
wxRichTextLine* line = node->GetData();
- if (!line->GetRange().IsOutside(range))
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ if (!lineRange.IsOutside(range))
{
wxSize lineSize;
{
wxRichTextObject* child = node2->GetData();
- if (!child->GetRange().IsOutside(line->GetRange()))
+ if (!child->GetRange().IsOutside(lineRange))
{
- wxRichTextRange rangeToUse = line->GetRange();
+ wxRichTextRange rangeToUse = lineRange;
rangeToUse.LimitTo(child->GetRange());
wxSize childSize;
while (node)
{
wxRichTextLine* line = node->GetData();
- if (index >= line->GetRange().GetStart() && index <= line->GetRange().GetEnd())
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
{
// If this is the last point in the line, and we're forcing the
// returned value to be the start of the next line, do the required
// thing.
- if (index == line->GetRange().GetEnd() && forceLineStart)
+ if (index == lineRange.GetEnd() && forceLineStart)
{
if (node->GetNext())
{
pt.y = line->GetPosition().y + GetPosition().y;
- wxRichTextRange r(line->GetRange().GetStart(), index);
+ wxRichTextRange r(lineRange.GetStart(), index);
wxSize rangeSize;
int descent = 0;
wxRichTextLine* line = node->GetData();
wxPoint linePos = paraPos + line->GetPosition();
wxSize lineSize = line->GetSize();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
if (pt.y >= linePos.y && pt.y <= linePos.y + lineSize.y)
{
if (pt.x < linePos.x)
{
- textPosition = line->GetRange().GetStart();
+ textPosition = lineRange.GetStart();
return wxRICHTEXT_HITTEST_BEFORE;
}
else if (pt.x >= (linePos.x + lineSize.x))
{
- textPosition = line->GetRange().GetEnd();
+ textPosition = lineRange.GetEnd();
return wxRICHTEXT_HITTEST_AFTER;
}
else
{
long i;
int lastX = linePos.x;
- for (i = line->GetRange().GetStart(); i <= line->GetRange().GetEnd(); i++)
+ for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
{
wxSize childSize;
int descent = 0;
- wxRichTextRange rangeToUse(line->GetRange().GetStart(), i);
+ wxRichTextRange rangeToUse(lineRange.GetStart(), i);
GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED);
return text;
}
+/// Allocate or reuse a line object
+wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
+{
+ if (pos < (int) m_cachedLines.GetCount())
+ {
+ wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
+ line->Init(this);
+ return line;
+ }
+ else
+ {
+ wxRichTextLine* line = new wxRichTextLine(this);
+ m_cachedLines.Append(line);
+ return line;
+ }
+}
+
+/// Clear remaining unused line objects, if any
+bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
+{
+ int cachedLineCount = m_cachedLines.GetCount();
+ if ((int) cachedLineCount > lineCount)
+ {
+ for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
+ {
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
+ wxRichTextLine* line = node->GetData();
+ m_cachedLines.Erase(node);
+ delete line;
+ }
+ }
+ return true;
+}
+
/*!
* wxRichTextLine
wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
{
- Init();
-
- m_parent = parent;
+ Init(parent);
}
/// Initialisation
-void wxRichTextLine::Init()
+void wxRichTextLine::Init(wxRichTextParagraph* parent)
{
- m_parent = NULL;
+ m_parent = parent;
+ m_range.SetRange(-1, -1);
+ m_pos = wxPoint(0, 0);
+ m_size = wxSize(0, 0);
m_descent = 0;
}
return m_parent->GetPosition() + m_pos;
}
+/// Get the absolute range
+wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
+{
+ wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
+ range.SetEnd(range.GetStart() + m_range.GetLength()-1);
+ return range;
+}
+
/*!
* wxRichTextPlainText
* This object represents a single piece of text.
DeleteChildren();
GetCommandProcessor()->ClearCommands();
Modify(false);
+ Invalidate(wxRICHTEXT_ALL);
}
void wxRichTextBuffer::Reset()
AddParagraph(wxEmptyString);
GetCommandProcessor()->ClearCommands();
Modify(false);
+ Invalidate(wxRICHTEXT_ALL);
}
/// Submit command to insert the given text
sm_handlers.Clear();
}
-wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save)
+wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
{
+ if (types)
+ types->Clear();
+
wxString wildcard;
wxList::compatibility_iterator node = GetHandlers().GetFirst();
wildcard += handler->GetExtension();
wildcard += wxT(")|*.");
wildcard += handler->GetExtension();
+ if (types)
+ types->Add(handler->GetType());
}
count ++;
}
{
wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
if (handler)
- return handler->LoadFile(this, filename);
+ {
+ SetDefaultStyle(wxTextAttrEx());
+
+ bool success = handler->LoadFile(this, filename);
+ Invalidate(wxRICHTEXT_ALL);
+ return success;
+ }
else
return false;
}
{
wxRichTextFileHandler* handler = FindHandler(type);
if (handler)
- return handler->LoadFile(this, stream);
+ {
+ SetDefaultStyle(wxTextAttrEx());
+ bool success = handler->LoadFile(this, stream);
+ Invalidate(wxRICHTEXT_ALL);
+ return success;
+ }
else
return false;
}
{
m_buffer->InsertFragment(GetPosition(), m_newParagraphs);
m_buffer->UpdateRanges();
+ m_buffer->Invalidate(GetRange());
long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength() - 1;
if (m_newParagraphs.GetPartialParagraph())
{
m_buffer->DeleteRange(GetRange());
m_buffer->UpdateRanges();
+ m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
UpdateAppearance(GetRange().GetStart()-1, true /* send update event */);
case wxRICHTEXT_CHANGE_STYLE:
{
ApplyParagraphs(GetNewParagraphs());
+ m_buffer->Invalidate(GetRange());
UpdateAppearance(GetPosition());
{
m_buffer->DeleteRange(GetRange());
m_buffer->UpdateRanges();
+ m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
long newCaretPosition = GetPosition() - 1;
// if (m_newParagraphs.GetPartialParagraph())
{
m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
m_buffer->UpdateRanges();
+ m_buffer->Invalidate(GetRange());
UpdateAppearance(GetPosition(), true /* send update event */);
case wxRICHTEXT_CHANGE_STYLE:
{
ApplyParagraphs(GetOldParagraphs());
+ m_buffer->Invalidate(GetRange());
UpdateAppearance(GetPosition());
wxString str;
int ch = 0;
+ int lastCh = 0;
while (!stream.Eof())
{
ch = stream.GetC();
- if (ch > 0)
+ if (ch == 10 && lastCh != 13)
+ str += wxT('\n');
+
+ if (ch > 0 && ch != 10)
str += wxChar(ch);
+
+ lastCh = ch;
}
buffer->Clear();
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
- if (line && para && line->GetRange().GetStart() == position && para->GetRange().GetStart() != position)
+ if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
caretAtLineStart = true;
position --;
}
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
- if (line && para && line->GetRange().GetStart() == position && para->GetRange().GetStart() != position)
+ if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
caretAtLineStart = true;
position --;
}
if (line)
{
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
// We're at the end of a line. See whether we need to
// stay at the same actual caret position but change visual
// position, or not.
- if (oldPosition == line->GetRange().GetEnd())
+ if (oldPosition == lineRange.GetEnd())
{
if (m_caretAtLineStart)
{
if (line)
{
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
// We're at the start of a line. See whether we need to
// stay at the same actual caret position but change visual
// position, or not.
- if (oldPosition == line->GetRange().GetStart())
+ if (oldPosition == lineRange.GetStart())
{
m_caretPosition = oldPosition-1;
m_caretAtLineStart = true;
return;
}
- else if (oldPosition == line->GetRange().GetEnd())
+ else if (oldPosition == lineRange.GetEnd())
{
if (m_caretAtLineStart)
{
if (hitTest == wxRICHTEXT_HITTEST_BEFORE)
{
wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1);
- if (thisLine && (newPos-1) == thisLine->GetRange().GetEnd())
+ wxRichTextRange lineRange;
+ if (thisLine)
+ lineRange = thisLine->GetAbsoluteRange();
+
+ if (thisLine && (newPos-1) == lineRange.GetEnd())
{
- // if (para->GetRange().GetStart() != thisLine->GetRange().GetStart())
- {
- newPos --;
- caretLineStart = true;
- }
+ newPos --;
+ caretLineStart = true;
}
else
{
if (line)
{
- long newPos = line->GetRange().GetEnd();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ long newPos = lineRange.GetEnd();
if (!ExtendSelection(m_caretPosition, newPos, flags))
SelectNone();
wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
if (line)
{
- long newPos = line->GetRange().GetStart()-1;
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ long newPos = lineRange.GetStart()-1;
if (!ExtendSelection(m_caretPosition, newPos, flags))
SelectNone();
wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line);
- SetCaretPosition(newPos, para->GetRange().GetStart() != line->GetRange().GetStart());
+ SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
PositionCaret();
SetDefaultStyleToCursorStyle();
wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY);
if (newLine)
{
- long pos = newLine->GetRange().GetStart()-1;
+ wxRichTextRange lineRange = newLine->GetAbsoluteRange();
+ long pos = lineRange.GetStart()-1;
if (pos != m_caretPosition)
{
wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine);
if (!ExtendSelection(m_caretPosition, pos, flags))
SelectNone();
- SetCaretPosition(pos, para->GetRange().GetStart() != newLine->GetRange().GetStart());
+ SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
PositionCaret();
SetDefaultStyleToCursorStyle();
/// Sizing
void wxRichTextCtrl::OnSize(wxSizeEvent& event)
{
- GetBuffer().SetDirty(true);
+ GetBuffer().Invalidate(wxRICHTEXT_ALL);
RecreateBuffer();
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true);
if (line)
{
- if (caretPosition == line->GetRange().GetStart()-1 &&
- (para->GetRange().GetStart() != line->GetRange().GetStart()))
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+ if (caretPosition == lineRange.GetStart()-1 &&
+ (para->GetRange().GetStart() != lineRange.GetStart()))
{
if (!m_caretAtLineStart)
line = GetBuffer().GetLineAtPosition(caretPosition-1, true);
/// setting the caret position.
bool wxRichTextCtrl::Layout()
{
- wxRect availableSpace(GetClientSize());
- if (availableSpace.width == 0)
- availableSpace.width = 10;
- if (availableSpace.height == 0)
- availableSpace.height = 10;
-
- wxClientDC dc(this);
- dc.SetFont(GetFont());
-
- PrepareDC(dc);
-
- GetBuffer().Defragment();
- GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
- GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
- GetBuffer().SetDirty(false);
-
- if (!IsFrozen())
- SetupScrollbars();
+ if (GetBuffer().GetDirty())
+ {
+ wxRect availableSpace(GetClientSize());
+ if (availableSpace.width == 0)
+ availableSpace.width = 10;
+ if (availableSpace.height == 0)
+ availableSpace.height = 10;
+
+ wxClientDC dc(this);
+ dc.SetFont(GetFont());
+
+ PrepareDC(dc);
+
+ GetBuffer().Defragment();
+ GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
+ GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
+ GetBuffer().SetDirty(false);
+
+ if (!IsFrozen())
+ SetupScrollbars();
+ }
return true;
}