1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        richtext/richtextbuffer.cpp 
   3 // Purpose:     Buffer for wxRichTextCtrl 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  27 #include "wx/filename.h" 
  28 #include "wx/clipbrd.h" 
  29 #include "wx/wfstream.h" 
  30 #include "wx/module.h" 
  31 #include "wx/mstream.h" 
  32 #include "wx/sstream.h" 
  34 #include "wx/richtext/richtextbuffer.h" 
  35 #include "wx/richtext/richtextctrl.h" 
  36 #include "wx/richtext/richtextstyles.h" 
  38 #include "wx/listimpl.cpp" 
  40 WX_DEFINE_LIST(wxRichTextObjectList
); 
  41 WX_DEFINE_LIST(wxRichTextLineList
); 
  45  * This is the base for drawable objects. 
  48 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
) 
  50 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
) 
  62 wxRichTextObject::~wxRichTextObject() 
  66 void wxRichTextObject::Dereference() 
  74 void wxRichTextObject::Copy(const wxRichTextObject
& obj
) 
  78     m_dirty 
= obj
.m_dirty
; 
  79     m_range 
= obj
.m_range
; 
  80     m_attributes 
= obj
.m_attributes
; 
  81     m_descent 
= obj
.m_descent
; 
  83     if (!m_attributes
.GetFont().Ok()) 
  84         wxLogDebug(wxT("No font!")); 
  85     if (!obj
.m_attributes
.GetFont().Ok()) 
  86         wxLogDebug(wxT("Parent has no font!")); 
  89 void wxRichTextObject::SetMargins(int margin
) 
  91     m_leftMargin 
= m_rightMargin 
= m_topMargin 
= m_bottomMargin 
= margin
; 
  94 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
) 
  96     m_leftMargin 
= leftMargin
; 
  97     m_rightMargin 
= rightMargin
; 
  98     m_topMargin 
= topMargin
; 
  99     m_bottomMargin 
= bottomMargin
; 
 102 // Convert units in tends of a millimetre to device units 
 103 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) 
 105     int ppi 
= dc
.GetPPI().x
; 
 107     // There are ppi pixels in 254.1 "1/10 mm" 
 109     double pixels 
= ((double) units 
* (double)ppi
) / 254.1; 
 114 /// Dump to output stream for debugging 
 115 void wxRichTextObject::Dump(wxTextOutputStream
& stream
) 
 117     stream 
<< GetClassInfo()->GetClassName() << wxT("\n"); 
 118     stream 
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n"); 
 119     stream 
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n"); 
 124  * wxRichTextCompositeObject 
 125  * This is the base for drawable objects. 
 128 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
) 
 130 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
): 
 131     wxRichTextObject(parent
) 
 135 wxRichTextCompositeObject::~wxRichTextCompositeObject() 
 140 /// Get the nth child 
 141 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const 
 143     wxASSERT ( n 
< m_children
.GetCount() ); 
 145     return m_children
.Item(n
)->GetData(); 
 148 /// Append a child, returning the position 
 149 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
) 
 151     m_children
.Append(child
); 
 152     child
->SetParent(this); 
 153     return m_children
.GetCount() - 1; 
 156 /// Insert the child in front of the given object, or at the beginning 
 157 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
) 
 161         wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(inFrontOf
); 
 162         m_children
.Insert(node
, child
); 
 165         m_children
.Insert(child
); 
 166     child
->SetParent(this); 
 172 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
) 
 174     wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(child
); 
 178             delete node
->GetData(); 
 186 /// Delete all children 
 187 bool wxRichTextCompositeObject::DeleteChildren() 
 189     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 192         wxRichTextObjectList::compatibility_iterator oldNode 
= node
; 
 194         wxRichTextObject
* child 
= node
->GetData(); 
 195         child
->Dereference(); // Only delete if reference count is zero 
 197         node 
= node
->GetNext(); 
 204 /// Get the child count 
 205 size_t wxRichTextCompositeObject::GetChildCount() const 
 207     return m_children
.GetCount(); 
 211 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
) 
 213     wxRichTextObject::Copy(obj
); 
 217     wxRichTextObjectList::compatibility_iterator node 
= obj
.m_children
.GetFirst(); 
 220         wxRichTextObject
* child 
= node
->GetData(); 
 221         m_children
.Append(child
->Clone()); 
 223         node 
= node
->GetNext(); 
 227 /// Hit-testing: returns a flag indicating hit test details, plus 
 228 /// information about position 
 229 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
) 
 231     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 234         wxRichTextObject
* child 
= node
->GetData(); 
 236         int ret 
= child
->HitTest(dc
, pt
, textPosition
); 
 237         if (ret 
!= wxRICHTEXT_HITTEST_NONE
) 
 240         node 
= node
->GetNext(); 
 243     return wxRICHTEXT_HITTEST_NONE
; 
 246 /// Finds the absolute position and row height for the given character position 
 247 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
) 
 249     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 252         wxRichTextObject
* child 
= node
->GetData(); 
 254         if (child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
)) 
 257         node 
= node
->GetNext(); 
 264 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
) 
 266     long current 
= start
; 
 267     long lastEnd 
= current
; 
 269     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 272         wxRichTextObject
* child 
= node
->GetData(); 
 275         child
->CalculateRange(current
, childEnd
); 
 278         current 
= childEnd 
+ 1; 
 280         node 
= node
->GetNext(); 
 285     // An object with no children has zero length 
 286     if (m_children
.GetCount() == 0) 
 289     m_range
.SetRange(start
, end
); 
 292 /// Delete range from layout. 
 293 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
) 
 295     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 299         wxRichTextObject
* obj 
= (wxRichTextObject
*) node
->GetData(); 
 300         wxRichTextObjectList::compatibility_iterator next 
= node
->GetNext(); 
 302         // Delete the range in each paragraph 
 304         // When a chunk has been deleted, internally the content does not 
 305         // now match the ranges. 
 306         // However, so long as deletion is not done on the same object twice this is OK. 
 307         // If you may delete content from the same object twice, recalculate 
 308         // the ranges inbetween DeleteRange calls by calling CalculateRanges, and 
 309         // adjust the range you're deleting accordingly. 
 311         if (!obj
->GetRange().IsOutside(range
)) 
 313             obj
->DeleteRange(range
); 
 315             // Delete an empty object, or paragraph within this range. 
 316             if (obj
->IsEmpty() || 
 317                 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd())) 
 319                 // An empty paragraph has length 1, so won't be deleted unless the 
 320                 // whole range is deleted. 
 321                 RemoveChild(obj
, true); 
 331 /// Get any text in this object for the given range 
 332 wxString 
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const 
 335     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 338         wxRichTextObject
* child 
= node
->GetData(); 
 339         wxRichTextRange childRange 
= range
; 
 340         if (!child
->GetRange().IsOutside(range
)) 
 342             childRange
.LimitTo(child
->GetRange()); 
 344             wxString childText 
= child
->GetTextForRange(childRange
); 
 348         node 
= node
->GetNext(); 
 354 /// Recursively merge all pieces that can be merged. 
 355 bool wxRichTextCompositeObject::Defragment() 
 357     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 360         wxRichTextObject
* child 
= node
->GetData(); 
 361         wxRichTextCompositeObject
* composite 
= wxDynamicCast(child
, wxRichTextCompositeObject
); 
 363             composite
->Defragment(); 
 367             wxRichTextObject
* nextChild 
= node
->GetNext()->GetData(); 
 368             if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
)) 
 370                 nextChild
->Dereference(); 
 371                 delete node
->GetNext(); 
 373                 // Don't set node -- we'll see if we can merge again with the next 
 377                 node 
= node
->GetNext(); 
 380             node 
= node
->GetNext(); 
 386 /// Dump to output stream for debugging 
 387 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
) 
 389     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 392         wxRichTextObject
* child 
= node
->GetData(); 
 394         node 
= node
->GetNext(); 
 401  * This defines a 2D space to lay out objects 
 404 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextCompositeObject
) 
 406 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
): 
 407     wxRichTextCompositeObject(parent
) 
 412 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& WXUNUSED(rect
), int descent
, int style
) 
 414     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 417         wxRichTextObject
* child 
= node
->GetData(); 
 419         wxRect childRect 
= wxRect(child
->GetPosition(), child
->GetCachedSize()); 
 420         child
->Draw(dc
, range
, selectionRange
, childRect
, descent
, style
); 
 422         node 
= node
->GetNext(); 
 428 bool wxRichTextBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
) 
 430     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 433         wxRichTextObject
* child 
= node
->GetData(); 
 434         child
->Layout(dc
, rect
, style
); 
 436         node 
= node
->GetNext(); 
 442 /// Get/set the size for the given range. Assume only has one child. 
 443 bool wxRichTextBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
) const 
 445     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 448         wxRichTextObject
* child 
= node
->GetData(); 
 449         return child
->GetRangeSize(range
, size
, descent
, dc
, flags
); 
 456 void wxRichTextBox::Copy(const wxRichTextBox
& obj
) 
 458     wxRichTextCompositeObject::Copy(obj
); 
 463  * wxRichTextParagraphLayoutBox 
 464  * This box knows how to lay out paragraphs. 
 467 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextBox
) 
 469 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
): 
 470     wxRichTextBox(parent
) 
 475 /// Initialize the object. 
 476 void wxRichTextParagraphLayoutBox::Init() 
 480     // For now, assume is the only box and has no initial size. 
 481     m_range 
= wxRichTextRange(0, -1); 
 483     m_invalidRange
.SetRange(-1, -1); 
 491 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int style
) 
 493     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 496         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 497         wxASSERT (child 
!= NULL
); 
 499         if (child 
&& !child
->GetRange().IsOutside(range
)) 
 501             wxRect 
childRect(child
->GetPosition(), child
->GetCachedSize()); 
 503             if (childRect
.GetTop() > rect
.GetBottom() || childRect
.GetBottom() < rect
.GetTop()) 
 508                 child
->Draw(dc
, child
->GetRange(), selectionRange
, childRect
, descent
, style
); 
 511         node 
= node
->GetNext(); 
 517 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
) 
 519     wxRect availableSpace
; 
 520     bool formatRect 
= (style 
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
; 
 522     // If only laying out a specific area, the passed rect has a different meaning: 
 523     // the visible part of the buffer. 
 526         availableSpace 
= wxRect(0 + m_leftMargin
, 
 528                           rect
.width 
- m_leftMargin 
- m_rightMargin
, 
 531         // Invalidate the part of the buffer from the first visible line 
 532         // to the end. If other parts of the buffer are currently invalid, 
 533         // then they too will be taken into account if they are above 
 534         // the visible point. 
 536         wxRichTextLine
* line 
= GetLineAtYPosition(rect
.y
); 
 538             startPos 
= line
->GetAbsoluteRange().GetStart(); 
 540         Invalidate(wxRichTextRange(startPos
, GetRange().GetEnd())); 
 543         availableSpace 
= wxRect(rect
.x 
+ m_leftMargin
, 
 544                           rect
.y 
+ m_topMargin
, 
 545                           rect
.width 
- m_leftMargin 
- m_rightMargin
, 
 546                           rect
.height 
- m_topMargin 
- m_bottomMargin
); 
 550     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 552     bool layoutAll 
= true; 
 554     // Get invalid range, rounding to paragraph start/end. 
 555     wxRichTextRange invalidRange 
= GetInvalidRange(true); 
 557     if (invalidRange 
== wxRICHTEXT_NONE 
&& !formatRect
) 
 560     if (invalidRange 
== wxRICHTEXT_ALL
) 
 562     else    // If we know what range is affected, start laying out from that point on. 
 563         if (invalidRange
.GetStart() > GetRange().GetStart()) 
 565         wxRichTextParagraph
* firstParagraph 
= GetParagraphAtPosition(invalidRange
.GetStart()); 
 568             wxRichTextObjectList::compatibility_iterator firstNode 
= m_children
.Find(firstParagraph
); 
 569             wxRichTextObjectList::compatibility_iterator previousNode 
= firstNode 
? firstNode
->GetPrevious() : (wxRichTextObjectList::compatibility_iterator
) NULL
; 
 570             if (firstNode 
&& previousNode
) 
 572                 wxRichTextParagraph
* previousParagraph 
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
); 
 573                 availableSpace
.y 
= previousParagraph
->GetPosition().y 
+ previousParagraph
->GetCachedSize().y
; 
 575                 // Now we're going to start iterating from the first affected paragraph. 
 583     // A way to force speedy rest-of-buffer layout (the 'else' below) 
 584     bool forceQuickLayout 
= false; 
 588         // Assume this box only contains paragraphs 
 590         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 591         wxASSERT (child 
!= NULL
); 
 593         // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines 
 594         if (child 
&& !forceQuickLayout 
&& (layoutAll 
|| child
->GetLines().GetCount() == 0 || !child
->GetRange().IsOutside(invalidRange
))) 
 596             child
->Layout(dc
, availableSpace
, style
); 
 598             // Layout must set the cached size 
 599             availableSpace
.y 
+= child
->GetCachedSize().y
; 
 600             maxWidth 
= wxMax(maxWidth
, child
->GetCachedSize().x
); 
 602             // If we're just formatting the visible part of the buffer, 
 603             // and we're now past the bottom of the window, start quick 
 605             if (formatRect 
&& child
->GetPosition().y 
> rect
.GetBottom()) 
 606                 forceQuickLayout 
= true; 
 610             // We're outside the immediately affected range, so now let's just 
 611             // move everything up or down. This assumes that all the children have previously 
 612             // been laid out and have wrapped line lists associated with them. 
 613             // TODO: check all paragraphs before the affected range. 
 615             int inc 
= availableSpace
.y 
- child
->GetPosition().y
; 
 619                 wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 622                     if (child
->GetLines().GetCount() == 0) 
 623                         child
->Layout(dc
, availableSpace
, style
); 
 625                         child
->SetPosition(wxPoint(child
->GetPosition().x
, child
->GetPosition().y 
+ inc
)); 
 627                     availableSpace
.y 
+= child
->GetCachedSize().y
; 
 628                     maxWidth 
= wxMax(maxWidth
, child
->GetCachedSize().x
); 
 631                 node 
= node
->GetNext(); 
 636         node 
= node
->GetNext(); 
 639     SetCachedSize(wxSize(maxWidth
, availableSpace
.y
)); 
 642     m_invalidRange 
= wxRICHTEXT_NONE
; 
 648 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
) 
 650     wxRichTextBox::Copy(obj
); 
 653 /// Get/set the size for the given range. 
 654 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
) const 
 658     wxRichTextObjectList::compatibility_iterator startPara 
= NULL
; 
 659     wxRichTextObjectList::compatibility_iterator endPara 
= NULL
; 
 661     // First find the first paragraph whose starting position is within the range. 
 662     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 665         // child is a paragraph 
 666         wxRichTextObject
* child 
= node
->GetData(); 
 667         const wxRichTextRange
& r 
= child
->GetRange(); 
 669         if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart()) 
 675         node 
= node
->GetNext(); 
 678     // Next find the last paragraph containing part of the range 
 679     node 
= m_children
.GetFirst(); 
 682         // child is a paragraph 
 683         wxRichTextObject
* child 
= node
->GetData(); 
 684         const wxRichTextRange
& r 
= child
->GetRange(); 
 686         if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd()) 
 692         node 
= node
->GetNext(); 
 695     if (!startPara 
|| !endPara
) 
 698     // Now we can add up the sizes 
 699     for (node 
= startPara
; node 
; node 
= node
->GetNext()) 
 701         // child is a paragraph 
 702         wxRichTextObject
* child 
= node
->GetData(); 
 703         const wxRichTextRange
& childRange 
= child
->GetRange(); 
 704         wxRichTextRange rangeToFind 
= range
; 
 705         rangeToFind
.LimitTo(childRange
); 
 709         int childDescent 
= 0; 
 710         child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
); 
 712         descent 
= wxMax(childDescent
, descent
); 
 714         sz
.x 
= wxMax(sz
.x
, childSize
.x
); 
 726 /// Get the paragraph at the given position 
 727 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const 
 732     // First find the first paragraph whose starting position is within the range. 
 733     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 736         // child is a paragraph 
 737         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 738         wxASSERT (child 
!= NULL
); 
 740         // Return first child in buffer if position is -1 
 744         if (child
->GetRange().Contains(pos
)) 
 747         node 
= node
->GetNext(); 
 752 /// Get the line at the given position 
 753 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const 
 758     // First find the first paragraph whose starting position is within the range. 
 759     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 762         // child is a paragraph 
 763         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 764         wxASSERT (child 
!= NULL
); 
 766         wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
 769             wxRichTextLine
* line 
= node2
->GetData(); 
 771             wxRichTextRange range 
= line
->GetAbsoluteRange(); 
 773             if (range
.Contains(pos
) || 
 775                 // If the position is end-of-paragraph, then return the last line of 
 777                 (range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos 
== child
->GetRange().GetEnd())) 
 780             node2 
= node2
->GetNext(); 
 783         node 
= node
->GetNext(); 
 786     int lineCount 
= GetLineCount(); 
 788         return GetLineForVisibleLineNumber(lineCount
-1); 
 793 /// Get the line at the given y pixel position, or the last line. 
 794 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const 
 796     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 799         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 800         wxASSERT (child 
!= NULL
); 
 802         wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
 805             wxRichTextLine
* line 
= node2
->GetData(); 
 807             wxRect 
rect(line
->GetRect()); 
 809             if (y 
<= rect
.GetBottom()) 
 812             node2 
= node2
->GetNext(); 
 815         node 
= node
->GetNext(); 
 819     int lineCount 
= GetLineCount(); 
 821         return GetLineForVisibleLineNumber(lineCount
-1); 
 826 /// Get the number of visible lines 
 827 int wxRichTextParagraphLayoutBox::GetLineCount() const 
 831     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 834         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 835         wxASSERT (child 
!= NULL
); 
 837         count 
+= child
->GetLines().GetCount(); 
 838         node 
= node
->GetNext(); 
 844 /// Get the paragraph for a given line 
 845 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const 
 847     return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart()); 
 850 /// Get the line size at the given position 
 851 wxSize 
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const 
 853     wxRichTextLine
* line 
= GetLineAtPosition(pos
, caretPosition
); 
 856         return line
->GetSize(); 
 863 /// Convenience function to add a paragraph of text 
 864 wxRichTextRange 
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
) 
 866     wxTextAttrEx 
style(GetAttributes()); 
 868     // Apply default style. If the style has no attributes set, 
 869     // then the attributes will remain the 'basic style' (i.e. the 
 870     // layout box's style). 
 871     wxRichTextApplyStyle(style
, GetDefaultStyle()); 
 873     wxRichTextParagraph
* para 
= new wxRichTextParagraph(text
, this, & style
); 
 880     return para
->GetRange(); 
 883 /// Adds multiple paragraphs, based on newlines. 
 884 wxRichTextRange 
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
) 
 886     wxTextAttrEx 
style(GetAttributes()); 
 887     //wxLogDebug("Initial style = %s", style.GetFont().GetFaceName()); 
 888     //wxLogDebug("Initial size = %d", style.GetFont().GetPointSize()); 
 890     // Apply default style. If the style has no attributes set, 
 891     // then the attributes will remain the 'basic style' (i.e. the 
 892     // layout box's style). 
 893     wxRichTextApplyStyle(style
, GetDefaultStyle()); 
 895     //wxLogDebug("Style after applying default style = %s", style.GetFont().GetFaceName()); 
 896     //wxLogDebug("Size after applying default style = %d", style.GetFont().GetPointSize()); 
 898     wxRichTextParagraph
* firstPara 
= NULL
; 
 899     wxRichTextParagraph
* lastPara 
= NULL
; 
 901     wxRichTextRange 
range(-1, -1); 
 903     size_t len 
= text
.Length(); 
 908         if (ch 
== wxT('\n') || ch 
== wxT('\r')) 
 910             wxRichTextParagraph
* para 
= new wxRichTextParagraph(line
, this, & style
); 
 916             line 
= wxEmptyString
; 
 925         lastPara 
= new wxRichTextParagraph(line
, this, & style
); 
 926         //wxLogDebug("Para Face = %s", lastPara->GetAttributes().GetFont().GetFaceName()); 
 927         AppendChild(lastPara
); 
 931         range
.SetStart(firstPara
->GetRange().GetStart()); 
 933         range
.SetStart(lastPara
->GetRange().GetStart()); 
 936         range
.SetEnd(lastPara
->GetRange().GetEnd()); 
 938         range
.SetEnd(firstPara
->GetRange().GetEnd()); 
 946 /// Convenience function to add an image 
 947 wxRichTextRange 
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
) 
 949     wxTextAttrEx 
style(GetAttributes()); 
 951     // Apply default style. If the style has no attributes set, 
 952     // then the attributes will remain the 'basic style' (i.e. the 
 953     // layout box's style). 
 954     wxRichTextApplyStyle(style
, GetDefaultStyle()); 
 956     wxRichTextParagraph
* para 
= new wxRichTextParagraph(this, & style
); 
 958     para
->AppendChild(new wxRichTextImage(image
, this)); 
 963     return para
->GetRange(); 
 967 /// Insert fragment into this box at the given position. If partialParagraph is true, 
 968 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph 
 970 /// TODO: if fragment is inserted inside styled fragment, must apply that style to 
 971 /// to the data (if it has a default style, anyway). 
 973 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextFragment
& fragment
) 
 977     // First, find the first paragraph whose starting position is within the range. 
 978     wxRichTextParagraph
* para 
= GetParagraphAtPosition(position
); 
 981         wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(para
); 
 983         // Now split at this position, returning the object to insert the new 
 985         wxRichTextObject
* nextObject 
= para
->SplitAt(position
); 
 987         // Special case: partial paragraph, just one paragraph. Might be a small amount of 
 988         // text, for example, so let's optimize. 
 990         if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1) 
 992             // Add the first para to this para... 
 993             wxRichTextObjectList::compatibility_iterator firstParaNode 
= fragment
.GetChildren().GetFirst(); 
 997             // Iterate through the fragment paragraph inserting the content into this paragraph. 
 998             wxRichTextParagraph
* firstPara 
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
); 
 999             wxASSERT (firstPara 
!= NULL
); 
1001             wxRichTextObjectList::compatibility_iterator objectNode 
= firstPara
->GetChildren().GetFirst(); 
1004                 wxRichTextObject
* newObj 
= objectNode
->GetData()->Clone(); 
1009                     para
->AppendChild(newObj
); 
1013                     // Insert before nextObject 
1014                     para
->InsertChild(newObj
, nextObject
); 
1017                 objectNode 
= objectNode
->GetNext(); 
1024             // Procedure for inserting a fragment consisting of a number of 
1027             // 1. Remove and save the content that's after the insertion point, for adding 
1028             //    back once we've added the fragment. 
1029             // 2. Add the content from the first fragment paragraph to the current 
1031             // 3. Add remaining fragment paragraphs after the current paragraph. 
1032             // 4. Add back the saved content from the first paragraph. If partialParagraph 
1033             //    is true, add it to the last paragraph added and not a new one. 
1035             // 1. Remove and save objects after split point. 
1036             wxList savedObjects
; 
1038                 para
->MoveToList(nextObject
, savedObjects
); 
1040             // 2. Add the content from the 1st fragment paragraph. 
1041             wxRichTextObjectList::compatibility_iterator firstParaNode 
= fragment
.GetChildren().GetFirst(); 
1045             wxRichTextParagraph
* firstPara 
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
); 
1046             wxASSERT(firstPara 
!= NULL
); 
1048             wxRichTextObjectList::compatibility_iterator objectNode 
= firstPara
->GetChildren().GetFirst(); 
1051                 wxRichTextObject
* newObj 
= objectNode
->GetData()->Clone(); 
1054                 para
->AppendChild(newObj
); 
1056                 objectNode 
= objectNode
->GetNext(); 
1059             // 3. Add remaining fragment paragraphs after the current paragraph. 
1060             wxRichTextObjectList::compatibility_iterator nextParagraphNode 
= node
->GetNext(); 
1061             wxRichTextObject
* nextParagraph 
= NULL
; 
1062             if (nextParagraphNode
) 
1063                 nextParagraph 
= nextParagraphNode
->GetData(); 
1065             wxRichTextObjectList::compatibility_iterator i 
= fragment
.GetChildren().GetFirst()->GetNext(); 
1066             wxRichTextParagraph
* finalPara 
= para
; 
1068             // If there was only one paragraph, we need to insert a new one. 
1071                 finalPara 
= new wxRichTextParagraph
; 
1073                 // TODO: These attributes should come from the subsequent paragraph 
1074                 // when originally deleted, since the subsequent para takes on 
1075                 // the previous para's attributes. 
1076                 finalPara
->SetAttributes(firstPara
->GetAttributes()); 
1079                     InsertChild(finalPara
, nextParagraph
); 
1081                     AppendChild(finalPara
); 
1085                 wxRichTextParagraph
* para 
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
); 
1086                 wxASSERT( para 
!= NULL 
); 
1088                 finalPara 
= (wxRichTextParagraph
*) para
->Clone(); 
1091                     InsertChild(finalPara
, nextParagraph
); 
1093                     AppendChild(finalPara
); 
1098             // 4. Add back the remaining content. 
1101                 finalPara
->MoveFromList(savedObjects
); 
1103                 // Ensure there's at least one object 
1104                 if (finalPara
->GetChildCount() == 0) 
1106                     wxRichTextPlainText
* text 
= new wxRichTextPlainText(wxEmptyString
); 
1107                     text
->SetAttributes(finalPara
->GetAttributes()); 
1109                     finalPara
->AppendChild(text
); 
1119         wxRichTextObjectList::compatibility_iterator i 
= fragment
.GetChildren().GetFirst(); 
1122             wxRichTextParagraph
* para 
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
); 
1123             wxASSERT( para 
!= NULL 
); 
1125             AppendChild(para
->Clone()); 
1136 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'. 
1137 /// If there was an incomplete paragraph at the end, partialParagraph is set to true. 
1138 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextFragment
& fragment
) 
1140     wxRichTextObjectList::compatibility_iterator i 
= GetChildren().GetFirst(); 
1143         wxRichTextParagraph
* para 
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
); 
1144         wxASSERT( para 
!= NULL 
); 
1146         if (!para
->GetRange().IsOutside(range
)) 
1148             fragment
.AppendChild(para
->Clone()); 
1153     // Now top and tail the first and last paragraphs in our new fragment (which might be the same). 
1154     if (!fragment
.IsEmpty()) 
1156         wxRichTextRange 
topTailRange(range
); 
1158         wxRichTextParagraph
* firstPara 
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
); 
1159         wxASSERT( firstPara 
!= NULL 
); 
1161         // Chop off the start of the paragraph 
1162         if (topTailRange
.GetStart() > firstPara
->GetRange().GetStart()) 
1164             wxRichTextRange 
r(firstPara
->GetRange().GetStart(), topTailRange
.GetStart()-1); 
1165             firstPara
->DeleteRange(r
); 
1167             // Make sure the numbering is correct 
1169             fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
); 
1171             // Now, we've deleted some positions, so adjust the range 
1173             topTailRange
.SetEnd(topTailRange
.GetEnd() - r
.GetLength()); 
1176         wxRichTextParagraph
* lastPara 
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
); 
1177         wxASSERT( lastPara 
!= NULL 
); 
1179         if (topTailRange
.GetEnd() < (lastPara
->GetRange().GetEnd()-1)) 
1181             wxRichTextRange 
r(topTailRange
.GetEnd()+1, lastPara
->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */ 
1182             lastPara
->DeleteRange(r
); 
1184             // Make sure the numbering is correct 
1186             fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
); 
1188             // We only have part of a paragraph at the end 
1189             fragment
.SetPartialParagraph(true); 
1193             if (topTailRange
.GetEnd() == (lastPara
->GetRange().GetEnd() - 1)) 
1194                 // We have a partial paragraph (don't save last new paragraph marker) 
1195                 fragment
.SetPartialParagraph(true); 
1197                 // We have a complete paragraph 
1198                 fragment
.SetPartialParagraph(false); 
1205 /// Given a position, get the number of the visible line (potentially many to a paragraph), 
1206 /// starting from zero at the start of the buffer. 
1207 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const 
1214     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1217         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1218         wxASSERT( child 
!= NULL 
); 
1220         if (child
->GetRange().Contains(pos
)) 
1222             wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
1225                 wxRichTextLine
* line 
= node2
->GetData(); 
1226                 wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
1228                 if (lineRange
.Contains(pos
)) 
1230                     // If the caret is displayed at the end of the previous wrapped line, 
1231                     // we want to return the line it's _displayed_ at (not the actual line 
1232                     // containing the position). 
1233                     if (lineRange
.GetStart() == pos 
&& !startOfLine 
&& child
->GetRange().GetStart() != pos
) 
1234                         return lineCount 
- 1; 
1241                 node2 
= node2
->GetNext(); 
1243             // If we didn't find it in the lines, it must be 
1244             // the last position of the paragraph. So return the last line. 
1248             lineCount 
+= child
->GetLines().GetCount(); 
1250         node 
= node
->GetNext(); 
1257 /// Given a line number, get the corresponding wxRichTextLine object. 
1258 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const 
1262     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1265         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1266         wxASSERT(child 
!= NULL
); 
1268         if (lineNumber 
< (int) (child
->GetLines().GetCount() + lineCount
)) 
1270             wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
1273                 wxRichTextLine
* line 
= node2
->GetData(); 
1275                 if (lineCount 
== lineNumber
) 
1280                 node2 
= node2
->GetNext(); 
1284             lineCount 
+= child
->GetLines().GetCount(); 
1286         node 
= node
->GetNext(); 
1293 /// Delete range from layout. 
1294 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
) 
1296     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1300         wxRichTextParagraph
* obj 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1301         wxASSERT (obj 
!= NULL
); 
1303         wxRichTextObjectList::compatibility_iterator next 
= node
->GetNext(); 
1305         // Delete the range in each paragraph 
1307         if (!obj
->GetRange().IsOutside(range
)) 
1309             // Deletes the content of this object within the given range 
1310             obj
->DeleteRange(range
); 
1312             // If the whole paragraph is within the range to delete, 
1313             // delete the whole thing. 
1314             if (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()) 
1316                 // Delete the whole object 
1317                 RemoveChild(obj
, true); 
1319             // If the range includes the paragraph end, we need to join this 
1320             // and the next paragraph. 
1321             else if (range
.Contains(obj
->GetRange().GetEnd())) 
1323                 // We need to move the objects from the next paragraph 
1324                 // to this paragraph 
1328                     wxRichTextParagraph
* nextParagraph 
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
); 
1329                     next 
= next
->GetNext(); 
1332                         // Delete the stuff we need to delete 
1333                         nextParagraph
->DeleteRange(range
); 
1335                         // Move the objects to the previous para 
1336                         wxRichTextObjectList::compatibility_iterator node1 
= nextParagraph
->GetChildren().GetFirst(); 
1340                             wxRichTextObject
* obj1 
= node1
->GetData(); 
1342                             // If the object is empty, optimise it out 
1343                             if (obj1
->IsEmpty()) 
1349                                 obj
->AppendChild(obj1
); 
1352                             wxRichTextObjectList::compatibility_iterator next1 
= node1
->GetNext(); 
1358                         // Delete the paragraph 
1359                         RemoveChild(nextParagraph
, true); 
1373 /// Get any text in this object for the given range 
1374 wxString 
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const 
1378     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1381         wxRichTextObject
* child 
= node
->GetData(); 
1382         if (!child
->GetRange().IsOutside(range
)) 
1386             wxRichTextRange childRange 
= range
; 
1387             childRange
.LimitTo(child
->GetRange()); 
1389             wxString childText 
= child
->GetTextForRange(childRange
); 
1395         node 
= node
->GetNext(); 
1401 /// Get all the text 
1402 wxString 
wxRichTextParagraphLayoutBox::GetText() const 
1404     return GetTextForRange(GetRange()); 
1407 /// Get the paragraph by number 
1408 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const 
1410     if ((size_t) paragraphNumber 
<= GetChildCount()) 
1413     return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
); 
1416 /// Get the length of the paragraph 
1417 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const 
1419     wxRichTextParagraph
* para 
= GetParagraphAtLine(paragraphNumber
); 
1421         return para
->GetRange().GetLength() - 1; // don't include newline 
1426 /// Get the text of the paragraph 
1427 wxString 
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const 
1429     wxRichTextParagraph
* para 
= GetParagraphAtLine(paragraphNumber
); 
1431         return para
->GetTextForRange(para
->GetRange()); 
1433         return wxEmptyString
; 
1436 /// Convert zero-based line column and paragraph number to a position. 
1437 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const 
1439     wxRichTextParagraph
* para 
= GetParagraphAtLine(y
); 
1442         return para
->GetRange().GetStart() + x
; 
1448 /// Convert zero-based position to line column and paragraph number 
1449 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const 
1451     wxRichTextParagraph
* para 
= GetParagraphAtPosition(pos
); 
1455         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1458             wxRichTextObject
* child 
= node
->GetData(); 
1462             node 
= node
->GetNext(); 
1466         *x 
= pos 
- para
->GetRange().GetStart(); 
1474 /// Get the leaf object in a paragraph at this position. 
1475 /// Given a line number, get the corresponding wxRichTextLine object. 
1476 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const 
1478     wxRichTextParagraph
* para 
= GetParagraphAtPosition(position
); 
1481         wxRichTextObjectList::compatibility_iterator node 
= para
->GetChildren().GetFirst(); 
1485             wxRichTextObject
* child 
= node
->GetData(); 
1486             if (child
->GetRange().Contains(position
)) 
1489             node 
= node
->GetNext(); 
1491         if (position 
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0) 
1492             return para
->GetChildren().GetLast()->GetData(); 
1497 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes 
1498 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, bool withUndo
) 
1500     bool characterStyle 
= false; 
1501     bool paragraphStyle 
= false; 
1503     if (style
.IsCharacterStyle()) 
1504         characterStyle 
= true; 
1505     if (style
.IsParagraphStyle()) 
1506         paragraphStyle 
= true; 
1508     // If we are associated with a control, make undoable; otherwise, apply immediately 
1511     bool haveControl 
= (GetRichTextCtrl() != NULL
); 
1513     wxRichTextAction
* action 
= NULL
; 
1515     if (haveControl 
&& withUndo
) 
1517         action 
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl()); 
1518         action
->SetRange(range
); 
1519         action
->SetPosition(GetRichTextCtrl()->GetCaretPosition()); 
1522     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1525         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1526         wxASSERT (para 
!= NULL
); 
1528         if (para 
&& para
->GetChildCount() > 0) 
1530             // Stop searching if we're beyond the range of interest 
1531             if (para
->GetRange().GetStart() > range
.GetEnd()) 
1534             if (!para
->GetRange().IsOutside(range
)) 
1536                 // We'll be using a copy of the paragraph to make style changes, 
1537                 // not updating the buffer directly. 
1538                 wxRichTextParagraph
* newPara 
wxDUMMY_INITIALIZE(NULL
); 
1540                 if (haveControl 
&& withUndo
) 
1542                     newPara 
= new wxRichTextParagraph(*para
); 
1543                     action
->GetNewParagraphs().AppendChild(newPara
); 
1545                     // Also store the old ones for Undo 
1546                     action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
)); 
1552                     wxRichTextApplyStyle(newPara
->GetAttributes(), style
); 
1554                 if (characterStyle 
&& range
.GetStart() != newPara
->GetRange().GetEnd()) 
1556                     wxRichTextRange 
childRange(range
); 
1557                     childRange
.LimitTo(newPara
->GetRange()); 
1559                     // Find the starting position and if necessary split it so 
1560                     // we can start applying a different style. 
1561                     // TODO: check that the style actually changes or is different 
1562                     // from style outside of range 
1563                     wxRichTextObject
* firstObject 
wxDUMMY_INITIALIZE(NULL
); 
1564                     wxRichTextObject
* lastObject 
wxDUMMY_INITIALIZE(NULL
); 
1566                     if (childRange
.GetStart() == newPara
->GetRange().GetStart()) 
1567                         firstObject 
= newPara
->GetChildren().GetFirst()->GetData(); 
1569                         firstObject 
= newPara
->SplitAt(range
.GetStart()); 
1571                     // Increment by 1 because we're apply the style one _after_ the split point 
1572                     long splitPoint 
= childRange
.GetEnd(); 
1573                     if (splitPoint 
!= newPara
->GetRange().GetEnd()) 
1577                     if (splitPoint 
== newPara
->GetRange().GetEnd() || splitPoint 
== (newPara
->GetRange().GetEnd() - 1)) 
1578                         lastObject 
= newPara
->GetChildren().GetLast()->GetData(); 
1580                         // lastObject is set as a side-effect of splitting. It's 
1581                         // returned as the object before the new object. 
1582                         (void) newPara
->SplitAt(splitPoint
, & lastObject
); 
1584                     wxASSERT(firstObject 
!= NULL
); 
1585                     wxASSERT(lastObject 
!= NULL
); 
1587                     if (!firstObject 
|| !lastObject
) 
1590                     wxRichTextObjectList::compatibility_iterator firstNode 
= newPara
->GetChildren().Find(firstObject
); 
1591                     wxRichTextObjectList::compatibility_iterator lastNode 
= newPara
->GetChildren().Find(lastObject
); 
1593                     wxASSERT(firstNode 
!= NULL
); 
1594                     wxASSERT(lastNode 
!= NULL
); 
1596                     wxRichTextObjectList::compatibility_iterator node2 
= firstNode
; 
1600                         wxRichTextObject
* child 
= node2
->GetData(); 
1602                         wxRichTextApplyStyle(child
->GetAttributes(), style
); 
1603                         if (node2 
== lastNode
) 
1606                         node2 
= node2
->GetNext(); 
1612         node 
= node
->GetNext(); 
1615     // Do action, or delay it until end of batch. 
1616     if (haveControl 
&& withUndo
) 
1617         GetRichTextCtrl()->GetBuffer().SubmitAction(action
); 
1622 /// Set text attributes 
1623 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxTextAttrEx
& style
, bool withUndo
) 
1625     wxRichTextAttr richStyle 
= style
; 
1626     return SetStyle(range
, richStyle
, withUndo
); 
1629 /// Get the text attributes for this position. 
1630 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxTextAttrEx
& style
) const 
1632     wxRichTextObject
* obj 
wxDUMMY_INITIALIZE(NULL
); 
1634     if (style
.IsParagraphStyle()) 
1635         obj 
= GetParagraphAtPosition(position
); 
1637         obj 
= GetLeafObjectAtPosition(position
); 
1641         style 
= obj
->GetAttributes(); 
1648 /// Get the text attributes for this position. 
1649 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
) const 
1651     wxRichTextObject
* obj 
wxDUMMY_INITIALIZE(NULL
); 
1653     if (style
.IsParagraphStyle()) 
1654         obj 
= GetParagraphAtPosition(position
); 
1656         obj 
= GetLeafObjectAtPosition(position
); 
1660         style 
= obj
->GetAttributes(); 
1667 /// Set default style 
1668 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttrEx
& style
) 
1670     m_defaultAttributes 
= style
; 
1675 /// Test if this whole range has character attributes of the specified kind. If any 
1676 /// of the attributes are different within the range, the test fails. You 
1677 /// can use this to implement, for example, bold button updating. style must have 
1678 /// flags indicating which attributes are of interest. 
1679 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const 
1682     int matchingCount 
= 0; 
1684     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1687         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1688         wxASSERT (para 
!= NULL
); 
1692             // Stop searching if we're beyond the range of interest 
1693             if (para
->GetRange().GetStart() > range
.GetEnd()) 
1694                 return foundCount 
== matchingCount
; 
1696             if (!para
->GetRange().IsOutside(range
)) 
1698                 wxRichTextObjectList::compatibility_iterator node2 
= para
->GetChildren().GetFirst(); 
1702                     wxRichTextObject
* child 
= node2
->GetData(); 
1703                     if (!child
->GetRange().IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
))) 
1706                         if (wxTextAttrEqPartial(child
->GetAttributes(), style
, style
.GetFlags())) 
1710                     node2 
= node2
->GetNext(); 
1715         node 
= node
->GetNext(); 
1718     return foundCount 
== matchingCount
; 
1721 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxTextAttrEx
& style
) const 
1723     wxRichTextAttr richStyle 
= style
; 
1724     return HasCharacterAttributes(range
, richStyle
); 
1727 /// Test if this whole range has paragraph attributes of the specified kind. If any 
1728 /// of the attributes are different within the range, the test fails. You 
1729 /// can use this to implement, for example, centering button updating. style must have 
1730 /// flags indicating which attributes are of interest. 
1731 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const 
1734     int matchingCount 
= 0; 
1736     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1739         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1740         wxASSERT (para 
!= NULL
); 
1744             // Stop searching if we're beyond the range of interest 
1745             if (para
->GetRange().GetStart() > range
.GetEnd()) 
1746                 return foundCount 
== matchingCount
; 
1748             if (!para
->GetRange().IsOutside(range
)) 
1751                 if (wxTextAttrEqPartial(para
->GetAttributes(), style
, style
.GetFlags())) 
1756         node 
= node
->GetNext(); 
1758     return foundCount 
== matchingCount
; 
1761 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxTextAttrEx
& style
) const 
1763     wxRichTextAttr richStyle 
= style
; 
1764     return HasParagraphAttributes(range
, richStyle
); 
1767 void wxRichTextParagraphLayoutBox::Clear() 
1772 void wxRichTextParagraphLayoutBox::Reset() 
1776     AddParagraph(wxEmptyString
); 
1779 /// Invalidate the buffer. With no argument, invalidates whole buffer. 
1780 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
) 
1784     if (invalidRange 
== wxRICHTEXT_ALL
) 
1786         m_invalidRange 
= wxRICHTEXT_ALL
; 
1790     // Already invalidating everything 
1791     if (m_invalidRange 
== wxRICHTEXT_ALL
) 
1794     if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1) 
1795         m_invalidRange
.SetStart(invalidRange
.GetStart()); 
1796     if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd()) 
1797         m_invalidRange
.SetEnd(invalidRange
.GetEnd()); 
1800 /// Get invalid range, rounding to entire paragraphs if argument is true. 
1801 wxRichTextRange 
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const 
1803     if (m_invalidRange 
== wxRICHTEXT_ALL 
|| m_invalidRange 
== wxRICHTEXT_NONE
) 
1804         return m_invalidRange
; 
1806     wxRichTextRange range 
= m_invalidRange
; 
1808     if (wholeParagraphs
) 
1810         wxRichTextParagraph
* para1 
= GetParagraphAtPosition(range
.GetStart()); 
1811         wxRichTextParagraph
* para2 
= GetParagraphAtPosition(range
.GetEnd()); 
1813             range
.SetStart(para1
->GetRange().GetStart()); 
1815             range
.SetEnd(para2
->GetRange().GetEnd()); 
1821  * wxRichTextFragment class declaration 
1822  * This is a lind of paragraph layout box used for storing 
1823  * paragraphs for Undo/Redo, for example. 
1826 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFragment
, wxRichTextParagraphLayoutBox
) 
1829 void wxRichTextFragment::Init() 
1831     m_partialParagraph 
= false; 
1835 void wxRichTextFragment::Copy(const wxRichTextFragment
& obj
) 
1837     wxRichTextParagraphLayoutBox::Copy(obj
); 
1839     m_partialParagraph 
= obj
.m_partialParagraph
; 
1843  * wxRichTextParagraph 
1844  * This object represents a single paragraph (or in a straight text editor, a line). 
1847 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextBox
) 
1849 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxTextAttrEx
* style
): 
1850     wxRichTextBox(parent
) 
1852     if (parent 
&& !style
) 
1853         SetAttributes(parent
->GetAttributes()); 
1855         SetAttributes(*style
); 
1858 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxTextAttrEx
* style
): 
1859     wxRichTextBox(parent
) 
1861     if (parent 
&& !style
) 
1862         SetAttributes(parent
->GetAttributes()); 
1864         SetAttributes(*style
); 
1866     AppendChild(new wxRichTextPlainText(text
, this)); 
1869 wxRichTextParagraph::~wxRichTextParagraph() 
1875 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextRange
& selectionRange
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(descent
), int style
) 
1877     // Draw the bullet, if any 
1878     if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
) 
1880         if (GetAttributes().GetLeftSubIndent() != 0) 
1882             int spaceBeforePara 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetParagraphSpacingBefore()); 
1883             // int spaceAfterPara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingAfter()); 
1884             int leftIndent 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetLeftIndent()); 
1885             // int leftSubIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftSubIndent()); 
1886             // int rightIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetRightIndent()); 
1888             if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
) 
1894                 wxString bulletText 
= GetBulletText(); 
1895                 if (!bulletText
.empty()) 
1897                     if (GetAttributes().GetFont().Ok()) 
1898                         dc
.SetFont(GetAttributes().GetFont()); 
1900                     if (GetAttributes().GetTextColour().Ok()) 
1901                         dc
.SetTextForeground(GetAttributes().GetTextColour()); 
1903                     dc
.SetBackgroundMode(wxTRANSPARENT
); 
1905                     // Get line height from first line, if any 
1906                     wxRichTextLine
* line 
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : (wxRichTextLine
*) NULL
; 
1909                     int lineHeight 
wxDUMMY_INITIALIZE(0); 
1912                         lineHeight 
= line
->GetSize().y
; 
1913                         linePos 
= line
->GetPosition() + GetPosition(); 
1917                         lineHeight 
= dc
.GetCharHeight(); 
1918                         linePos 
= GetPosition(); 
1919                         linePos
.y 
+= spaceBeforePara
; 
1922                     int charHeight 
= dc
.GetCharHeight(); 
1924                     int x 
= GetPosition().x 
+ leftIndent
; 
1925                     int y 
= linePos
.y 
+ (lineHeight 
- charHeight
); 
1927                     dc
.DrawText(bulletText
, x
, y
); 
1933     // Draw the range for each line, one object at a time. 
1935     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
1938         wxRichTextLine
* line 
= node
->GetData(); 
1939         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
1941         int maxDescent 
= line
->GetDescent(); 
1943         // Lines are specified relative to the paragraph 
1945         wxPoint linePosition 
= line
->GetPosition() + GetPosition(); 
1946         wxPoint objectPosition 
= linePosition
; 
1948         // Loop through objects until we get to the one within range 
1949         wxRichTextObjectList::compatibility_iterator node2 
= m_children
.GetFirst(); 
1952             wxRichTextObject
* child 
= node2
->GetData(); 
1953             if (!child
->GetRange().IsOutside(lineRange
)) 
1955                 // Draw this part of the line at the correct position 
1956                 wxRichTextRange 
objectRange(child
->GetRange()); 
1957                 objectRange
.LimitTo(lineRange
); 
1961                 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
1963                 // Use the child object's width, but the whole line's height 
1964                 wxRect 
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
)); 
1965                 child
->Draw(dc
, objectRange
, selectionRange
, childRect
, maxDescent
, style
); 
1967                 objectPosition
.x 
+= objectSize
.x
; 
1969             else if (child
->GetRange().GetStart() > lineRange
.GetEnd()) 
1970                 // Can break out of inner loop now since we've passed this line's range 
1973             node2 
= node2
->GetNext(); 
1976         node 
= node
->GetNext(); 
1982 /// Lay the item out 
1983 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
) 
1987     // Increase the size of the paragraph due to spacing 
1988     int spaceBeforePara 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetParagraphSpacingBefore()); 
1989     int spaceAfterPara 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetParagraphSpacingAfter()); 
1990     int leftIndent 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetLeftIndent()); 
1991     int leftSubIndent 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetLeftSubIndent()); 
1992     int rightIndent 
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetRightIndent()); 
1994     int lineSpacing 
= 0; 
1996     // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc. 
1997     if (GetAttributes().GetLineSpacing() > 10 && GetAttributes().GetFont().Ok()) 
1999         dc
.SetFont(GetAttributes().GetFont()); 
2000         lineSpacing 
= (ConvertTenthsMMToPixels(dc
, dc
.GetCharHeight()) * GetAttributes().GetLineSpacing())/10; 
2003     // Available space for text on each line differs. 
2004     int availableTextSpaceFirstLine 
= rect
.GetWidth() - leftIndent 
- rightIndent
; 
2006     // Bullets start the text at the same position as subsequent lines 
2007     if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
) 
2008         availableTextSpaceFirstLine 
-= leftSubIndent
; 
2010     int availableTextSpaceSubsequentLines 
= rect
.GetWidth() - leftIndent 
- rightIndent 
- leftSubIndent
; 
2012     // Start position for each line relative to the paragraph 
2013     int startPositionFirstLine 
= leftIndent
; 
2014     int startPositionSubsequentLines 
= leftIndent 
+ leftSubIndent
; 
2016     // If we have a bullet in this paragraph, the start position for the first line's text 
2017     // is actually leftIndent + leftSubIndent. 
2018     if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
) 
2019         startPositionFirstLine 
= startPositionSubsequentLines
; 
2021     //bool restrictWidth = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_WIDTH); 
2022     //bool restrictHeight = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_HEIGHT); 
2024     long lastEndPos 
= GetRange().GetStart()-1; 
2025     long lastCompletedEndPos 
= lastEndPos
; 
2027     int currentWidth 
= 0; 
2028     SetPosition(rect
.GetPosition()); 
2030     wxPoint 
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph 
2039     // We may need to go back to a previous child, in which case create the new line, 
2040     // find the child corresponding to the start position of the string, and 
2043     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2046         wxRichTextObject
* child 
= node
->GetData(); 
2048         // If this is e.g. a composite text box, it will need to be laid out itself. 
2049         // But if just a text fragment or image, for example, this will 
2050         // do nothing. NB: won't we need to set the position after layout? 
2051         // since for example if position is dependent on vertical line size, we 
2052         // can't tell the position until the size is determined. So possibly introduce 
2053         // another layout phase. 
2055         child
->Layout(dc
, rect
, style
); 
2057         // Available width depends on whether we're on the first or subsequent lines 
2058         int availableSpaceForText 
= (lineCount 
== 0 ? availableTextSpaceFirstLine 
: availableTextSpaceSubsequentLines
); 
2060         currentPosition
.x 
= (lineCount 
== 0 ? startPositionFirstLine 
: startPositionSubsequentLines
); 
2062         // We may only be looking at part of a child, if we searched back for wrapping 
2063         // and found a suitable point some way into the child. So get the size for the fragment 
2067         int childDescent 
= 0; 
2068         if (lastEndPos 
== child
->GetRange().GetStart() - 1) 
2070             childSize 
= child
->GetCachedSize(); 
2071             childDescent 
= child
->GetDescent(); 
2074             GetRangeSize(wxRichTextRange(lastEndPos
+1, child
->GetRange().GetEnd()), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
); 
2076         if (childSize
.x 
+ currentWidth 
> availableSpaceForText
) 
2078             long wrapPosition 
= 0; 
2080             // Find a place to wrap. This may walk back to previous children, 
2081             // for example if a word spans several objects. 
2082             if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableSpaceForText
, wrapPosition
)) 
2084                 // If the function failed, just cut it off at the end of this child. 
2085                 wrapPosition 
= child
->GetRange().GetEnd(); 
2088             // FindWrapPosition can still return a value that will put us in an endless wrapping loop 
2089             if (wrapPosition 
<= lastCompletedEndPos
) 
2090                 wrapPosition 
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd()); 
2092             // wxLogDebug(wxT("Split at %ld"), wrapPosition); 
2094             // Let's find the actual size of the current line now 
2096             wxRichTextRange 
actualRange(lastCompletedEndPos
+1, wrapPosition
); 
2097             GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
); 
2098             currentWidth 
= actualSize
.x
; 
2099             lineHeight 
= wxMax(lineHeight
, actualSize
.y
); 
2100             maxDescent 
= wxMax(childDescent
, maxDescent
); 
2103             wxRichTextLine
* line 
= AllocateLine(lineCount
); 
2105             // Set relative range so we won't have to change line ranges when paragraphs are moved 
2106             line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart())); 
2107             line
->SetPosition(currentPosition
); 
2108             line
->SetSize(wxSize(currentWidth
, lineHeight
)); 
2109             line
->SetDescent(maxDescent
); 
2111             // Now move down a line. TODO: add margins, spacing 
2112             currentPosition
.y 
+= lineHeight
; 
2113             currentPosition
.y 
+= lineSpacing
; 
2116             maxWidth 
= wxMax(maxWidth
, currentWidth
); 
2120             // TODO: account for zero-length objects, such as fields 
2121             wxASSERT(wrapPosition 
> lastCompletedEndPos
); 
2123             lastEndPos 
= wrapPosition
; 
2124             lastCompletedEndPos 
= lastEndPos
; 
2128             // May need to set the node back to a previous one, due to searching back in wrapping 
2129             wxRichTextObject
* childAfterWrapPosition 
= FindObjectAtPosition(wrapPosition
+1); 
2130             if (childAfterWrapPosition
) 
2131                 node 
= m_children
.Find(childAfterWrapPosition
); 
2133                 node 
= node
->GetNext(); 
2137             // We still fit, so don't add a line, and keep going 
2138             currentWidth 
+= childSize
.x
; 
2139             lineHeight 
= wxMax(lineHeight
, childSize
.y
); 
2140             maxDescent 
= wxMax(childDescent
, maxDescent
); 
2142             maxWidth 
= wxMax(maxWidth
, currentWidth
); 
2143             lastEndPos 
= child
->GetRange().GetEnd(); 
2145             node 
= node
->GetNext(); 
2149     // Add the last line - it's the current pos -> last para pos 
2150     // Substract -1 because the last position is always the end-paragraph position. 
2151     if (lastCompletedEndPos 
<= GetRange().GetEnd()-1) 
2153         currentPosition
.x 
= (lineCount 
== 0 ? startPositionFirstLine 
: startPositionSubsequentLines
); 
2155         wxRichTextLine
* line 
= AllocateLine(lineCount
); 
2157         wxRichTextRange 
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1); 
2159         // Set relative range so we won't have to change line ranges when paragraphs are moved 
2160         line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart())); 
2162         line
->SetPosition(currentPosition
); 
2164         if (lineHeight 
== 0) 
2166             if (GetAttributes().GetFont().Ok()) 
2167                 dc
.SetFont(GetAttributes().GetFont()); 
2168             lineHeight 
= dc
.GetCharHeight(); 
2170         if (maxDescent 
== 0) 
2173             dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
); 
2176         line
->SetSize(wxSize(currentWidth
, lineHeight
)); 
2177         line
->SetDescent(maxDescent
); 
2178         currentPosition
.y 
+= lineHeight
; 
2179         currentPosition
.y 
+= lineSpacing
; 
2183     // Remove remaining unused line objects, if any 
2184     ClearUnusedLines(lineCount
); 
2186     // Apply styles to wrapped lines 
2187     ApplyParagraphStyle(rect
); 
2189     SetCachedSize(wxSize(maxWidth
, currentPosition
.y 
+ spaceBeforePara 
+ spaceAfterPara
)); 
2196 /// Apply paragraph styles, such as centering, to wrapped lines 
2197 void wxRichTextParagraph::ApplyParagraphStyle(const wxRect
& rect
) 
2199     if (!GetAttributes().HasAlignment()) 
2202     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
2205         wxRichTextLine
* line 
= node
->GetData(); 
2207         wxPoint pos 
= line
->GetPosition(); 
2208         wxSize size 
= line
->GetSize(); 
2210         // centering, right-justification 
2211         if (GetAttributes().HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
) 
2213             pos
.x 
= (rect
.GetWidth() - size
.x
)/2 + pos
.x
; 
2214             line
->SetPosition(pos
); 
2216         else if (GetAttributes().HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
) 
2218             pos
.x 
= rect
.GetRight() - size
.x
; 
2219             line
->SetPosition(pos
); 
2222         node 
= node
->GetNext(); 
2226 /// Insert text at the given position 
2227 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
) 
2229     wxRichTextObject
* childToUse 
= NULL
; 
2230     wxRichTextObjectList::compatibility_iterator nodeToUse 
= NULL
; 
2232     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2235         wxRichTextObject
* child 
= node
->GetData(); 
2236         if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0) 
2243         node 
= node
->GetNext(); 
2248         wxRichTextPlainText
* textObject 
= wxDynamicCast(childToUse
, wxRichTextPlainText
); 
2251             int posInString 
= pos 
- textObject
->GetRange().GetStart(); 
2253             wxString newText 
= textObject
->GetText().Mid(0, posInString
) + 
2254                                text 
+ textObject
->GetText().Mid(posInString
); 
2255             textObject
->SetText(newText
); 
2257             int textLength 
= text
.Length(); 
2259             textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(), 
2260                                                  textObject
->GetRange().GetEnd() + textLength
)); 
2262             // Increment the end range of subsequent fragments in this paragraph. 
2263             // We'll set the paragraph range itself at a higher level. 
2265             wxRichTextObjectList::compatibility_iterator node 
= nodeToUse
->GetNext(); 
2268                 wxRichTextObject
* child 
= node
->GetData(); 
2269                 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
, 
2270                                                  textObject
->GetRange().GetEnd() + textLength
)); 
2272                 node 
= node
->GetNext(); 
2279             // TODO: if not a text object, insert at closest position, e.g. in front of it 
2285         // Don't pass parent initially to suppress auto-setting of parent range. 
2286         // We'll do that at a higher level. 
2287         wxRichTextPlainText
* textObject 
= new wxRichTextPlainText(text
, this); 
2289         AppendChild(textObject
); 
2296 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
) 
2298     wxRichTextBox::Copy(obj
); 
2301 /// Clear the cached lines 
2302 void wxRichTextParagraph::ClearLines() 
2304     WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
); 
2307 /// Get/set the object size for the given range. Returns false if the range 
2308 /// is invalid for this object. 
2309 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
) const 
2311     if (!range
.IsWithin(GetRange())) 
2314     if (flags 
& wxRICHTEXT_UNFORMATTED
) 
2316         // Just use unformatted data, assume no line breaks 
2317         // TODO: take into account line breaks 
2321         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2324             wxRichTextObject
* child 
= node
->GetData(); 
2325             if (!child
->GetRange().IsOutside(range
)) 
2329                 wxRichTextRange rangeToUse 
= range
; 
2330                 rangeToUse
.LimitTo(child
->GetRange()); 
2331                 int childDescent 
= 0; 
2333                 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
)) 
2335                     sz
.y 
= wxMax(sz
.y
, childSize
.y
); 
2336                     sz
.x 
+= childSize
.x
; 
2337                     descent 
= wxMax(descent
, childDescent
); 
2341             node 
= node
->GetNext(); 
2347         // Use formatted data, with line breaks 
2350         // We're going to loop through each line, and then for each line, 
2351         // call GetRangeSize for the fragment that comprises that line. 
2352         // Only we have to do that multiple times within the line, because 
2353         // the line may be broken into pieces. For now ignore line break commands 
2354         // (so we can assume that getting the unformatted size for a fragment 
2355         // within a line is the actual size) 
2357         wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
2360             wxRichTextLine
* line 
= node
->GetData(); 
2361             wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
2362             if (!lineRange
.IsOutside(range
)) 
2366                 wxRichTextObjectList::compatibility_iterator node2 
= m_children
.GetFirst(); 
2369                     wxRichTextObject
* child 
= node2
->GetData(); 
2371                     if (!child
->GetRange().IsOutside(lineRange
)) 
2373                         wxRichTextRange rangeToUse 
= lineRange
; 
2374                         rangeToUse
.LimitTo(child
->GetRange()); 
2377                         int childDescent 
= 0; 
2378                         if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
)) 
2380                             lineSize
.y 
= wxMax(lineSize
.y
, childSize
.y
); 
2381                             lineSize
.x 
+= childSize
.x
; 
2383                         descent 
= wxMax(descent
, childDescent
); 
2386                     node2 
= node2
->GetNext(); 
2389                 // Increase size by a line (TODO: paragraph spacing) 
2391                 sz
.x 
= wxMax(sz
.x
, lineSize
.x
); 
2393             node 
= node
->GetNext(); 
2400 /// Finds the absolute position and row height for the given character position 
2401 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
) 
2405         wxRichTextLine
* line 
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0); 
2407             *height 
= line
->GetSize().y
; 
2409             *height 
= dc
.GetCharHeight(); 
2411         // -1 means 'the start of the buffer'. 
2414             pt 
= pt 
+ line
->GetPosition(); 
2416         *height 
= dc
.GetCharHeight(); 
2421     // The final position in a paragraph is taken to mean the position 
2422     // at the start of the next paragraph. 
2423     if (index 
== GetRange().GetEnd()) 
2425         wxRichTextParagraphLayoutBox
* parent 
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
); 
2426         wxASSERT( parent 
!= NULL 
); 
2428         // Find the height at the next paragraph, if any 
2429         wxRichTextLine
* line 
= parent
->GetLineAtPosition(index 
+ 1); 
2432             *height 
= line
->GetSize().y
; 
2433             pt 
= line
->GetAbsolutePosition(); 
2437             *height 
= dc
.GetCharHeight(); 
2438             int indent 
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent()); 
2439             pt 
= wxPoint(indent
, GetCachedSize().y
); 
2445     if (index 
< GetRange().GetStart() || index 
> GetRange().GetEnd()) 
2448     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
2451         wxRichTextLine
* line 
= node
->GetData(); 
2452         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
2453         if (index 
>= lineRange
.GetStart() && index 
<= lineRange
.GetEnd()) 
2455             // If this is the last point in the line, and we're forcing the 
2456             // returned value to be the start of the next line, do the required 
2458             if (index 
== lineRange
.GetEnd() && forceLineStart
) 
2460                 if (node
->GetNext()) 
2462                     wxRichTextLine
* nextLine 
= node
->GetNext()->GetData(); 
2463                     *height 
= nextLine
->GetSize().y
; 
2464                     pt 
= nextLine
->GetAbsolutePosition(); 
2469             pt
.y 
= line
->GetPosition().y 
+ GetPosition().y
; 
2471             wxRichTextRange 
r(lineRange
.GetStart(), index
); 
2475             // We find the size of the line up to this point, 
2476             // then we can add this size to the line start position and 
2477             // paragraph start position to find the actual position. 
2479             if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
)) 
2481                 pt
.x 
= line
->GetPosition().x 
+ GetPosition().x 
+ rangeSize
.x
; 
2482                 *height 
= line
->GetSize().y
; 
2489         node 
= node
->GetNext(); 
2495 /// Hit-testing: returns a flag indicating hit test details, plus 
2496 /// information about position 
2497 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
) 
2499     wxPoint paraPos 
= GetPosition(); 
2501     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
2504         wxRichTextLine
* line 
= node
->GetData(); 
2505         wxPoint linePos 
= paraPos 
+ line
->GetPosition(); 
2506         wxSize lineSize 
= line
->GetSize(); 
2507         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
2509         if (pt
.y 
>= linePos
.y 
&& pt
.y 
<= linePos
.y 
+ lineSize
.y
) 
2511             if (pt
.x 
< linePos
.x
) 
2513                 textPosition 
= lineRange
.GetStart(); 
2514                 return wxRICHTEXT_HITTEST_BEFORE
; 
2516             else if (pt
.x 
>= (linePos
.x 
+ lineSize
.x
)) 
2518                 textPosition 
= lineRange
.GetEnd(); 
2519                 return wxRICHTEXT_HITTEST_AFTER
; 
2524                 int lastX 
= linePos
.x
; 
2525                 for (i 
= lineRange
.GetStart(); i 
<= lineRange
.GetEnd(); i
++) 
2530                     wxRichTextRange 
rangeToUse(lineRange
.GetStart(), i
); 
2532                     GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
2534                     int nextX 
= childSize
.x 
+ linePos
.x
; 
2536                     if (pt
.x 
>= lastX 
&& pt
.x 
<= nextX
) 
2540                         // So now we know it's between i-1 and i. 
2541                         // Let's see if we can be more precise about 
2542                         // which side of the position it's on. 
2544                         int midPoint 
= (nextX 
- lastX
)/2 + lastX
; 
2545                         if (pt
.x 
>= midPoint
) 
2546                             return wxRICHTEXT_HITTEST_AFTER
; 
2548                             return wxRICHTEXT_HITTEST_BEFORE
; 
2558         node 
= node
->GetNext(); 
2561     return wxRICHTEXT_HITTEST_NONE
; 
2564 /// Split an object at this position if necessary, and return 
2565 /// the previous object, or NULL if inserting at beginning. 
2566 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
) 
2568     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2571         wxRichTextObject
* child 
= node
->GetData(); 
2573         if (pos 
== child
->GetRange().GetStart()) 
2577                 if (node
->GetPrevious()) 
2578                     *previousObject 
= node
->GetPrevious()->GetData(); 
2580                     *previousObject 
= NULL
; 
2586         if (child
->GetRange().Contains(pos
)) 
2588             // This should create a new object, transferring part of 
2589             // the content to the old object and the rest to the new object. 
2590             wxRichTextObject
* newObject 
= child
->DoSplit(pos
); 
2592             // If we couldn't split this object, just insert in front of it. 
2595                 // Maybe this is an empty string, try the next one 
2600                 // Insert the new object after 'child' 
2601                 if (node
->GetNext()) 
2602                     m_children
.Insert(node
->GetNext(), newObject
); 
2604                     m_children
.Append(newObject
); 
2605                 newObject
->SetParent(this); 
2608                     *previousObject 
= child
; 
2614         node 
= node
->GetNext(); 
2617         *previousObject 
= NULL
; 
2621 /// Move content to a list from obj on 
2622 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
) 
2624     wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(obj
); 
2627         wxRichTextObject
* child 
= node
->GetData(); 
2630         wxRichTextObjectList::compatibility_iterator oldNode 
= node
; 
2632         node 
= node
->GetNext(); 
2634         m_children
.DeleteNode(oldNode
); 
2638 /// Add content back from list 
2639 void wxRichTextParagraph::MoveFromList(wxList
& list
) 
2641     for (wxNode
* node 
= list
.GetFirst(); node
; node 
= node
->GetNext()) 
2643         AppendChild((wxRichTextObject
*) node
->GetData()); 
2648 void wxRichTextParagraph::CalculateRange(long start
, long& end
) 
2650     wxRichTextCompositeObject::CalculateRange(start
, end
); 
2652     // Add one for end of paragraph 
2655     m_range
.SetRange(start
, end
); 
2658 /// Find the object at the given position 
2659 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
) 
2661     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2664         wxRichTextObject
* obj 
= node
->GetData(); 
2665         if (obj
->GetRange().Contains(position
)) 
2668         node 
= node
->GetNext(); 
2673 /// Get the plain text searching from the start or end of the range. 
2674 /// The resulting string may be shorter than the range given. 
2675 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
) 
2677     text 
= wxEmptyString
; 
2681         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2684             wxRichTextObject
* obj 
= node
->GetData(); 
2685             if (!obj
->GetRange().IsOutside(range
)) 
2687                 wxRichTextPlainText
* textObj 
= wxDynamicCast(obj
, wxRichTextPlainText
); 
2690                     text 
+= textObj
->GetTextForRange(range
); 
2696             node 
= node
->GetNext(); 
2701         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetLast(); 
2704             wxRichTextObject
* obj 
= node
->GetData(); 
2705             if (!obj
->GetRange().IsOutside(range
)) 
2707                 wxRichTextPlainText
* textObj 
= wxDynamicCast(obj
, wxRichTextPlainText
); 
2710                     text 
= textObj
->GetTextForRange(range
) + text
; 
2716             node 
= node
->GetPrevious(); 
2723 /// Find a suitable wrap position. 
2724 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
) 
2726     // Find the first position where the line exceeds the available space. 
2729     long breakPosition 
= range
.GetEnd(); 
2730     for (i 
= range
.GetStart(); i 
<= range
.GetEnd(); i
++) 
2733         GetRangeSize(wxRichTextRange(range
.GetStart(), i
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
2735         if (sz
.x 
> availableSpace
) 
2737             breakPosition 
= i
-1; 
2742     // Now we know the last position on the line. 
2743     // Let's try to find a word break. 
2746     if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false)) 
2748         int spacePos 
= plainText
.Find(wxT(' '), true); 
2749         if (spacePos 
!= wxNOT_FOUND
) 
2751             int positionsFromEndOfString 
= plainText
.Length() - spacePos 
- 1; 
2752             breakPosition 
= breakPosition 
- positionsFromEndOfString
; 
2756     wrapPosition 
= breakPosition
; 
2761 /// Get the bullet text for this paragraph. 
2762 wxString 
wxRichTextParagraph::GetBulletText() 
2764     if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE 
|| 
2765         (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)) 
2766         return wxEmptyString
; 
2768     int number 
= GetAttributes().GetBulletNumber(); 
2771     if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) 
2773         text
.Printf(wxT("%d"), number
); 
2775     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
) 
2777         // TODO: Unicode, and also check if number > 26 
2778         text
.Printf(wxT("%c"), (wxChar
) (number
+64)); 
2780     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
) 
2782         // TODO: Unicode, and also check if number > 26 
2783         text
.Printf(wxT("%c"), (wxChar
) (number
+96)); 
2785     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
) 
2787         // TODO: convert from number to roman numeral 
2790         else if (number 
== 2) 
2792         else if (number 
== 3) 
2794         else if (number 
== 4) 
2799     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
) 
2801         // TODO: convert from number to roman numeral 
2804         else if (number 
== 2) 
2806         else if (number 
== 3) 
2808         else if (number 
== 4) 
2813     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) 
2815         text 
= GetAttributes().GetBulletSymbol(); 
2818     if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
) 
2820         text 
= wxT("(") + text 
+ wxT(")"); 
2822     if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
) 
2830 /// Allocate or reuse a line object 
2831 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
) 
2833     if (pos 
< (int) m_cachedLines
.GetCount()) 
2835         wxRichTextLine
* line 
= m_cachedLines
.Item(pos
)->GetData(); 
2841         wxRichTextLine
* line 
= new wxRichTextLine(this); 
2842         m_cachedLines
.Append(line
); 
2847 /// Clear remaining unused line objects, if any 
2848 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
) 
2850     int cachedLineCount 
= m_cachedLines
.GetCount(); 
2851     if ((int) cachedLineCount 
> lineCount
) 
2853         for (int i 
= 0; i 
< (int) (cachedLineCount 
- lineCount
); i 
++) 
2855             wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetLast(); 
2856             wxRichTextLine
* line 
= node
->GetData(); 
2857             m_cachedLines
.Erase(node
); 
2867  * This object represents a line in a paragraph, and stores 
2868  * offsets from the start of the paragraph representing the 
2869  * start and end positions of the line. 
2872 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
) 
2878 void wxRichTextLine::Init(wxRichTextParagraph
* parent
) 
2881     m_range
.SetRange(-1, -1); 
2882     m_pos 
= wxPoint(0, 0); 
2883     m_size 
= wxSize(0, 0); 
2888 void wxRichTextLine::Copy(const wxRichTextLine
& obj
) 
2890     m_range 
= obj
.m_range
; 
2893 /// Get the absolute object position 
2894 wxPoint 
wxRichTextLine::GetAbsolutePosition() const 
2896     return m_parent
->GetPosition() + m_pos
; 
2899 /// Get the absolute range 
2900 wxRichTextRange 
wxRichTextLine::GetAbsoluteRange() const 
2902     wxRichTextRange 
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0); 
2903     range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1); 
2908  * wxRichTextPlainText 
2909  * This object represents a single piece of text. 
2912 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
) 
2914 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxTextAttrEx
* style
): 
2915     wxRichTextObject(parent
) 
2917     if (parent 
&& !style
) 
2918         SetAttributes(parent
->GetAttributes()); 
2920         SetAttributes(*style
); 
2926 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int WXUNUSED(style
)) 
2928     int offset 
= GetRange().GetStart(); 
2930     long len 
= range
.GetLength(); 
2931     wxString stringChunk 
= m_text
.Mid(range
.GetStart() - offset
, (size_t) len
); 
2933     int charHeight 
= dc
.GetCharHeight(); 
2936     int y 
= rect
.y 
+ (rect
.height 
- charHeight 
- (descent 
- m_descent
)); 
2938     // Test for the optimized situations where all is selected, or none 
2941     if (GetAttributes().GetFont().Ok()) 
2942         dc
.SetFont(GetAttributes().GetFont()); 
2944     // (a) All selected. 
2945     if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd()) 
2947         // Draw all selected 
2948         dc
.SetBrush(*wxBLACK_BRUSH
); 
2949         dc
.SetPen(*wxBLACK_PEN
); 
2951         dc
.GetTextExtent(stringChunk
, & w
, & h
); 
2952         wxRect 
selRect(x
, rect
.y
, w
, rect
.GetHeight()); 
2953         dc
.DrawRectangle(selRect
); 
2954         dc
.SetTextForeground(*wxWHITE
); 
2955         dc
.SetBackgroundMode(wxTRANSPARENT
); 
2956         dc
.DrawText(stringChunk
, x
, y
); 
2958     // (b) None selected. 
2959     else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd()) 
2961         // Draw all unselected 
2962         dc
.SetTextForeground(GetAttributes().GetTextColour()); 
2963         dc
.SetBackgroundMode(wxTRANSPARENT
); 
2964         dc
.DrawText(stringChunk
, x
, y
); 
2968         // (c) Part selected, part not 
2969         // Let's draw unselected chunk, selected chunk, then unselected chunk. 
2971         dc
.SetBackgroundMode(wxTRANSPARENT
); 
2973         // 1. Initial unselected chunk, if any, up until start of selection. 
2974         if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd()) 
2976             int r1 
= range
.GetStart(); 
2977             int s1 
= selectionRange
.GetStart()-1; 
2978             int fragmentLen 
= s1 
- r1 
+ 1; 
2979             if (fragmentLen 
< 0) 
2980                 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 
- offset
), (int)fragmentLen
); 
2981             wxString stringFragment 
= m_text
.Mid(r1 
- offset
, fragmentLen
); 
2983             dc
.SetTextForeground(GetAttributes().GetTextColour()); 
2984             dc
.DrawText(stringFragment
, x
, y
); 
2987             dc
.GetTextExtent(stringFragment
, & w
, & h
); 
2991         // 2. Selected chunk, if any. 
2992         if (selectionRange
.GetEnd() >= range
.GetStart()) 
2994             int s1 
= wxMax(selectionRange
.GetStart(), range
.GetStart()); 
2995             int s2 
= wxMin(selectionRange
.GetEnd(), range
.GetEnd()); 
2997             int fragmentLen 
= s2 
- s1 
+ 1; 
2998             if (fragmentLen 
< 0) 
2999                 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 
- offset
), (int)fragmentLen
); 
3000             wxString stringFragment 
= m_text
.Mid(s1 
- offset
, fragmentLen
); 
3003             dc
.GetTextExtent(stringFragment
, & w
, & h
); 
3004             wxRect 
selRect(x
, rect
.y
, w
, rect
.GetHeight()); 
3006             dc
.SetBrush(*wxBLACK_BRUSH
); 
3007             dc
.SetPen(*wxBLACK_PEN
); 
3008             dc
.DrawRectangle(selRect
); 
3009             dc
.SetTextForeground(*wxWHITE
); 
3010             dc
.DrawText(stringFragment
, x
, y
); 
3015         // 3. Remaining unselected chunk, if any 
3016         if (selectionRange
.GetEnd() < range
.GetEnd()) 
3018             int s2 
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd()); 
3019             int r2 
= range
.GetEnd(); 
3021             int fragmentLen 
= r2 
- s2 
+ 1; 
3022             if (fragmentLen 
< 0) 
3023                 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 
- offset
), (int)fragmentLen
); 
3024             wxString stringFragment 
= m_text
.Mid(s2 
- offset
, fragmentLen
); 
3026             dc
.SetTextForeground(GetAttributes().GetTextColour()); 
3027             dc
.DrawText(stringFragment
, x
, y
); 
3034 /// Lay the item out 
3035 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
)) 
3037     if (GetAttributes().GetFont().Ok()) 
3038         dc
.SetFont(GetAttributes().GetFont()); 
3041     dc
.GetTextExtent(m_text
, & w
, & h
, & m_descent
); 
3042     m_size 
= wxSize(w
, dc
.GetCharHeight()); 
3048 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
) 
3050     wxRichTextObject::Copy(obj
); 
3052     m_text 
= obj
.m_text
; 
3055 /// Get/set the object size for the given range. Returns false if the range 
3056 /// is invalid for this object. 
3057 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
)) const 
3059     if (!range
.IsWithin(GetRange())) 
3062     // Always assume unformatted text, since at this level we have no knowledge 
3063     // of line breaks - and we don't need it, since we'll calculate size within 
3064     // formatted text by doing it in chunks according to the line ranges 
3066     if (GetAttributes().GetFont().Ok()) 
3067         dc
.SetFont(GetAttributes().GetFont()); 
3069     int startPos 
= range
.GetStart() - GetRange().GetStart(); 
3070     long len 
= range
.GetLength(); 
3071     wxString stringChunk 
= m_text
.Mid(startPos
, (size_t) len
); 
3073     dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
); 
3074     size 
= wxSize(w
, dc
.GetCharHeight()); 
3079 /// Do a split, returning an object containing the second part, and setting 
3080 /// the first part in 'this'. 
3081 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
) 
3083     int index 
= pos 
- GetRange().GetStart(); 
3084     if (index 
< 0 || index 
>= (int) m_text
.Length()) 
3087     wxString firstPart 
= m_text
.Mid(0, index
); 
3088     wxString secondPart 
= m_text
.Mid(index
); 
3092     wxRichTextPlainText
* newObject 
= new wxRichTextPlainText(secondPart
); 
3093     newObject
->SetAttributes(GetAttributes()); 
3095     newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd())); 
3096     GetRange().SetEnd(pos
-1); 
3102 void wxRichTextPlainText::CalculateRange(long start
, long& end
) 
3104     end 
= start 
+ m_text
.Length() - 1; 
3105     m_range
.SetRange(start
, end
); 
3109 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
) 
3111     wxRichTextRange r 
= range
; 
3113     r
.LimitTo(GetRange()); 
3115     if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd()) 
3121     long startIndex 
= r
.GetStart() - GetRange().GetStart(); 
3122     long len 
= r
.GetLength(); 
3124     m_text 
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
); 
3128 /// Get text for the given range. 
3129 wxString 
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const 
3131     wxRichTextRange r 
= range
; 
3133     r
.LimitTo(GetRange()); 
3135     long startIndex 
= r
.GetStart() - GetRange().GetStart(); 
3136     long len 
= r
.GetLength(); 
3138     return m_text
.Mid(startIndex
, len
); 
3141 /// Returns true if this object can merge itself with the given one. 
3142 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const 
3144     return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) && 
3145         (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes())); 
3148 /// Returns true if this object merged itself with the given one. 
3149 /// The calling code will then delete the given object. 
3150 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
) 
3152     wxRichTextPlainText
* textObject 
= wxDynamicCast(object
, wxRichTextPlainText
); 
3153     wxASSERT( textObject 
!= NULL 
); 
3157         m_text 
+= textObject
->GetText(); 
3164 /// Dump to output stream for debugging 
3165 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
) 
3167     wxRichTextObject::Dump(stream
); 
3168     stream 
<< m_text 
<< wxT("\n"); 
3173  * This is a kind of box, used to represent the whole buffer 
3176 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
) 
3178 wxList 
wxRichTextBuffer::sm_handlers
; 
3181 void wxRichTextBuffer::Init() 
3183     m_commandProcessor 
= new wxCommandProcessor
; 
3184     m_styleSheet 
= NULL
; 
3186     m_batchedCommandDepth 
= 0; 
3187     m_batchedCommand 
= NULL
; 
3192 wxRichTextBuffer::~wxRichTextBuffer() 
3194     delete m_commandProcessor
; 
3195     delete m_batchedCommand
; 
3200 void wxRichTextBuffer::Clear() 
3203     GetCommandProcessor()->ClearCommands(); 
3205     Invalidate(wxRICHTEXT_ALL
); 
3208 void wxRichTextBuffer::Reset() 
3211     AddParagraph(wxEmptyString
); 
3212     GetCommandProcessor()->ClearCommands(); 
3214     Invalidate(wxRICHTEXT_ALL
); 
3217 /// Submit command to insert the given text 
3218 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
) 
3220     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
3222     action
->GetNewParagraphs().AddParagraphs(text
); 
3223     if (action
->GetNewParagraphs().GetChildCount() == 1) 
3224         action
->GetNewParagraphs().SetPartialParagraph(true); 
3226     action
->SetPosition(pos
); 
3228     // Set the range we'll need to delete in Undo 
3229     action
->SetRange(wxRichTextRange(pos
, pos 
+ text
.Length() - 1)); 
3231     SubmitAction(action
); 
3236 /// Submit command to insert the given text 
3237 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
) 
3239     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
3241     wxTextAttrEx 
attr(GetBasicStyle()); 
3242     wxRichTextApplyStyle(attr
, GetDefaultStyle()); 
3244     wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(wxEmptyString
, this, & attr
); 
3245     action
->GetNewParagraphs().AppendChild(newPara
); 
3246     action
->GetNewParagraphs().UpdateRanges(); 
3247     action
->GetNewParagraphs().SetPartialParagraph(false); 
3248     action
->SetPosition(pos
); 
3250     // Set the range we'll need to delete in Undo 
3251     action
->SetRange(wxRichTextRange(pos
, pos
)); 
3253     SubmitAction(action
); 
3258 /// Submit command to insert the given image 
3259 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
) 
3261     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
3263     wxTextAttrEx 
attr(GetBasicStyle()); 
3264     wxRichTextApplyStyle(attr
, GetDefaultStyle()); 
3266     wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(this, & attr
); 
3267     wxRichTextImage
* imageObject 
= new wxRichTextImage(imageBlock
, newPara
); 
3268     newPara
->AppendChild(imageObject
); 
3269     action
->GetNewParagraphs().AppendChild(newPara
); 
3270     action
->GetNewParagraphs().UpdateRanges(); 
3272     action
->GetNewParagraphs().SetPartialParagraph(true); 
3274     action
->SetPosition(pos
); 
3276     // Set the range we'll need to delete in Undo 
3277     action
->SetRange(wxRichTextRange(pos
, pos
)); 
3279     SubmitAction(action
); 
3284 /// Submit command to delete this range 
3285 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, long initialCaretPosition
, long WXUNUSED(newCaretPositon
), wxRichTextCtrl
* ctrl
) 
3287     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, this, ctrl
); 
3289     action
->SetPosition(initialCaretPosition
); 
3291     // Set the range to delete 
3292     action
->SetRange(range
); 
3294     // Copy the fragment that we'll need to restore in Undo 
3295     CopyFragment(range
, action
->GetOldParagraphs()); 
3297     // Special case: if there is only one (non-partial) paragraph, 
3298     // we must save the *next* paragraph's style, because that 
3299     // is the style we must apply when inserting the content back 
3300     // when undoing the delete. (This is because we're merging the 
3301     // paragraph with the previous paragraph and throwing away 
3302     // the style, and we need to restore it.) 
3303     if (!action
->GetOldParagraphs().GetPartialParagraph() && action
->GetOldParagraphs().GetChildCount() == 1) 
3305         wxRichTextParagraph
* lastPara 
= GetParagraphAtPosition(range
.GetStart()); 
3308             wxRichTextParagraph
* nextPara 
= GetParagraphAtPosition(range
.GetEnd()+1); 
3311                 wxRichTextParagraph
* para 
= (wxRichTextParagraph
*) action
->GetOldParagraphs().GetChild(0); 
3312                 para
->SetAttributes(nextPara
->GetAttributes()); 
3317     SubmitAction(action
); 
3322 /// Collapse undo/redo commands 
3323 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
) 
3325     if (m_batchedCommandDepth 
== 0) 
3327         wxASSERT(m_batchedCommand 
== NULL
); 
3328         if (m_batchedCommand
) 
3330             GetCommandProcessor()->Submit(m_batchedCommand
); 
3332         m_batchedCommand 
= new wxRichTextCommand(cmdName
); 
3335     m_batchedCommandDepth 
++; 
3340 /// Collapse undo/redo commands 
3341 bool wxRichTextBuffer::EndBatchUndo() 
3343     m_batchedCommandDepth 
--; 
3345     wxASSERT(m_batchedCommandDepth 
>= 0); 
3346     wxASSERT(m_batchedCommand 
!= NULL
); 
3348     if (m_batchedCommandDepth 
== 0) 
3350         GetCommandProcessor()->Submit(m_batchedCommand
); 
3351         m_batchedCommand 
= NULL
; 
3357 /// Submit immediately, or delay according to whether collapsing is on 
3358 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
) 
3360     if (BatchingUndo() && m_batchedCommand 
&& !SuppressingUndo()) 
3361         m_batchedCommand
->AddAction(action
); 
3364         wxRichTextCommand
* cmd 
= new wxRichTextCommand(action
->GetName()); 
3365         cmd
->AddAction(action
); 
3367         // Only store it if we're not suppressing undo. 
3368         return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo()); 
3374 /// Begin suppressing undo/redo commands. 
3375 bool wxRichTextBuffer::BeginSuppressUndo() 
3382 /// End suppressing undo/redo commands. 
3383 bool wxRichTextBuffer::EndSuppressUndo() 
3390 /// Begin using a style 
3391 bool wxRichTextBuffer::BeginStyle(const wxTextAttrEx
& style
) 
3393     wxTextAttrEx 
newStyle(GetDefaultStyle()); 
3395     // Save the old default style 
3396     m_attributeStack
.Append((wxObject
*) new wxTextAttrEx(GetDefaultStyle())); 
3398     wxRichTextApplyStyle(newStyle
, style
); 
3399     newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags()); 
3401     SetDefaultStyle(newStyle
); 
3403     // wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize()); 
3409 bool wxRichTextBuffer::EndStyle() 
3411     if (m_attributeStack
.GetFirst() == NULL
) 
3413         wxLogDebug(_("Too many EndStyle calls!")); 
3417     wxNode
* node 
= m_attributeStack
.GetLast(); 
3418     wxTextAttrEx
* attr 
= (wxTextAttrEx
*)node
->GetData(); 
3421     SetDefaultStyle(*attr
); 
3428 bool wxRichTextBuffer::EndAllStyles() 
3430     while (m_attributeStack
.GetCount() != 0) 
3435 /// Clear the style stack 
3436 void wxRichTextBuffer::ClearStyleStack() 
3438     for (wxNode
* node 
= m_attributeStack
.GetFirst(); node
; node 
= node
->GetNext()) 
3439         delete (wxTextAttrEx
*) node
->GetData(); 
3440     m_attributeStack
.Clear(); 
3443 /// Begin using bold 
3444 bool wxRichTextBuffer::BeginBold() 
3446     wxFont 
font(GetBasicStyle().GetFont()); 
3447     font
.SetWeight(wxBOLD
); 
3450     attr
.SetFont(font
,wxTEXT_ATTR_FONT_WEIGHT
); 
3452     return BeginStyle(attr
); 
3455 /// Begin using italic 
3456 bool wxRichTextBuffer::BeginItalic() 
3458     wxFont 
font(GetBasicStyle().GetFont()); 
3459     font
.SetStyle(wxITALIC
); 
3462     attr
.SetFont(font
, wxTEXT_ATTR_FONT_ITALIC
); 
3464     return BeginStyle(attr
); 
3467 /// Begin using underline 
3468 bool wxRichTextBuffer::BeginUnderline() 
3470     wxFont 
font(GetBasicStyle().GetFont()); 
3471     font
.SetUnderlined(true); 
3474     attr
.SetFont(font
, wxTEXT_ATTR_FONT_UNDERLINE
); 
3476     return BeginStyle(attr
); 
3479 /// Begin using point size 
3480 bool wxRichTextBuffer::BeginFontSize(int pointSize
) 
3482     wxFont 
font(GetBasicStyle().GetFont()); 
3483     font
.SetPointSize(pointSize
); 
3486     attr
.SetFont(font
, wxTEXT_ATTR_FONT_SIZE
); 
3488     return BeginStyle(attr
); 
3491 /// Begin using this font 
3492 bool wxRichTextBuffer::BeginFont(const wxFont
& font
) 
3495     attr
.SetFlags(wxTEXT_ATTR_FONT
); 
3498     return BeginStyle(attr
); 
3501 /// Begin using this colour 
3502 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
) 
3505     attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
); 
3506     attr
.SetTextColour(colour
); 
3508     return BeginStyle(attr
); 
3511 /// Begin using alignment 
3512 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
) 
3515     attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
); 
3516     attr
.SetAlignment(alignment
); 
3518     return BeginStyle(attr
); 
3521 /// Begin left indent 
3522 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
) 
3525     attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
); 
3526     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
3528     return BeginStyle(attr
); 
3531 /// Begin right indent 
3532 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
) 
3535     attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
); 
3536     attr
.SetRightIndent(rightIndent
); 
3538     return BeginStyle(attr
); 
3541 /// Begin paragraph spacing 
3542 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
) 
3546         flags 
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
; 
3548         flags 
|= wxTEXT_ATTR_PARA_SPACING_AFTER
; 
3551     attr
.SetFlags(flags
); 
3552     attr
.SetParagraphSpacingBefore(before
); 
3553     attr
.SetParagraphSpacingAfter(after
); 
3555     return BeginStyle(attr
); 
3558 /// Begin line spacing 
3559 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
) 
3562     attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
); 
3563     attr
.SetLineSpacing(lineSpacing
); 
3565     return BeginStyle(attr
); 
3568 /// Begin numbered bullet 
3569 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
) 
3572     attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_LEFT_INDENT
); 
3573     attr
.SetBulletStyle(bulletStyle
); 
3574     attr
.SetBulletNumber(bulletNumber
); 
3575     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
3577     return BeginStyle(attr
); 
3580 /// Begin symbol bullet 
3581 bool wxRichTextBuffer::BeginSymbolBullet(wxChar symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
) 
3584     attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_SYMBOL
|wxTEXT_ATTR_LEFT_INDENT
); 
3585     attr
.SetBulletStyle(bulletStyle
); 
3586     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
3587     attr
.SetBulletSymbol(symbol
); 
3589     return BeginStyle(attr
); 
3592 /// Begin named character style 
3593 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
) 
3595     if (GetStyleSheet()) 
3597         wxRichTextCharacterStyleDefinition
* def 
= GetStyleSheet()->FindCharacterStyle(characterStyle
); 
3601             def
->GetStyle().CopyTo(attr
); 
3602             return BeginStyle(attr
); 
3608 /// Begin named paragraph style 
3609 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
) 
3611     if (GetStyleSheet()) 
3613         wxRichTextParagraphStyleDefinition
* def 
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
); 
3617             def
->GetStyle().CopyTo(attr
); 
3618             return BeginStyle(attr
); 
3624 /// Adds a handler to the end 
3625 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler 
*handler
) 
3627     sm_handlers
.Append(handler
); 
3630 /// Inserts a handler at the front 
3631 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler 
*handler
) 
3633     sm_handlers
.Insert( handler 
); 
3636 /// Removes a handler 
3637 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
) 
3639     wxRichTextFileHandler 
*handler 
= FindHandler(name
); 
3642         sm_handlers
.DeleteObject(handler
); 
3650 /// Finds a handler by filename or, if supplied, type 
3651 wxRichTextFileHandler 
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
, int imageType
) 
3653     if (imageType 
!= wxRICHTEXT_TYPE_ANY
) 
3654         return FindHandler(imageType
); 
3657         wxString path
, file
, ext
; 
3658         wxSplitPath(filename
, & path
, & file
, & ext
); 
3659         return FindHandler(ext
, imageType
); 
3664 /// Finds a handler by name 
3665 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
) 
3667     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
3670         wxRichTextFileHandler 
*handler 
= (wxRichTextFileHandler
*)node
->GetData(); 
3671         if (handler
->GetName().Lower() == name
.Lower()) return handler
; 
3673         node 
= node
->GetNext(); 
3678 /// Finds a handler by extension and type 
3679 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, int type
) 
3681     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
3684         wxRichTextFileHandler 
*handler 
= (wxRichTextFileHandler
*)node
->GetData(); 
3685         if ( handler
->GetExtension().Lower() == extension
.Lower() && 
3686             (type 
== wxRICHTEXT_TYPE_ANY 
|| handler
->GetType() == type
) ) 
3688         node 
= node
->GetNext(); 
3693 /// Finds a handler by type 
3694 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(int type
) 
3696     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
3699         wxRichTextFileHandler 
*handler 
= (wxRichTextFileHandler 
*)node
->GetData(); 
3700         if (handler
->GetType() == type
) return handler
; 
3701         node 
= node
->GetNext(); 
3706 void wxRichTextBuffer::InitStandardHandlers() 
3708     if (!FindHandler(wxRICHTEXT_TYPE_TEXT
)) 
3709         AddHandler(new wxRichTextPlainTextHandler
); 
3712 void wxRichTextBuffer::CleanUpHandlers() 
3714     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
3717         wxRichTextFileHandler
* handler 
= (wxRichTextFileHandler
*)node
->GetData(); 
3718         wxList::compatibility_iterator next 
= node
->GetNext(); 
3723     sm_handlers
.Clear(); 
3726 wxString 
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
) 
3733     wxList::compatibility_iterator node 
= GetHandlers().GetFirst(); 
3737         wxRichTextFileHandler
* handler 
= (wxRichTextFileHandler
*) node
->GetData(); 
3738         if (handler
->IsVisible() && ((save 
&& handler
->CanSave()) || !save 
&& handler
->CanLoad())) 
3743                     wildcard 
+= wxT(";"); 
3744                 wildcard 
+= wxT("*.") + handler
->GetExtension(); 
3749                     wildcard 
+= wxT("|"); 
3750                 wildcard 
+= handler
->GetName(); 
3751                 wildcard 
+= wxT(" "); 
3752                 wildcard 
+= _("files"); 
3753                 wildcard 
+= wxT(" (*."); 
3754                 wildcard 
+= handler
->GetExtension(); 
3755                 wildcard 
+= wxT(")|*."); 
3756                 wildcard 
+= handler
->GetExtension(); 
3758                     types
->Add(handler
->GetType()); 
3763         node 
= node
->GetNext(); 
3767         wildcard 
= wxT("(") + wildcard 
+ wxT(")|") + wildcard
; 
3772 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, int type
) 
3774     wxRichTextFileHandler
* handler 
= FindHandlerFilenameOrType(filename
, type
); 
3777         SetDefaultStyle(wxTextAttrEx()); 
3779         bool success 
= handler
->LoadFile(this, filename
); 
3780         Invalidate(wxRICHTEXT_ALL
); 
3788 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, int type
) 
3790     wxRichTextFileHandler
* handler 
= FindHandlerFilenameOrType(filename
, type
); 
3792         return handler
->SaveFile(this, filename
); 
3797 /// Load from a stream 
3798 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, int type
) 
3800     wxRichTextFileHandler
* handler 
= FindHandler(type
); 
3803         SetDefaultStyle(wxTextAttrEx()); 
3804         bool success 
= handler
->LoadFile(this, stream
); 
3805         Invalidate(wxRICHTEXT_ALL
); 
3812 /// Save to a stream 
3813 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, int type
) 
3815     wxRichTextFileHandler
* handler 
= FindHandler(type
); 
3817         return handler
->SaveFile(this, stream
); 
3822 /// Copy the range to the clipboard 
3823 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
) 
3825     bool success 
= false; 
3826     wxString text 
= GetTextForRange(range
); 
3827     if (wxTheClipboard
->Open()) 
3829         success 
= wxTheClipboard
->SetData(new wxTextDataObject(text
)); 
3830         wxTheClipboard
->Close(); 
3835 /// Paste the clipboard content to the buffer 
3836 bool wxRichTextBuffer::PasteFromClipboard(long position
) 
3838     bool success 
= false; 
3839     if (CanPasteFromClipboard()) 
3841         if (wxTheClipboard
->Open()) 
3843             if (wxTheClipboard
->IsSupported(wxDF_TEXT
)) 
3845                 wxTextDataObject data
; 
3846                 wxTheClipboard
->GetData(data
); 
3847                 wxString 
text(data
.GetText()); 
3849                 InsertTextWithUndo(position
+1, text
, GetRichTextCtrl()); 
3853             else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
)) 
3855                 wxBitmapDataObject data
; 
3856                 wxTheClipboard
->GetData(data
); 
3857                 wxBitmap 
bitmap(data
.GetBitmap()); 
3858                 wxImage 
image(bitmap
.ConvertToImage()); 
3860                 wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, GetRichTextCtrl(), false); 
3862                 action
->GetNewParagraphs().AddImage(image
); 
3864                 if (action
->GetNewParagraphs().GetChildCount() == 1) 
3865                     action
->GetNewParagraphs().SetPartialParagraph(true); 
3867                 action
->SetPosition(position
); 
3869                 // Set the range we'll need to delete in Undo 
3870                 action
->SetRange(wxRichTextRange(position
, position
)); 
3872                 SubmitAction(action
); 
3876             wxTheClipboard
->Close(); 
3882 /// Can we paste from the clipboard? 
3883 bool wxRichTextBuffer::CanPasteFromClipboard() const 
3885     bool canPaste 
= false; 
3886     if (wxTheClipboard
->Open()) 
3888         if (wxTheClipboard
->IsSupported(wxDF_TEXT
) || wxTheClipboard
->IsSupported(wxDF_BITMAP
)) 
3892         wxTheClipboard
->Close(); 
3897 /// Dumps contents of buffer for debugging purposes 
3898 void wxRichTextBuffer::Dump() 
3902         wxStringOutputStream 
stream(& text
); 
3903         wxTextOutputStream 
textStream(stream
); 
3912  * Module to initialise and clean up handlers 
3915 class wxRichTextModule
: public wxModule
 
3917 DECLARE_DYNAMIC_CLASS(wxRichTextModule
) 
3919     wxRichTextModule() {} 
3920     bool OnInit() { wxRichTextBuffer::InitStandardHandlers(); return true; }; 
3921     void OnExit() { wxRichTextBuffer::CleanUpHandlers(); }; 
3924 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
) 
3928  * Commands for undo/redo 
3932 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
, 
3933                                      wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
) 
3935     /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, ctrl
, ignoreFirstTime
); 
3938 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
) 
3942 wxRichTextCommand::~wxRichTextCommand() 
3947 void wxRichTextCommand::AddAction(wxRichTextAction
* action
) 
3949     if (!m_actions
.Member(action
)) 
3950         m_actions
.Append(action
); 
3953 bool wxRichTextCommand::Do() 
3955     for (wxNode
* node 
= m_actions
.GetFirst(); node
; node 
= node
->GetNext()) 
3957         wxRichTextAction
* action 
= (wxRichTextAction
*) node
->GetData(); 
3964 bool wxRichTextCommand::Undo() 
3966     for (wxNode
* node 
= m_actions
.GetLast(); node
; node 
= node
->GetPrevious()) 
3968         wxRichTextAction
* action 
= (wxRichTextAction
*) node
->GetData(); 
3975 void wxRichTextCommand::ClearActions() 
3977     WX_CLEAR_LIST(wxList
, m_actions
); 
3985 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
, 
3986                                      wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
) 
3989     m_ignoreThis 
= ignoreFirstTime
; 
3994     m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle()); 
3995     m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle()); 
3997         cmd
->AddAction(this); 
4000 wxRichTextAction::~wxRichTextAction() 
4004 bool wxRichTextAction::Do() 
4006     m_buffer
->Modify(true); 
4010     case wxRICHTEXT_INSERT
: 
4012             m_buffer
->InsertFragment(GetPosition(), m_newParagraphs
); 
4013             m_buffer
->UpdateRanges(); 
4014             m_buffer
->Invalidate(GetRange()); 
4016             long newCaretPosition 
= GetPosition() + m_newParagraphs
.GetRange().GetLength() - 1; 
4017             if (m_newParagraphs
.GetPartialParagraph()) 
4018                 newCaretPosition 
--; 
4020             UpdateAppearance(newCaretPosition
, true /* send update event */); 
4024     case wxRICHTEXT_DELETE
: 
4026             m_buffer
->DeleteRange(GetRange()); 
4027             m_buffer
->UpdateRanges(); 
4028             m_buffer
->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); 
4030             UpdateAppearance(GetRange().GetStart()-1, true /* send update event */); 
4034     case wxRICHTEXT_CHANGE_STYLE
: 
4036             ApplyParagraphs(GetNewParagraphs()); 
4037             m_buffer
->Invalidate(GetRange()); 
4039             UpdateAppearance(GetPosition()); 
4050 bool wxRichTextAction::Undo() 
4052     m_buffer
->Modify(true); 
4056     case wxRICHTEXT_INSERT
: 
4058             m_buffer
->DeleteRange(GetRange()); 
4059             m_buffer
->UpdateRanges(); 
4060             m_buffer
->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); 
4062             long newCaretPosition 
= GetPosition() - 1; 
4063             // if (m_newParagraphs.GetPartialParagraph()) 
4064             //    newCaretPosition --; 
4066             UpdateAppearance(newCaretPosition
, true /* send update event */); 
4070     case wxRICHTEXT_DELETE
: 
4072             m_buffer
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
); 
4073             m_buffer
->UpdateRanges(); 
4074             m_buffer
->Invalidate(GetRange()); 
4076             UpdateAppearance(GetPosition(), true /* send update event */); 
4080     case wxRICHTEXT_CHANGE_STYLE
: 
4082             ApplyParagraphs(GetOldParagraphs()); 
4083             m_buffer
->Invalidate(GetRange()); 
4085             UpdateAppearance(GetPosition()); 
4096 /// Update the control appearance 
4097 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
) 
4101         m_ctrl
->SetCaretPosition(caretPosition
); 
4102         if (!m_ctrl
->IsFrozen()) 
4105             m_ctrl
->PositionCaret(); 
4108             if (sendUpdateEvent
) 
4109                 m_ctrl
->SendUpdateEvent(); 
4114 /// Replace the buffer paragraphs with the new ones. 
4115 void wxRichTextAction::ApplyParagraphs(const wxRichTextFragment
& fragment
) 
4117     wxRichTextObjectList::compatibility_iterator node 
= fragment
.GetChildren().GetFirst(); 
4120         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
4121         wxASSERT (para 
!= NULL
); 
4123         // We'll replace the existing paragraph by finding the paragraph at this position, 
4124         // delete its node data, and setting a copy as the new node data. 
4125         // TODO: make more efficient by simply swapping old and new paragraph objects. 
4127         wxRichTextParagraph
* existingPara 
= m_buffer
->GetParagraphAtPosition(para
->GetRange().GetStart()); 
4130             wxRichTextObjectList::compatibility_iterator bufferParaNode 
= m_buffer
->GetChildren().Find(existingPara
); 
4133                 wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(*para
); 
4134                 newPara
->SetParent(m_buffer
); 
4136                 bufferParaNode
->SetData(newPara
); 
4138                 delete existingPara
; 
4142         node 
= node
->GetNext(); 
4149  * This stores beginning and end positions for a range of data. 
4152 /// Limit this range to be within 'range' 
4153 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
) 
4155     if (m_start 
< range
.m_start
) 
4156         m_start 
= range
.m_start
; 
4158     if (m_end 
> range
.m_end
) 
4159         m_end 
= range
.m_end
; 
4165  * wxRichTextImage implementation 
4166  * This object represents an image. 
4169 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
) 
4171 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
): 
4172     wxRichTextObject(parent
) 
4177 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
): 
4178     wxRichTextObject(parent
) 
4180     m_imageBlock 
= imageBlock
; 
4181     m_imageBlock
.Load(m_image
); 
4184 /// Load wxImage from the block 
4185 bool wxRichTextImage::LoadFromBlock() 
4187     m_imageBlock
.Load(m_image
); 
4188     return m_imageBlock
.Ok(); 
4191 /// Make block from the wxImage 
4192 bool wxRichTextImage::MakeBlock() 
4194     if (m_imageBlock
.GetImageType() == wxBITMAP_TYPE_ANY 
|| m_imageBlock
.GetImageType() == -1) 
4195         m_imageBlock
.SetImageType(wxBITMAP_TYPE_PNG
); 
4197     m_imageBlock
.MakeImageBlock(m_image
, m_imageBlock
.GetImageType()); 
4198     return m_imageBlock
.Ok(); 
4203 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
)) 
4205     if (!m_image
.Ok() && m_imageBlock
.Ok()) 
4211     if (m_image
.Ok() && !m_bitmap
.Ok()) 
4212         m_bitmap 
= wxBitmap(m_image
); 
4214     int y 
= rect
.y 
+ (rect
.height 
- m_image
.GetHeight()); 
4217         dc
.DrawBitmap(m_bitmap
, rect
.x
, y
, true); 
4219     if (selectionRange
.Contains(range
.GetStart())) 
4221         dc
.SetBrush(*wxBLACK_BRUSH
); 
4222         dc
.SetPen(*wxBLACK_PEN
); 
4223         dc
.SetLogicalFunction(wxINVERT
); 
4224         dc
.DrawRectangle(rect
); 
4225         dc
.SetLogicalFunction(wxCOPY
); 
4231 /// Lay the item out 
4232 bool wxRichTextImage::Layout(wxDC
& WXUNUSED(dc
), const wxRect
& rect
, int WXUNUSED(style
)) 
4239         SetCachedSize(wxSize(m_image
.GetWidth(), m_image
.GetHeight())); 
4240         SetPosition(rect
.GetPosition()); 
4246 /// Get/set the object size for the given range. Returns false if the range 
4247 /// is invalid for this object. 
4248 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& WXUNUSED(dc
), int WXUNUSED(flags
)) const 
4250     if (!range
.IsWithin(GetRange())) 
4256     size
.x 
= m_image
.GetWidth(); 
4257     size
.y 
= m_image
.GetHeight(); 
4263 void wxRichTextImage::Copy(const wxRichTextImage
& obj
) 
4265     m_image 
= obj
.m_image
; 
4266     m_imageBlock 
= obj
.m_imageBlock
; 
4274 /// Compare two attribute objects 
4275 bool wxTextAttrEq(const wxTextAttrEx
& attr1
, const wxTextAttrEx
& attr2
) 
4278         attr1
.GetTextColour() == attr2
.GetTextColour() && 
4279         attr1
.GetBackgroundColour() == attr2
.GetBackgroundColour() && 
4280         attr1
.GetFont() == attr2
.GetFont() && 
4281         attr1
.GetAlignment() == attr2
.GetAlignment() && 
4282         attr1
.GetLeftIndent() == attr2
.GetLeftIndent() && 
4283         attr1
.GetRightIndent() == attr2
.GetRightIndent() && 
4284         attr1
.GetLeftSubIndent() == attr2
.GetLeftSubIndent() && 
4285         attr1
.GetTabs().GetCount() == attr2
.GetTabs().GetCount() && // heuristic 
4286         attr1
.GetLineSpacing() == attr2
.GetLineSpacing() && 
4287         attr1
.GetParagraphSpacingAfter() == attr2
.GetParagraphSpacingAfter() && 
4288         attr1
.GetParagraphSpacingBefore() == attr2
.GetParagraphSpacingBefore() && 
4289         attr1
.GetBulletStyle() == attr2
.GetBulletStyle() && 
4290         attr1
.GetBulletNumber() == attr2
.GetBulletNumber() && 
4291         attr1
.GetBulletSymbol() == attr2
.GetBulletSymbol() && 
4292         attr1
.GetCharacterStyleName() == attr2
.GetCharacterStyleName() && 
4293         attr1
.GetParagraphStyleName() == attr2
.GetParagraphStyleName()); 
4296 bool wxTextAttrEq(const wxTextAttrEx
& attr1
, const wxRichTextAttr
& attr2
) 
4299         attr1
.GetTextColour() == attr2
.GetTextColour() && 
4300         attr1
.GetBackgroundColour() == attr2
.GetBackgroundColour() && 
4301         attr1
.GetFont().GetPointSize() == attr2
.GetFontSize() && 
4302         attr1
.GetFont().GetStyle() == attr2
.GetFontStyle() && 
4303         attr1
.GetFont().GetWeight() == attr2
.GetFontWeight() && 
4304         attr1
.GetFont().GetFaceName() == attr2
.GetFontFaceName() && 
4305         attr1
.GetFont().GetUnderlined() == attr2
.GetFontUnderlined() && 
4306         attr1
.GetAlignment() == attr2
.GetAlignment() && 
4307         attr1
.GetLeftIndent() == attr2
.GetLeftIndent() && 
4308         attr1
.GetRightIndent() == attr2
.GetRightIndent() && 
4309         attr1
.GetLeftSubIndent() == attr2
.GetLeftSubIndent() && 
4310         attr1
.GetTabs().GetCount() == attr2
.GetTabs().GetCount() && // heuristic 
4311         attr1
.GetLineSpacing() == attr2
.GetLineSpacing() && 
4312         attr1
.GetParagraphSpacingAfter() == attr2
.GetParagraphSpacingAfter() && 
4313         attr1
.GetParagraphSpacingBefore() == attr2
.GetParagraphSpacingBefore() && 
4314         attr1
.GetBulletStyle() == attr2
.GetBulletStyle() && 
4315         attr1
.GetBulletNumber() == attr2
.GetBulletNumber() && 
4316         attr1
.GetBulletSymbol() == attr2
.GetBulletSymbol() && 
4317         attr1
.GetCharacterStyleName() == attr2
.GetCharacterStyleName() && 
4318         attr1
.GetParagraphStyleName() == attr2
.GetParagraphStyleName()); 
4321 /// Compare two attribute objects, but take into account the flags 
4322 /// specifying attributes of interest. 
4323 bool wxTextAttrEqPartial(const wxTextAttrEx
& attr1
, const wxTextAttrEx
& attr2
, int flags
) 
4325     if ((flags 
& wxTEXT_ATTR_TEXT_COLOUR
) && attr1
.GetTextColour() != attr2
.GetTextColour()) 
4328     if ((flags 
& wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr1
.GetBackgroundColour() != attr2
.GetBackgroundColour()) 
4331     if ((flags 
& wxTEXT_ATTR_FONT_FACE
) && attr1
.GetFont().Ok() && attr2
.GetFont().Ok() && 
4332         attr1
.GetFont().GetFaceName() != attr2
.GetFont().GetFaceName()) 
4335     if ((flags 
& wxTEXT_ATTR_FONT_SIZE
) && attr1
.GetFont().Ok() && attr2
.GetFont().Ok() && 
4336         attr1
.GetFont().GetPointSize() != attr2
.GetFont().GetPointSize()) 
4339     if ((flags 
& wxTEXT_ATTR_FONT_WEIGHT
) && attr1
.GetFont().Ok() && attr2
.GetFont().Ok() && 
4340         attr1
.GetFont().GetWeight() != attr2
.GetFont().GetWeight()) 
4343     if ((flags 
& wxTEXT_ATTR_FONT_ITALIC
) && attr1
.GetFont().Ok() && attr2
.GetFont().Ok() && 
4344         attr1
.GetFont().GetStyle() != attr2
.GetFont().GetStyle()) 
4347     if ((flags 
& wxTEXT_ATTR_FONT_UNDERLINE
) && attr1
.GetFont().Ok() && attr2
.GetFont().Ok() && 
4348         attr1
.GetFont().GetUnderlined() != attr2
.GetFont().GetUnderlined()) 
4351     if ((flags 
& wxTEXT_ATTR_ALIGNMENT
) && attr1
.GetAlignment() != attr2
.GetAlignment()) 
4354     if ((flags 
& wxTEXT_ATTR_LEFT_INDENT
) && 
4355         ((attr1
.GetLeftIndent() != attr2
.GetLeftIndent()) || (attr1
.GetLeftSubIndent() != attr2
.GetLeftSubIndent()))) 
4358     if ((flags 
& wxTEXT_ATTR_RIGHT_INDENT
) && 
4359         (attr1
.GetRightIndent() != attr2
.GetRightIndent())) 
4362     if ((flags 
& wxTEXT_ATTR_PARA_SPACING_AFTER
) && 
4363         (attr1
.GetParagraphSpacingAfter() != attr2
.GetParagraphSpacingAfter())) 
4366     if ((flags 
& wxTEXT_ATTR_PARA_SPACING_BEFORE
) && 
4367         (attr1
.GetParagraphSpacingBefore() != attr2
.GetParagraphSpacingBefore())) 
4370     if ((flags 
& wxTEXT_ATTR_LINE_SPACING
) && 
4371         (attr1
.GetLineSpacing() != attr2
.GetLineSpacing())) 
4374     if ((flags 
& wxTEXT_ATTR_CHARACTER_STYLE_NAME
) && 
4375         (attr1
.GetCharacterStyleName() != attr2
.GetCharacterStyleName())) 
4378     if ((flags 
& wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
) && 
4379         (attr1
.GetParagraphStyleName() != attr2
.GetParagraphStyleName())) 
4382     if ((flags 
& wxTEXT_ATTR_BULLET_STYLE
) && 
4383         (attr1
.GetBulletStyle() != attr2
.GetBulletStyle())) 
4386     if ((flags 
& wxTEXT_ATTR_BULLET_NUMBER
) && 
4387         (attr1
.GetBulletNumber() != attr2
.GetBulletNumber())) 
4390     if ((flags 
& wxTEXT_ATTR_BULLET_SYMBOL
) && 
4391         (attr1
.GetBulletSymbol() != attr2
.GetBulletSymbol())) 
4395     if ((flags & wxTEXT_ATTR_TABS) && 
4402 bool wxTextAttrEqPartial(const wxTextAttrEx
& attr1
, const wxRichTextAttr
& attr2
, int flags
) 
4404     if ((flags 
& wxTEXT_ATTR_TEXT_COLOUR
) && attr1
.GetTextColour() != attr2
.GetTextColour()) 
4407     if ((flags 
& wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr1
.GetBackgroundColour() != attr2
.GetBackgroundColour()) 
4410     if ((flags 
& (wxTEXT_ATTR_FONT
)) && !attr1
.GetFont().Ok()) 
4413     if ((flags 
& wxTEXT_ATTR_FONT_FACE
) && attr1
.GetFont().Ok() && 
4414         attr1
.GetFont().GetFaceName() != attr2
.GetFontFaceName()) 
4417     if ((flags 
& wxTEXT_ATTR_FONT_SIZE
) && attr1
.GetFont().Ok() && 
4418         attr1
.GetFont().GetPointSize() != attr2
.GetFontSize()) 
4421     if ((flags 
& wxTEXT_ATTR_FONT_WEIGHT
) && attr1
.GetFont().Ok() && 
4422         attr1
.GetFont().GetWeight() != attr2
.GetFontWeight()) 
4425     if ((flags 
& wxTEXT_ATTR_FONT_ITALIC
) && attr1
.GetFont().Ok() && 
4426         attr1
.GetFont().GetStyle() != attr2
.GetFontStyle()) 
4429     if ((flags 
& wxTEXT_ATTR_FONT_UNDERLINE
) && attr1
.GetFont().Ok() && 
4430         attr1
.GetFont().GetUnderlined() != attr2
.GetFontUnderlined()) 
4433     if ((flags 
& wxTEXT_ATTR_ALIGNMENT
) && attr1
.GetAlignment() != attr2
.GetAlignment()) 
4436     if ((flags 
& wxTEXT_ATTR_LEFT_INDENT
) && 
4437         ((attr1
.GetLeftIndent() != attr2
.GetLeftIndent()) || (attr1
.GetLeftSubIndent() != attr2
.GetLeftSubIndent()))) 
4440     if ((flags 
& wxTEXT_ATTR_RIGHT_INDENT
) && 
4441         (attr1
.GetRightIndent() != attr2
.GetRightIndent())) 
4444     if ((flags 
& wxTEXT_ATTR_PARA_SPACING_AFTER
) && 
4445         (attr1
.GetParagraphSpacingAfter() != attr2
.GetParagraphSpacingAfter())) 
4448     if ((flags 
& wxTEXT_ATTR_PARA_SPACING_BEFORE
) && 
4449         (attr1
.GetParagraphSpacingBefore() != attr2
.GetParagraphSpacingBefore())) 
4452     if ((flags 
& wxTEXT_ATTR_LINE_SPACING
) && 
4453         (attr1
.GetLineSpacing() != attr2
.GetLineSpacing())) 
4456     if ((flags 
& wxTEXT_ATTR_CHARACTER_STYLE_NAME
) && 
4457         (attr1
.GetCharacterStyleName() != attr2
.GetCharacterStyleName())) 
4460     if ((flags 
& wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
) && 
4461         (attr1
.GetParagraphStyleName() != attr2
.GetParagraphStyleName())) 
4464     if ((flags 
& wxTEXT_ATTR_BULLET_STYLE
) && 
4465         (attr1
.GetBulletStyle() != attr2
.GetBulletStyle())) 
4468     if ((flags 
& wxTEXT_ATTR_BULLET_NUMBER
) && 
4469         (attr1
.GetBulletNumber() != attr2
.GetBulletNumber())) 
4472     if ((flags 
& wxTEXT_ATTR_BULLET_SYMBOL
) && 
4473         (attr1
.GetBulletSymbol() != attr2
.GetBulletSymbol())) 
4477     if ((flags & wxTEXT_ATTR_TABS) && 
4485 /// Apply one style to another 
4486 bool wxRichTextApplyStyle(wxTextAttrEx
& destStyle
, const wxTextAttrEx
& style
) 
4489     if (style
.GetFont().Ok() && ((style
.GetFlags() & (wxTEXT_ATTR_FONT
)) == (wxTEXT_ATTR_FONT
))) 
4490         destStyle
.SetFont(style
.GetFont()); 
4491     else if (style
.GetFont().Ok()) 
4493         wxFont font 
= destStyle
.GetFont(); 
4495         if (style
.GetFlags() & wxTEXT_ATTR_FONT_FACE
) 
4496             font
.SetFaceName(style
.GetFont().GetFaceName()); 
4498         if (style
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) 
4499             font
.SetPointSize(style
.GetFont().GetPointSize()); 
4501         if (style
.GetFlags() & wxTEXT_ATTR_FONT_ITALIC
) 
4502             font
.SetStyle(style
.GetFont().GetStyle()); 
4504         if (style
.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT
) 
4505             font
.SetWeight(style
.GetFont().GetWeight()); 
4507         if (style
.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE
) 
4508             font
.SetUnderlined(style
.GetFont().GetUnderlined()); 
4510         if (font 
!= destStyle
.GetFont()) 
4511             destStyle
.SetFont(font
); 
4514     if ( style
.GetTextColour().Ok() && style
.HasTextColour()) 
4515         destStyle
.SetTextColour(style
.GetTextColour()); 
4517     if ( style
.GetBackgroundColour().Ok() && style
.HasBackgroundColour()) 
4518         destStyle
.SetBackgroundColour(style
.GetBackgroundColour()); 
4520     if (style
.HasAlignment()) 
4521         destStyle
.SetAlignment(style
.GetAlignment()); 
4523     if (style
.HasTabs()) 
4524         destStyle
.SetTabs(style
.GetTabs()); 
4526     if (style
.HasLeftIndent()) 
4527         destStyle
.SetLeftIndent(style
.GetLeftIndent(), style
.GetLeftSubIndent()); 
4529     if (style
.HasRightIndent()) 
4530         destStyle
.SetRightIndent(style
.GetRightIndent()); 
4532     if (style
.HasParagraphSpacingAfter()) 
4533         destStyle
.SetParagraphSpacingAfter(style
.GetParagraphSpacingAfter()); 
4535     if (style
.HasParagraphSpacingBefore()) 
4536         destStyle
.SetParagraphSpacingBefore(style
.GetParagraphSpacingBefore()); 
4538     if (style
.HasLineSpacing()) 
4539         destStyle
.SetLineSpacing(style
.GetLineSpacing()); 
4541     if (style
.HasCharacterStyleName()) 
4542         destStyle
.SetCharacterStyleName(style
.GetCharacterStyleName()); 
4544     if (style
.HasParagraphStyleName()) 
4545         destStyle
.SetParagraphStyleName(style
.GetParagraphStyleName()); 
4547     if (style
.HasBulletStyle()) 
4549         destStyle
.SetBulletStyle(style
.GetBulletStyle()); 
4550         destStyle
.SetBulletSymbol(style
.GetBulletSymbol()); 
4553     if (style
.HasBulletNumber()) 
4554         destStyle
.SetBulletNumber(style
.GetBulletNumber()); 
4559 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxTextAttrEx
& style
) 
4561     wxTextAttrEx destStyle2
; 
4562     destStyle
.CopyTo(destStyle2
); 
4563     wxRichTextApplyStyle(destStyle2
, style
); 
4564     destStyle 
= destStyle2
; 
4568 bool wxRichTextApplyStyle(wxTextAttrEx
& destStyle
, const wxRichTextAttr
& style
) 
4571     // Whole font. Avoiding setting individual attributes if possible, since 
4572     // it recreates the font each time. 
4573     if ((style
.GetFlags() & (wxTEXT_ATTR_FONT
)) == (wxTEXT_ATTR_FONT
)) 
4575         destStyle
.SetFont(wxFont(style
.GetFontSize(), destStyle
.GetFont().Ok() ? destStyle
.GetFont().GetFamily() : wxDEFAULT
, 
4576             style
.GetFontStyle(), style
.GetFontWeight(), style
.GetFontUnderlined(), style
.GetFontFaceName())); 
4578     else if (style
.GetFlags() & (wxTEXT_ATTR_FONT
)) 
4580         wxFont font 
= destStyle
.GetFont(); 
4582         if (style
.GetFlags() & wxTEXT_ATTR_FONT_FACE
) 
4583             font
.SetFaceName(style
.GetFontFaceName()); 
4585         if (style
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) 
4586             font
.SetPointSize(style
.GetFontSize()); 
4588         if (style
.GetFlags() & wxTEXT_ATTR_FONT_ITALIC
) 
4589             font
.SetStyle(style
.GetFontStyle()); 
4591         if (style
.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT
) 
4592             font
.SetWeight(style
.GetFontWeight()); 
4594         if (style
.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE
) 
4595             font
.SetUnderlined(style
.GetFontUnderlined()); 
4597         if (font 
!= destStyle
.GetFont()) 
4598             destStyle
.SetFont(font
); 
4601     if ( style
.GetTextColour().Ok() && style
.HasTextColour()) 
4602         destStyle
.SetTextColour(style
.GetTextColour()); 
4604     if ( style
.GetBackgroundColour().Ok() && style
.HasBackgroundColour()) 
4605         destStyle
.SetBackgroundColour(style
.GetBackgroundColour()); 
4607     if (style
.HasAlignment()) 
4608         destStyle
.SetAlignment(style
.GetAlignment()); 
4610     if (style
.HasTabs()) 
4611         destStyle
.SetTabs(style
.GetTabs()); 
4613     if (style
.HasLeftIndent()) 
4614         destStyle
.SetLeftIndent(style
.GetLeftIndent(), style
.GetLeftSubIndent()); 
4616     if (style
.HasRightIndent()) 
4617         destStyle
.SetRightIndent(style
.GetRightIndent()); 
4619     if (style
.HasParagraphSpacingAfter()) 
4620         destStyle
.SetParagraphSpacingAfter(style
.GetParagraphSpacingAfter()); 
4622     if (style
.HasParagraphSpacingBefore()) 
4623         destStyle
.SetParagraphSpacingBefore(style
.GetParagraphSpacingBefore()); 
4625     if (style
.HasLineSpacing()) 
4626         destStyle
.SetLineSpacing(style
.GetLineSpacing()); 
4628     if (style
.HasCharacterStyleName()) 
4629         destStyle
.SetCharacterStyleName(style
.GetCharacterStyleName()); 
4631     if (style
.HasParagraphStyleName()) 
4632         destStyle
.SetParagraphStyleName(style
.GetParagraphStyleName()); 
4634     if (style
.HasBulletStyle()) 
4636         destStyle
.SetBulletStyle(style
.GetBulletStyle()); 
4637         destStyle
.SetBulletSymbol(style
.GetBulletSymbol()); 
4640     if (style
.HasBulletNumber()) 
4641         destStyle
.SetBulletNumber(style
.GetBulletNumber()); 
4648  * wxRichTextAttr stores attributes without a wxFont object, so is a much more 
4649  * efficient way to query styles. 
4653 wxRichTextAttr::wxRichTextAttr(const wxColour
& colText
, 
4654                const wxColour
& colBack
, 
4655                wxTextAttrAlignment alignment
): m_textAlignment(alignment
), m_colText(colText
), m_colBack(colBack
) 
4659     if (m_colText
.Ok()) m_flags 
|= wxTEXT_ATTR_TEXT_COLOUR
; 
4660     if (m_colBack
.Ok()) m_flags 
|= wxTEXT_ATTR_BACKGROUND_COLOUR
; 
4661     if (alignment 
!= wxTEXT_ALIGNMENT_DEFAULT
) 
4662         m_flags 
|= wxTEXT_ATTR_ALIGNMENT
; 
4665 wxRichTextAttr::wxRichTextAttr(const wxTextAttrEx
& attr
) 
4673 void wxRichTextAttr::Init() 
4675     m_textAlignment 
= wxTEXT_ALIGNMENT_DEFAULT
; 
4678     m_leftSubIndent 
= 0; 
4682     m_fontStyle 
= wxNORMAL
; 
4683     m_fontWeight 
= wxNORMAL
; 
4684     m_fontUnderlined 
= false; 
4686     m_paragraphSpacingAfter 
= 0; 
4687     m_paragraphSpacingBefore 
= 0; 
4689     m_bulletStyle 
= wxTEXT_ATTR_BULLET_STYLE_NONE
; 
4691     m_bulletSymbol 
= wxT('*'); 
4695 void wxRichTextAttr::operator= (const wxRichTextAttr
& attr
) 
4697     m_colText 
= attr
.m_colText
; 
4698     m_colBack 
= attr
.m_colBack
; 
4699     m_textAlignment 
= attr
.m_textAlignment
; 
4700     m_leftIndent 
= attr
.m_leftIndent
; 
4701     m_leftSubIndent 
= attr
.m_leftSubIndent
; 
4702     m_rightIndent 
= attr
.m_rightIndent
; 
4703     m_tabs 
= attr
.m_tabs
; 
4704     m_flags 
= attr
.m_flags
; 
4706     m_fontSize 
= attr
.m_fontSize
; 
4707     m_fontStyle 
= attr
.m_fontStyle
; 
4708     m_fontWeight 
= attr
.m_fontWeight
; 
4709     m_fontUnderlined 
= attr
.m_fontUnderlined
; 
4710     m_fontFaceName 
= attr
.m_fontFaceName
; 
4712     m_paragraphSpacingAfter 
= attr
.m_paragraphSpacingAfter
; 
4713     m_paragraphSpacingBefore 
= attr
.m_paragraphSpacingBefore
; 
4714     m_lineSpacing 
= attr
.m_lineSpacing
; 
4715     m_characterStyleName 
= attr
.m_characterStyleName
; 
4716     m_paragraphStyleName 
= attr
.m_paragraphStyleName
; 
4717     m_bulletStyle 
= attr
.m_bulletStyle
; 
4718     m_bulletNumber 
= attr
.m_bulletNumber
; 
4719     m_bulletSymbol 
= attr
.m_bulletSymbol
; 
4723 void wxRichTextAttr::operator= (const wxTextAttrEx
& attr
) 
4725     m_colText 
= attr
.GetTextColour(); 
4726     m_colBack 
= attr
.GetBackgroundColour(); 
4727     m_textAlignment 
= attr
.GetAlignment(); 
4728     m_leftIndent 
= attr
.GetLeftIndent(); 
4729     m_leftSubIndent 
= attr
.GetLeftSubIndent(); 
4730     m_rightIndent 
= attr
.GetRightIndent(); 
4731     m_tabs 
= attr
.GetTabs(); 
4732     m_flags 
= attr
.GetFlags(); 
4734     m_paragraphSpacingAfter 
= attr
.GetParagraphSpacingAfter(); 
4735     m_paragraphSpacingBefore 
= attr
.GetParagraphSpacingBefore(); 
4736     m_lineSpacing 
= attr
.GetLineSpacing(); 
4737     m_characterStyleName 
= attr
.GetCharacterStyleName(); 
4738     m_paragraphStyleName 
= attr
.GetParagraphStyleName(); 
4740     if (attr
.GetFont().Ok()) 
4741         GetFontAttributes(attr
.GetFont()); 
4744 // Making a wxTextAttrEx object. 
4745 wxRichTextAttr::operator wxTextAttrEx () const 
4752 // Copy to a wxTextAttr 
4753 void wxRichTextAttr::CopyTo(wxTextAttrEx
& attr
) const 
4755     attr
.SetTextColour(GetTextColour()); 
4756     attr
.SetBackgroundColour(GetBackgroundColour()); 
4757     attr
.SetAlignment(GetAlignment()); 
4758     attr
.SetTabs(GetTabs()); 
4759     attr
.SetLeftIndent(GetLeftIndent(), GetLeftSubIndent()); 
4760     attr
.SetRightIndent(GetRightIndent()); 
4761     attr
.SetFont(CreateFont()); 
4762     attr
.SetFlags(GetFlags()); // Important: set after SetFont, since SetFont sets flags 
4764     attr
.SetParagraphSpacingAfter(m_paragraphSpacingAfter
); 
4765     attr
.SetParagraphSpacingBefore(m_paragraphSpacingBefore
); 
4766     attr
.SetLineSpacing(m_lineSpacing
); 
4767     attr
.SetBulletStyle(m_bulletStyle
); 
4768     attr
.SetBulletNumber(m_bulletNumber
); 
4769     attr
.SetBulletSymbol(m_bulletSymbol
); 
4770     attr
.SetCharacterStyleName(m_characterStyleName
); 
4771     attr
.SetParagraphStyleName(m_paragraphStyleName
); 
4775 // Create font from font attributes. 
4776 wxFont 
wxRichTextAttr::CreateFont() const 
4778     wxFont 
font(m_fontSize
, wxDEFAULT
, m_fontStyle
, m_fontWeight
, m_fontUnderlined
, m_fontFaceName
); 
4780     font
.SetNoAntiAliasing(true); 
4785 // Get attributes from font. 
4786 bool wxRichTextAttr::GetFontAttributes(const wxFont
& font
) 
4791     m_fontSize 
= font
.GetPointSize(); 
4792     m_fontStyle 
= font
.GetStyle(); 
4793     m_fontWeight 
= font
.GetWeight(); 
4794     m_fontUnderlined 
= font
.GetUnderlined(); 
4795     m_fontFaceName 
= font
.GetFaceName(); 
4801  * wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes. 
4804 wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx
& attr
): wxTextAttr(attr
) 
4806     m_paragraphSpacingAfter 
= attr
.m_paragraphSpacingAfter
; 
4807     m_paragraphSpacingBefore 
= attr
.m_paragraphSpacingBefore
; 
4808     m_lineSpacing 
= attr
.m_lineSpacing
; 
4809     m_paragraphStyleName 
= attr
.m_paragraphStyleName
; 
4810     m_characterStyleName 
= attr
.m_characterStyleName
; 
4811     m_bulletStyle 
= attr
.m_bulletStyle
; 
4812     m_bulletNumber 
= attr
.m_bulletNumber
; 
4813     m_bulletSymbol 
= attr
.m_bulletSymbol
; 
4816 // Initialise this object. 
4817 void wxTextAttrEx::Init() 
4819     m_paragraphSpacingAfter 
= 0; 
4820     m_paragraphSpacingBefore 
= 0; 
4822     m_bulletStyle 
= wxTEXT_ATTR_BULLET_STYLE_NONE
; 
4825     m_bulletSymbol 
= wxT('*'); 
4828 // Assignment from a wxTextAttrEx object 
4829 void wxTextAttrEx::operator= (const wxTextAttrEx
& attr
) 
4831     wxTextAttr::operator= (attr
); 
4833     m_paragraphSpacingAfter 
= attr
.m_paragraphSpacingAfter
; 
4834     m_paragraphSpacingBefore 
= attr
.m_paragraphSpacingBefore
; 
4835     m_lineSpacing 
= attr
.m_lineSpacing
; 
4836     m_characterStyleName 
= attr
.m_characterStyleName
; 
4837     m_paragraphStyleName 
= attr
.m_paragraphStyleName
; 
4838     m_bulletStyle 
= attr
.m_bulletStyle
; 
4839     m_bulletNumber 
= attr
.m_bulletNumber
; 
4840     m_bulletSymbol 
= attr
.m_bulletSymbol
; 
4843 // Assignment from a wxTextAttr object. 
4844 void wxTextAttrEx::operator= (const wxTextAttr
& attr
) 
4846     wxTextAttr::operator= (attr
); 
4850  * wxRichTextFileHandler 
4851  * Base class for file handlers 
4854 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
) 
4857 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer 
*buffer
, const wxString
& filename
) 
4859     wxFFileInputStream 
stream(filename
); 
4861         return LoadFile(buffer
, stream
); 
4866 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer 
*buffer
, const wxString
& filename
) 
4868     wxFFileOutputStream 
stream(filename
); 
4870         return SaveFile(buffer
, stream
); 
4874 #endif // wxUSE_STREAMS 
4876 /// Can we handle this filename (if using files)? By default, checks the extension. 
4877 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const 
4879     wxString path
, file
, ext
; 
4880     wxSplitPath(filename
, & path
, & file
, & ext
); 
4882     return (ext
.Lower() == GetExtension()); 
4886  * wxRichTextTextHandler 
4887  * Plain text handler 
4890 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
) 
4893 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer 
*buffer
, wxInputStream
& stream
) 
4901     while (!stream
.Eof()) 
4903         int ch 
= stream
.GetC(); 
4907             if (ch 
== 10 && lastCh 
!= 13) 
4910             if (ch 
> 0 && ch 
!= 10) 
4918     buffer
->AddParagraphs(str
); 
4919     buffer
->UpdateRanges(); 
4925 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer 
*buffer
, wxOutputStream
& stream
) 
4930     wxString text 
= buffer
->GetText(); 
4931     wxCharBuffer buf 
= text
.ToAscii(); 
4933     stream
.Write((const char*) buf
, text
.Length()); 
4936 #endif // wxUSE_STREAMS 
4939  * Stores information about an image, in binary in-memory form 
4942 wxRichTextImageBlock::wxRichTextImageBlock() 
4947 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject() 
4953 wxRichTextImageBlock::~wxRichTextImageBlock() 
4962 void wxRichTextImageBlock::Init() 
4969 void wxRichTextImageBlock::Clear() 
4979 // Load the original image into a memory block. 
4980 // If the image is not a JPEG, we must convert it into a JPEG 
4981 // to conserve space. 
4982 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to 
4983 // load the image a 2nd time. 
4985 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, int imageType
, wxImage
& image
, bool convertToJPEG
) 
4987     m_imageType 
= imageType
; 
4989     wxString 
filenameToRead(filename
); 
4990     bool removeFile 
= false; 
4992     if (imageType 
== -1) 
4993         return false; // Could not determine image type 
4995     if ((imageType 
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
) 
4998         bool success 
= wxGetTempFileName(_("image"), tempFile
) ; 
5002         wxUnusedVar(success
); 
5004         image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
); 
5005         filenameToRead 
= tempFile
; 
5008         m_imageType 
= wxBITMAP_TYPE_JPEG
; 
5011     if (!file
.Open(filenameToRead
)) 
5014     m_dataSize 
= (size_t) file
.Length(); 
5019     m_data 
= ReadBlock(filenameToRead
, m_dataSize
); 
5022         wxRemoveFile(filenameToRead
); 
5024     return (m_data 
!= NULL
); 
5027 // Make an image block from the wxImage in the given 
5029 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, int imageType
, int quality
) 
5031     m_imageType 
= imageType
; 
5032     image
.SetOption(wxT("quality"), quality
); 
5034     if (imageType 
== -1) 
5035         return false; // Could not determine image type 
5038     bool success 
= wxGetTempFileName(_("image"), tempFile
) ; 
5041     wxUnusedVar(success
); 
5043     if (!image
.SaveFile(tempFile
, m_imageType
)) 
5045         if (wxFileExists(tempFile
)) 
5046             wxRemoveFile(tempFile
); 
5051     if (!file
.Open(tempFile
)) 
5054     m_dataSize 
= (size_t) file
.Length(); 
5059     m_data 
= ReadBlock(tempFile
, m_dataSize
); 
5061     wxRemoveFile(tempFile
); 
5063     return (m_data 
!= NULL
); 
5068 bool wxRichTextImageBlock::Write(const wxString
& filename
) 
5070     return WriteBlock(filename
, m_data
, m_dataSize
); 
5073 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
) 
5075     m_imageType 
= block
.m_imageType
; 
5081     m_dataSize 
= block
.m_dataSize
; 
5082     if (m_dataSize 
== 0) 
5085     m_data 
= new unsigned char[m_dataSize
]; 
5087     for (i 
= 0; i 
< m_dataSize
; i
++) 
5088         m_data
[i
] = block
.m_data
[i
]; 
5092 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
) 
5097 // Load a wxImage from the block 
5098 bool wxRichTextImageBlock::Load(wxImage
& image
) 
5103     // Read in the image. 
5105     wxMemoryInputStream 
mstream(m_data
, m_dataSize
); 
5106     bool success 
= image
.LoadFile(mstream
, GetImageType()); 
5109     bool success 
= wxGetTempFileName(_("image"), tempFile
) ; 
5112     if (!WriteBlock(tempFile
, m_data
, m_dataSize
)) 
5116     success 
= image
.LoadFile(tempFile
, GetImageType()); 
5117     wxRemoveFile(tempFile
); 
5123 // Write data in hex to a stream 
5124 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
) 
5128     for (i 
= 0; i 
< (int) m_dataSize
; i
++) 
5130         hex 
= wxDecToHex(m_data
[i
]); 
5131         wxCharBuffer buf 
= hex
.ToAscii(); 
5133         stream
.Write((const char*) buf
, hex
.Length()); 
5139 // Read data in hex from a stream 
5140 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, int imageType
) 
5142     int dataSize 
= length
/2; 
5147     wxString 
str(wxT("  ")); 
5148     m_data 
= new unsigned char[dataSize
]; 
5150     for (i 
= 0; i 
< dataSize
; i 
++) 
5152         str
[0] = stream
.GetC(); 
5153         str
[1] = stream
.GetC(); 
5155         m_data
[i
] = (unsigned char)wxHexToDec(str
); 
5158     m_dataSize 
= dataSize
; 
5159     m_imageType 
= imageType
; 
5165 // Allocate and read from stream as a block of memory 
5166 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
) 
5168     unsigned char* block 
= new unsigned char[size
]; 
5172     stream
.Read(block
, size
); 
5177 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
) 
5179     wxFileInputStream 
stream(filename
); 
5183     return ReadBlock(stream
, size
); 
5186 // Write memory block to stream 
5187 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
) 
5189     stream
.Write((void*) block
, size
); 
5190     return stream
.IsOk(); 
5194 // Write memory block to file 
5195 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
) 
5197     wxFileOutputStream 
outStream(filename
); 
5198     if (!outStream
.Ok()) 
5201     return WriteBlock(outStream
, block
, size
);