1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/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" 
  21 #include "wx/richtext/richtextbuffer.h" 
  27     #include "wx/dataobj.h" 
  28     #include "wx/module.h" 
  31 #include "wx/settings.h" 
  32 #include "wx/filename.h" 
  33 #include "wx/clipbrd.h" 
  34 #include "wx/wfstream.h" 
  35 #include "wx/mstream.h" 
  36 #include "wx/sstream.h" 
  37 #include "wx/textfile.h" 
  38 #include "wx/hashmap.h" 
  39 #include "wx/dynarray.h" 
  41 #include "wx/richtext/richtextctrl.h" 
  42 #include "wx/richtext/richtextstyles.h" 
  43 #include "wx/richtext/richtextimagedlg.h" 
  45 #include "wx/listimpl.cpp" 
  47 WX_DEFINE_LIST(wxRichTextObjectList
) 
  48 WX_DEFINE_LIST(wxRichTextLineList
) 
  50 // Switch off if the platform doesn't like it for some reason 
  51 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1 
  53 // Use GetPartialTextExtents for platforms that support it natively 
  54 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1 
  56 const wxChar wxRichTextLineBreakChar 
= (wxChar
) 29; 
  58 // Helper classes for floating layout 
  59 struct wxRichTextFloatRectMap
 
  61     wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
) 
  71     wxRichTextObject
* anchor
; 
  74 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
); 
  76 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
) 
  78     return r1
->startY 
- r2
->startY
; 
  81 class wxRichTextFloatCollector
 
  84     wxRichTextFloatCollector(int width
); 
  85     ~wxRichTextFloatCollector(); 
  87     // Collect the floating objects info in the given paragraph 
  88     void CollectFloat(wxRichTextParagraph
* para
); 
  89     void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
); 
  91     // Return the last paragraph we collected 
  92     wxRichTextParagraph
* LastParagraph(); 
  94     // Given the start y position and the height of the line, 
  95     // find out how wide the line can be 
  96     wxRect 
GetAvailableRect(int startY
, int endY
); 
  98     // Given a floating box, find its fit position 
  99     int GetFitPosition(int direction
, int start
, int height
) const; 
 100     int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const; 
 102     // Find the last y position 
 103     int GetLastRectBottom(); 
 105     // Draw the floats inside a rect 
 106     void Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int style
); 
 108     // HitTest the floats 
 109     int HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
); 
 111     static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
); 
 113     static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
); 
 115     static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
); 
 117     static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int style
); 
 119     static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
); 
 122     wxRichTextFloatRectMapArray m_left
; 
 123     wxRichTextFloatRectMapArray m_right
; 
 125     wxRichTextParagraph
* m_para
; 
 129  * Binary search helper function 
 130  * The argument point is the Y coordinate, and this fuction 
 131  * always return the floating rect that contain this coordinate 
 132  * or under this coordinate. 
 134 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
) 
 136     int end 
= array
.GetCount() - 1; 
 149         int mid 
= (start 
+ end
) / 2; 
 150         if (array
[mid
]->startY 
<= point 
&& array
[mid
]->endY 
>= point
) 
 152         else if (array
[mid
]->startY 
> point
) 
 157         else if (array
[mid
]->endY 
< point
) 
 167 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
) 
 170     int len 
= array
.GetCount(); 
 172     wxASSERT(index 
>= 0 && index 
< len
); 
 174     if (array
[index
]->startY 
< startY 
&& array
[index
]->endY 
> startY
) 
 175         ret 
= ret 
< array
[index
]->width 
? array
[index
]->width 
: ret
; 
 176     while (index 
< len 
&& array
[index
]->startY 
<= endY
) 
 178         ret 
= ret 
< array
[index
]->width 
? array
[index
]->width 
: ret
; 
 185 wxRichTextFloatCollector::wxRichTextFloatCollector(int width
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
) 
 191 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
) 
 193     int len 
= array
.GetCount(); 
 194     for (int i 
= 0; i 
< len
; i
++) 
 198 wxRichTextFloatCollector::~wxRichTextFloatCollector() 
 200     FreeFloatRectMapArray(m_left
); 
 201     FreeFloatRectMapArray(m_right
); 
 204 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const 
 206     if (array
.GetCount() == 0) 
 209     unsigned int i 
= SearchAdjacentRect(array
, start
); 
 211     while (i 
< array
.GetCount()) 
 213         if (array
[i
]->startY 
- last 
>= height
) 
 215         last 
= array
[i
]->endY
; 
 222 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const 
 224     if (direction 
== wxRICHTEXT_FLOAT_LEFT
) 
 225         return GetFitPosition(m_left
, start
, height
); 
 226     else if (direction 
== wxRICHTEXT_FLOAT_RIGHT
) 
 227         return GetFitPosition(m_right
, start
, height
); 
 230         wxASSERT("Never should be here"); 
 235 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
) 
 237         int direction 
= floating
->GetFloatDirection(); 
 238         wxPoint pos 
= floating
->GetPosition(); 
 239         wxSize size 
= floating
->GetCachedSize(); 
 240         wxRichTextFloatRectMap 
*map 
= new wxRichTextFloatRectMap(pos
.y
, pos
.y 
+ size
.y
, size
.x
, floating
); 
 244             case wxRICHTEXT_FLOAT_NONE
: 
 247             case wxRICHTEXT_FLOAT_LEFT
: 
 248                 // Just a not-enough simple assertion 
 249                 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
); 
 252             case wxRICHTEXT_FLOAT_RIGHT
: 
 253                 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
); 
 258                 wxASSERT("Must some error occurs"); 
 264 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
) 
 266     wxRichTextObjectList::compatibility_iterator node 
= para
->GetChildren().GetFirst(); 
 269         wxRichTextObject
* floating 
= node
->GetData(); 
 271         if (floating
->IsFloating()) 
 273             wxRichTextAnchoredObject
* anchor 
= wxDynamicCast(floating
, wxRichTextAnchoredObject
); 
 274             if (anchor 
&& anchor
->GetAnchoredAttr().IsAnchored()) 
 276                 CollectFloat(para
, floating
); 
 280         node 
= node
->GetNext(); 
 286 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph() 
 291 wxRect 
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
) 
 293     int widthLeft 
= 0, widthRight 
= 0; 
 294     if (m_left
.GetCount() != 0) 
 296         unsigned int i 
= SearchAdjacentRect(m_left
, startY
); 
 297         if (i 
>= 0 && i 
< m_left
.GetCount()) 
 298             widthLeft 
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
); 
 300     if (m_right
.GetCount() != 0) 
 302         unsigned int j 
= SearchAdjacentRect(m_right
, startY
); 
 303         if (j 
>= 0 && j 
< m_right
.GetCount()) 
 304             widthRight 
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
); 
 307     return wxRect(widthLeft
, 0, m_width 
- widthLeft 
- widthRight
, 0); 
 310 int wxRichTextFloatCollector::GetLastRectBottom() 
 313     int len 
= m_left
.GetCount(); 
 315         ret 
= ret 
> m_left
[len
-1]->endY 
? ret 
: m_left
[len
-1]->endY
; 
 317     len 
= m_right
.GetCount(); 
 319         ret 
= ret 
> m_right
[len
-1]->endY 
? ret 
: m_right
[len
-1]->endY
; 
 325 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextRange
& WXUNUSED(selectionRange
), const wxRect
& rect
, int descent
, int style
) 
 328     int end 
= rect
.y 
+ rect
.height
; 
 330     i 
= SearchAdjacentRect(array
, start
); 
 331     if (i 
< 0 || i 
>= array
.GetCount()) 
 333     j 
= SearchAdjacentRect(array
, end
); 
 334     if (j 
< 0 || j 
>= array
.GetCount()) 
 335         j 
= array
.GetCount() - 1; 
 338         wxRichTextObject
* obj 
= array
[i
]->anchor
; 
 339         wxRichTextRange r 
= obj
->GetRange(); 
 340         obj
->Draw(dc
, r
, wxRichTextRange(0, -1), wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
); 
 345 void wxRichTextFloatCollector::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int style
) 
 347     if (m_left
.GetCount() > 0) 
 348         DrawFloat(m_left
, dc
, range
, selectionRange
, rect
, descent
, style
); 
 349     if (m_right
.GetCount() > 0) 
 350         DrawFloat(m_right
, dc
, range
, selectionRange
, rect
, descent
, style
); 
 353 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
) 
 356     if (array
.GetCount() == 0) 
 357         return wxRICHTEXT_HITTEST_NONE
; 
 358     i 
= SearchAdjacentRect(array
, pt
.y
); 
 359     if (i 
< 0 || i 
>= array
.GetCount()) 
 360         return wxRICHTEXT_HITTEST_NONE
; 
 361     wxPoint point 
= array
[i
]->anchor
->GetPosition(); 
 362     wxSize size 
= array
[i
]->anchor
->GetCachedSize(); 
 363     if (point
.x 
<= pt
.x 
&& point
.x 
+ size
.x 
>= pt
.x
 
 364         && point
.y 
<= pt
.y 
&& point
.y 
+ size
.y 
>= pt
.y
) 
 366         textPosition 
= array
[i
]->anchor
->GetRange().GetStart(); 
 367         if (pt
.x 
> (pt
.x 
+ pt
.x 
+ size
.x
) / 2) 
 368             return wxRICHTEXT_HITTEST_BEFORE
; 
 370             return wxRICHTEXT_HITTEST_AFTER
; 
 373     return wxRICHTEXT_HITTEST_NONE
; 
 376 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
) 
 378     int ret 
= HitTestFloat(m_left
, dc
, pt
, textPosition
); 
 379     if (ret 
== wxRICHTEXT_HITTEST_NONE
) 
 381         ret 
= HitTestFloat(m_right
, dc
, pt
, textPosition
); 
 386 // Helpers for efficiency  
 387 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
) 
 389     // JACS: did I do this some time ago when testing? Should we re-enable it? 
 391     const wxFont
& font1 
= dc
.GetFont(); 
 392     if (font1
.IsOk() && font
.IsOk()) 
 394         if (font1
.GetPointSize() == font
.GetPointSize() && 
 395             font1
.GetFamily() == font
.GetFamily() && 
 396             font1
.GetStyle() == font
.GetStyle() && 
 397             font1
.GetWeight() == font
.GetWeight() && 
 398             font1
.GetUnderlined() == font
.GetUnderlined() && 
 399             font1
.GetFamily() == font
.GetFamily() && 
 400             font1
.GetFaceName() == font
.GetFaceName()) 
 407 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
) 
 409     const wxPen
& pen1 
= dc
.GetPen(); 
 410     if (pen1
.IsOk() && pen
.IsOk()) 
 412         if (pen1
.GetWidth() == pen
.GetWidth() && 
 413             pen1
.GetStyle() == pen
.GetStyle() && 
 414             pen1
.GetColour() == pen
.GetColour()) 
 420 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
) 
 422     const wxBrush
& brush1 
= dc
.GetBrush(); 
 423     if (brush1
.IsOk() && brush
.IsOk()) 
 425         if (brush1
.GetStyle() == brush
.GetStyle() && 
 426             brush1
.GetColour() == brush
.GetColour()) 
 432 void wxRichTextAnchoredObjectAttr::Init() 
 434     m_align 
= wxRICHTEXT_CENTRE
; 
 435     m_floating 
= wxRICHTEXT_FLOAT_NONE
; 
 437     m_unitsOffset 
= wxRICHTEXT_PX
; 
 439     m_unitsW 
= wxRICHTEXT_PX
; 
 440     m_unitsH 
= wxRICHTEXT_PX
; 
 442     // Unspecified to begin with (use actual image size) 
 447 void wxRichTextAnchoredObjectAttr::Copy(const wxRichTextAnchoredObjectAttr
& attr
) 
 449     m_align 
= attr
.m_align
; 
 450     m_floating 
= attr
.m_floating
; 
 451     m_offset 
= attr
.m_offset
; 
 452     m_unitsOffset 
= attr
.m_unitsOffset
; 
 454     m_unitsW 
= attr
.m_unitsW
; 
 455     m_unitsH 
= attr
.m_unitsH
; 
 456     m_width 
= attr
.m_width
; 
 457     m_height 
= attr
.m_height
; 
 462  * This is the base for drawable objects. 
 465 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
) 
 467 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
) 
 479 wxRichTextObject::~wxRichTextObject() 
 483 void wxRichTextObject::Dereference() 
 491 void wxRichTextObject::Copy(const wxRichTextObject
& obj
) 
 495     m_dirty 
= obj
.m_dirty
; 
 496     m_range 
= obj
.m_range
; 
 497     m_attributes 
= obj
.m_attributes
; 
 498     m_descent 
= obj
.m_descent
; 
 501 void wxRichTextObject::SetMargins(int margin
) 
 503     m_leftMargin 
= m_rightMargin 
= m_topMargin 
= m_bottomMargin 
= margin
; 
 506 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
) 
 508     m_leftMargin 
= leftMargin
; 
 509     m_rightMargin 
= rightMargin
; 
 510     m_topMargin 
= topMargin
; 
 511     m_bottomMargin 
= bottomMargin
; 
 514 // Convert units in tenths of a millimetre to device units 
 515 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const 
 517     int p 
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
); 
 520     wxRichTextBuffer
* buffer 
= GetBuffer(); 
 522         p 
= (int) ((double)p 
/ buffer
->GetScale()); 
 526 // Convert units in tenths of a millimetre to device units 
 527 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
) 
 529     // There are ppi pixels in 254.1 "1/10 mm" 
 531     double pixels 
= ((double) units 
* (double)ppi
) / 254.1; 
 536 /// Dump to output stream for debugging 
 537 void wxRichTextObject::Dump(wxTextOutputStream
& stream
) 
 539     stream 
<< GetClassInfo()->GetClassName() << wxT("\n"); 
 540     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"); 
 541     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"); 
 544 /// Gets the containing buffer 
 545 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const 
 547     const wxRichTextObject
* obj 
= this; 
 548     while (obj 
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
))) 
 549         obj 
= obj
->GetParent(); 
 550     return wxDynamicCast(obj
, wxRichTextBuffer
); 
 554  * wxRichTextCompositeObject 
 555  * This is the base for drawable objects. 
 558 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
) 
 560 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
): 
 561     wxRichTextObject(parent
) 
 565 wxRichTextCompositeObject::~wxRichTextCompositeObject() 
 570 /// Get the nth child 
 571 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const 
 573     wxASSERT ( n 
< m_children
.GetCount() ); 
 575     return m_children
.Item(n
)->GetData(); 
 578 /// Append a child, returning the position 
 579 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
) 
 581     m_children
.Append(child
); 
 582     child
->SetParent(this); 
 583     return m_children
.GetCount() - 1; 
 586 /// Insert the child in front of the given object, or at the beginning 
 587 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
) 
 591         wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(inFrontOf
); 
 592         m_children
.Insert(node
, child
); 
 595         m_children
.Insert(child
); 
 596     child
->SetParent(this); 
 602 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
) 
 604     wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(child
); 
 607         wxRichTextObject
* obj 
= node
->GetData(); 
 608         m_children
.Erase(node
); 
 617 /// Delete all children 
 618 bool wxRichTextCompositeObject::DeleteChildren() 
 620     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 623         wxRichTextObjectList::compatibility_iterator oldNode 
= node
; 
 625         wxRichTextObject
* child 
= node
->GetData(); 
 626         child
->Dereference(); // Only delete if reference count is zero 
 628         node 
= node
->GetNext(); 
 629         m_children
.Erase(oldNode
); 
 635 /// Get the child count 
 636 size_t wxRichTextCompositeObject::GetChildCount() const 
 638     return m_children
.GetCount(); 
 642 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
) 
 644     wxRichTextObject::Copy(obj
); 
 648     wxRichTextObjectList::compatibility_iterator node 
= obj
.m_children
.GetFirst(); 
 651         wxRichTextObject
* child 
= node
->GetData(); 
 652         wxRichTextObject
* newChild 
= child
->Clone(); 
 653         newChild
->SetParent(this); 
 654         m_children
.Append(newChild
); 
 656         node 
= node
->GetNext(); 
 660 /// Hit-testing: returns a flag indicating hit test details, plus 
 661 /// information about position 
 662 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
) 
 664     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 667         wxRichTextObject
* child 
= node
->GetData(); 
 669         int ret 
= child
->HitTest(dc
, pt
, textPosition
); 
 670         if (ret 
!= wxRICHTEXT_HITTEST_NONE
) 
 673         node 
= node
->GetNext(); 
 676     textPosition 
= GetRange().GetEnd()-1; 
 677     return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
; 
 680 /// Finds the absolute position and row height for the given character position 
 681 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
) 
 683     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 686         wxRichTextObject
* child 
= node
->GetData(); 
 688         if (child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
)) 
 691         node 
= node
->GetNext(); 
 698 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
) 
 700     long current 
= start
; 
 701     long lastEnd 
= current
; 
 703     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 706         wxRichTextObject
* child 
= node
->GetData(); 
 709         child
->CalculateRange(current
, childEnd
); 
 712         current 
= childEnd 
+ 1; 
 714         node 
= node
->GetNext(); 
 719     // An object with no children has zero length 
 720     if (m_children
.GetCount() == 0) 
 723     m_range
.SetRange(start
, end
); 
 726 /// Delete range from layout. 
 727 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
) 
 729     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 733         wxRichTextObject
* obj 
= (wxRichTextObject
*) node
->GetData(); 
 734         wxRichTextObjectList::compatibility_iterator next 
= node
->GetNext(); 
 736         // Delete the range in each paragraph 
 738         // When a chunk has been deleted, internally the content does not 
 739         // now match the ranges. 
 740         // However, so long as deletion is not done on the same object twice this is OK. 
 741         // If you may delete content from the same object twice, recalculate 
 742         // the ranges inbetween DeleteRange calls by calling CalculateRanges, and 
 743         // adjust the range you're deleting accordingly. 
 745         if (!obj
->GetRange().IsOutside(range
)) 
 747             obj
->DeleteRange(range
); 
 749             // Delete an empty object, or paragraph within this range. 
 750             if (obj
->IsEmpty() || 
 751                 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd())) 
 753                 // An empty paragraph has length 1, so won't be deleted unless the 
 754                 // whole range is deleted. 
 755                 RemoveChild(obj
, true); 
 765 /// Get any text in this object for the given range 
 766 wxString 
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const 
 769     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 772         wxRichTextObject
* child 
= node
->GetData(); 
 773         wxRichTextRange childRange 
= range
; 
 774         if (!child
->GetRange().IsOutside(range
)) 
 776             childRange
.LimitTo(child
->GetRange()); 
 778             wxString childText 
= child
->GetTextForRange(childRange
); 
 782         node 
= node
->GetNext(); 
 788 /// Recursively merge all pieces that can be merged. 
 789 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
) 
 791     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 794         wxRichTextObject
* child 
= node
->GetData(); 
 795         if (range 
== wxRICHTEXT_ALL 
|| !child
->GetRange().IsOutside(range
)) 
 797             wxRichTextCompositeObject
* composite 
= wxDynamicCast(child
, wxRichTextCompositeObject
); 
 799                 composite
->Defragment(); 
 803                 wxRichTextObject
* nextChild 
= node
->GetNext()->GetData(); 
 804                 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
)) 
 806                     nextChild
->Dereference(); 
 807                     m_children
.Erase(node
->GetNext()); 
 809                     // Don't set node -- we'll see if we can merge again with the next 
 813                     node 
= node
->GetNext(); 
 816                 node 
= node
->GetNext(); 
 819             node 
= node
->GetNext(); 
 825 /// Dump to output stream for debugging 
 826 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
) 
 828     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 831         wxRichTextObject
* child 
= node
->GetData(); 
 833         node 
= node
->GetNext(); 
 840  * This defines a 2D space to lay out objects 
 843 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextCompositeObject
) 
 845 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
): 
 846     wxRichTextCompositeObject(parent
) 
 851 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& WXUNUSED(rect
), int descent
, int style
) 
 853     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 856         wxRichTextObject
* child 
= node
->GetData(); 
 858         wxRect childRect 
= wxRect(child
->GetPosition(), child
->GetCachedSize()); 
 859         child
->Draw(dc
, range
, selectionRange
, childRect
, descent
, style
); 
 861         node 
= node
->GetNext(); 
 867 bool wxRichTextBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
) 
 869     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 872         wxRichTextObject
* child 
= node
->GetData(); 
 873         child
->Layout(dc
, rect
, style
); 
 875         node 
= node
->GetNext(); 
 881 /// Get/set the size for the given range. Assume only has one child. 
 882 bool wxRichTextBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const 
 884     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 887         wxRichTextObject
* child 
= node
->GetData(); 
 888         return child
->GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
); 
 895 void wxRichTextBox::Copy(const wxRichTextBox
& obj
) 
 897     wxRichTextCompositeObject::Copy(obj
); 
 902  * wxRichTextParagraphLayoutBox 
 903  * This box knows how to lay out paragraphs. 
 906 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextBox
) 
 908 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
): 
 909     wxRichTextBox(parent
) 
 914 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox() 
 916     if (m_floatCollector
) 
 918         delete m_floatCollector
; 
 919         m_floatCollector 
= NULL
; 
 923 /// Initialize the object. 
 924 void wxRichTextParagraphLayoutBox::Init() 
 928     // For now, assume is the only box and has no initial size. 
 929     m_range 
= wxRichTextRange(0, -1); 
 931     m_invalidRange
.SetRange(-1, -1); 
 936     m_partialParagraph 
= false; 
 937     m_floatCollector 
= NULL
; 
 940 // Gather information about floating objects 
 941 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(int width
, wxRichTextObject
* untilObj
) 
 943     if (m_floatCollector 
!= NULL
) 
 944         delete m_floatCollector
; 
 945     m_floatCollector 
= new wxRichTextFloatCollector(width
); 
 946     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 947     while (node 
&& node
->GetData() != untilObj
) 
 949         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 950         wxASSERT (child 
!= NULL
); 
 952             m_floatCollector
->CollectFloat(child
); 
 953         node 
= node
->GetNext(); 
 960 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
) 
 962     int ret 
= wxRICHTEXT_HITTEST_NONE
; 
 963     if (m_floatCollector
) 
 964         ret 
= m_floatCollector
->HitTest(dc
, pt
, textPosition
); 
 966     if (ret 
== wxRICHTEXT_HITTEST_NONE
) 
 967         return wxRichTextCompositeObject::HitTest(dc
, pt
, textPosition
); 
 972 /// Draw the floating objects 
 973 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int style
) 
 975     if (m_floatCollector
) 
 976         m_floatCollector
->Draw(dc
, range
, selectionRange
, rect
, descent
, style
); 
 979 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextAnchoredObject
* obj
) 
 984     from
->RemoveChild(obj
); 
 985     to
->AppendChild(obj
); 
 989 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int style
) 
 991     DrawFloats(dc
, range
, selectionRange
, rect
, descent
, style
); 
 992     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
 995         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
 996         wxASSERT (child 
!= NULL
); 
 998         if (child 
&& !child
->GetRange().IsOutside(range
)) 
1000             wxRect 
childRect(child
->GetPosition(), child
->GetCachedSize()); 
1002             if (((style 
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom()) 
1007             else if (((style 
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop()) 
1012                 child
->Draw(dc
, range
, selectionRange
, rect
, descent
, style
); 
1015         node 
= node
->GetNext(); 
1020 /// Lay the item out 
1021 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
) 
1023     wxRect availableSpace
; 
1024     bool formatRect 
= (style 
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
; 
1026     // If only laying out a specific area, the passed rect has a different meaning: 
1027     // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize, 
1028     // so that during a size, only the visible part will be relaid out, or 
1029     // it would take too long causing flicker. As an approximation, we assume that 
1030     // everything up to the start of the visible area is laid out correctly. 
1033         availableSpace 
= wxRect(0 + m_leftMargin
, 
1035                           rect
.width 
- m_leftMargin 
- m_rightMargin
, 
1038         // Invalidate the part of the buffer from the first visible line 
1039         // to the end. If other parts of the buffer are currently invalid, 
1040         // then they too will be taken into account if they are above 
1041         // the visible point. 
1043         wxRichTextLine
* line 
= GetLineAtYPosition(rect
.y
); 
1045             startPos 
= line
->GetAbsoluteRange().GetStart(); 
1047         Invalidate(wxRichTextRange(startPos
, GetRange().GetEnd())); 
1050         availableSpace 
= wxRect(rect
.x 
+ m_leftMargin
, 
1051                           rect
.y 
+ m_topMargin
, 
1052                           rect
.width 
- m_leftMargin 
- m_rightMargin
, 
1053                           rect
.height 
- m_topMargin 
- m_bottomMargin
); 
1057     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1059     bool layoutAll 
= true; 
1061     // Get invalid range, rounding to paragraph start/end. 
1062     wxRichTextRange invalidRange 
= GetInvalidRange(true); 
1064     if (invalidRange 
== wxRICHTEXT_NONE 
&& !formatRect
) 
1067     if (invalidRange 
== wxRICHTEXT_ALL
) 
1069     else    // If we know what range is affected, start laying out from that point on. 
1070         if (invalidRange
.GetStart() >= GetRange().GetStart()) 
1072         wxRichTextParagraph
* firstParagraph 
= GetParagraphAtPosition(invalidRange
.GetStart()); 
1075             wxRichTextObjectList::compatibility_iterator firstNode 
= m_children
.Find(firstParagraph
); 
1076             wxRichTextObjectList::compatibility_iterator previousNode
; 
1078                 previousNode 
= firstNode
->GetPrevious(); 
1083                     wxRichTextParagraph
* previousParagraph 
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
); 
1084                     availableSpace
.y 
= previousParagraph
->GetPosition().y 
+ previousParagraph
->GetCachedSize().y
; 
1087                 // Now we're going to start iterating from the first affected paragraph. 
1095     UpdateFloatingObjects(availableSpace
.width
, node 
? node
->GetData() : (wxRichTextObject
*) NULL
); 
1097     // A way to force speedy rest-of-buffer layout (the 'else' below) 
1098     bool forceQuickLayout 
= false; 
1102         // Assume this box only contains paragraphs 
1104         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1105         wxCHECK_MSG( child
, false, wxT("Unknown object in layout") ); 
1107         // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines 
1108         if ( !forceQuickLayout 
&& 
1110                     child
->GetLines().IsEmpty() || 
1111                         !child
->GetRange().IsOutside(invalidRange
)) ) 
1113             child
->Layout(dc
, availableSpace
, style
); 
1115             // Layout must set the cached size 
1116             availableSpace
.y 
+= child
->GetCachedSize().y
; 
1117             maxWidth 
= wxMax(maxWidth
, child
->GetCachedSize().x
); 
1119             // If we're just formatting the visible part of the buffer, 
1120             // and we're now past the bottom of the window, start quick 
1122             if (formatRect 
&& child
->GetPosition().y 
> rect
.GetBottom()) 
1123                 forceQuickLayout 
= true; 
1127             // We're outside the immediately affected range, so now let's just 
1128             // move everything up or down. This assumes that all the children have previously 
1129             // been laid out and have wrapped line lists associated with them. 
1130             // TODO: check all paragraphs before the affected range. 
1132             int inc 
= availableSpace
.y 
- child
->GetPosition().y
; 
1136                 wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1139                     if (child
->GetLines().GetCount() == 0) 
1140                         child
->Layout(dc
, availableSpace
, style
); 
1142                         child
->SetPosition(wxPoint(child
->GetPosition().x
, child
->GetPosition().y 
+ inc
)); 
1144                     availableSpace
.y 
+= child
->GetCachedSize().y
; 
1145                     maxWidth 
= wxMax(maxWidth
, child
->GetCachedSize().x
); 
1148                 node 
= node
->GetNext(); 
1153         node 
= node
->GetNext(); 
1156     SetCachedSize(wxSize(maxWidth
, availableSpace
.y
)); 
1159     m_invalidRange 
= wxRICHTEXT_NONE
; 
1165 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
) 
1167     wxRichTextBox::Copy(obj
); 
1169     m_partialParagraph 
= obj
.m_partialParagraph
; 
1170     m_defaultAttributes 
= obj
.m_defaultAttributes
; 
1173 /// Get/set the size for the given range. 
1174 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const 
1178     wxRichTextObjectList::compatibility_iterator startPara 
= wxRichTextObjectList::compatibility_iterator(); 
1179     wxRichTextObjectList::compatibility_iterator endPara 
= wxRichTextObjectList::compatibility_iterator(); 
1181     // First find the first paragraph whose starting position is within the range. 
1182     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1185         // child is a paragraph 
1186         wxRichTextObject
* child 
= node
->GetData(); 
1187         const wxRichTextRange
& r 
= child
->GetRange(); 
1189         if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart()) 
1195         node 
= node
->GetNext(); 
1198     // Next find the last paragraph containing part of the range 
1199     node 
= m_children
.GetFirst(); 
1202         // child is a paragraph 
1203         wxRichTextObject
* child 
= node
->GetData(); 
1204         const wxRichTextRange
& r 
= child
->GetRange(); 
1206         if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd()) 
1212         node 
= node
->GetNext(); 
1215     if (!startPara 
|| !endPara
) 
1218     // Now we can add up the sizes 
1219     for (node 
= startPara
; node 
; node 
= node
->GetNext()) 
1221         // child is a paragraph 
1222         wxRichTextObject
* child 
= node
->GetData(); 
1223         const wxRichTextRange
& childRange 
= child
->GetRange(); 
1224         wxRichTextRange rangeToFind 
= range
; 
1225         rangeToFind
.LimitTo(childRange
); 
1229         int childDescent 
= 0; 
1230         child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
); 
1232         descent 
= wxMax(childDescent
, descent
); 
1234         sz
.x 
= wxMax(sz
.x
, childSize
.x
); 
1235         sz
.y 
+= childSize
.y
; 
1237         if (node 
== endPara
) 
1246 /// Get the paragraph at the given position 
1247 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const 
1252     // First find the first paragraph whose starting position is within the range. 
1253     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1256         // child is a paragraph 
1257         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1258         wxASSERT (child 
!= NULL
); 
1260         // Return first child in buffer if position is -1 
1264         if (child
->GetRange().Contains(pos
)) 
1267         node 
= node
->GetNext(); 
1272 /// Get the line at the given position 
1273 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const 
1278     // First find the first paragraph whose starting position is within the range. 
1279     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1282         wxRichTextObject
* obj 
= (wxRichTextObject
*) node
->GetData(); 
1283         if (obj
->GetRange().Contains(pos
)) 
1285             // child is a paragraph 
1286             wxRichTextParagraph
* child 
= wxDynamicCast(obj
, wxRichTextParagraph
); 
1287             wxASSERT (child 
!= NULL
); 
1289             wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
1292                 wxRichTextLine
* line 
= node2
->GetData(); 
1294                 wxRichTextRange range 
= line
->GetAbsoluteRange(); 
1296                 if (range
.Contains(pos
) || 
1298                     // If the position is end-of-paragraph, then return the last line of 
1299                     // of the paragraph. 
1300                     ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos 
== child
->GetRange().GetEnd()))) 
1303                 node2 
= node2
->GetNext(); 
1307         node 
= node
->GetNext(); 
1310     int lineCount 
= GetLineCount(); 
1312         return GetLineForVisibleLineNumber(lineCount
-1); 
1317 /// Get the line at the given y pixel position, or the last line. 
1318 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const 
1320     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1323         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1324         wxASSERT (child 
!= NULL
); 
1326         wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
1329             wxRichTextLine
* line 
= node2
->GetData(); 
1331             wxRect 
rect(line
->GetRect()); 
1333             if (y 
<= rect
.GetBottom()) 
1336             node2 
= node2
->GetNext(); 
1339         node 
= node
->GetNext(); 
1343     int lineCount 
= GetLineCount(); 
1345         return GetLineForVisibleLineNumber(lineCount
-1); 
1350 /// Get the number of visible lines 
1351 int wxRichTextParagraphLayoutBox::GetLineCount() const 
1355     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1358         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1359         wxASSERT (child 
!= NULL
); 
1361         count 
+= child
->GetLines().GetCount(); 
1362         node 
= node
->GetNext(); 
1368 /// Get the paragraph for a given line 
1369 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const 
1371     return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart()); 
1374 /// Get the line size at the given position 
1375 wxSize 
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const 
1377     wxRichTextLine
* line 
= GetLineAtPosition(pos
, caretPosition
); 
1380         return line
->GetSize(); 
1383         return wxSize(0, 0); 
1387 /// Convenience function to add a paragraph of text 
1388 wxRichTextRange 
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxTextAttr
* paraStyle
) 
1390     // Don't use the base style, just the default style, and the base style will 
1391     // be combined at display time. 
1392     // Divide into paragraph and character styles. 
1394     wxTextAttr defaultCharStyle
; 
1395     wxTextAttr defaultParaStyle
; 
1397     // If the default style is a named paragraph style, don't apply any character formatting 
1398     // to the initial text string. 
1399     if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet()) 
1401         wxRichTextParagraphStyleDefinition
* def 
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName()); 
1403             defaultParaStyle 
= def
->GetStyleMergedWithBase(GetStyleSheet()); 
1406         wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
); 
1408     wxTextAttr
* pStyle 
= paraStyle 
? paraStyle 
: (wxTextAttr
*) & defaultParaStyle
; 
1409     wxTextAttr
* cStyle 
= & defaultCharStyle
; 
1411     wxRichTextParagraph
* para 
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
); 
1418     return para
->GetRange(); 
1421 /// Adds multiple paragraphs, based on newlines. 
1422 wxRichTextRange 
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxTextAttr
* paraStyle
) 
1424     // Don't use the base style, just the default style, and the base style will 
1425     // be combined at display time. 
1426     // Divide into paragraph and character styles. 
1428     wxTextAttr defaultCharStyle
; 
1429     wxTextAttr defaultParaStyle
; 
1431     // If the default style is a named paragraph style, don't apply any character formatting 
1432     // to the initial text string. 
1433     if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet()) 
1435         wxRichTextParagraphStyleDefinition
* def 
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName()); 
1437             defaultParaStyle 
= def
->GetStyleMergedWithBase(GetStyleSheet()); 
1440         wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
); 
1442     wxTextAttr
* pStyle 
= paraStyle 
? paraStyle 
: (wxTextAttr
*) & defaultParaStyle
; 
1443     wxTextAttr
* cStyle 
= & defaultCharStyle
; 
1445     wxRichTextParagraph
* firstPara 
= NULL
; 
1446     wxRichTextParagraph
* lastPara 
= NULL
; 
1448     wxRichTextRange 
range(-1, -1); 
1451     size_t len 
= text
.length(); 
1453     wxRichTextParagraph
* para 
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
); 
1462         wxChar ch 
= text
[i
]; 
1463         if (ch 
== wxT('\n') || ch 
== wxT('\r')) 
1467                 wxRichTextPlainText
* plainText 
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData(); 
1468                 plainText
->SetText(line
); 
1470                 para 
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
); 
1475                 line 
= wxEmptyString
; 
1486         wxRichTextPlainText
* plainText 
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData(); 
1487         plainText
->SetText(line
); 
1494     return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd()); 
1497 /// Convenience function to add an image 
1498 wxRichTextRange 
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxTextAttr
* paraStyle
) 
1500     // Don't use the base style, just the default style, and the base style will 
1501     // be combined at display time. 
1502     // Divide into paragraph and character styles. 
1504     wxTextAttr defaultCharStyle
; 
1505     wxTextAttr defaultParaStyle
; 
1507     // If the default style is a named paragraph style, don't apply any character formatting 
1508     // to the initial text string. 
1509     if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet()) 
1511         wxRichTextParagraphStyleDefinition
* def 
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName()); 
1513             defaultParaStyle 
= def
->GetStyleMergedWithBase(GetStyleSheet()); 
1516         wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
); 
1518     wxTextAttr
* pStyle 
= paraStyle 
? paraStyle 
: (wxTextAttr
*) & defaultParaStyle
; 
1519     wxTextAttr
* cStyle 
= & defaultCharStyle
; 
1521     wxRichTextParagraph
* para 
= new wxRichTextParagraph(this, pStyle
); 
1523     para
->AppendChild(new wxRichTextImage(image
, this, cStyle
)); 
1528     return para
->GetRange(); 
1532 /// Insert fragment into this box at the given position. If partialParagraph is true, 
1533 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph 
1536 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
) 
1540     // First, find the first paragraph whose starting position is within the range. 
1541     wxRichTextParagraph
* para 
= GetParagraphAtPosition(position
); 
1544         wxTextAttrEx originalAttr 
= para
->GetAttributes(); 
1546         wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(para
); 
1548         // Now split at this position, returning the object to insert the new 
1549         // ones in front of. 
1550         wxRichTextObject
* nextObject 
= para
->SplitAt(position
); 
1552         // Special case: partial paragraph, just one paragraph. Might be a small amount of 
1553         // text, for example, so let's optimize. 
1555         if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1) 
1557             // Add the first para to this para... 
1558             wxRichTextObjectList::compatibility_iterator firstParaNode 
= fragment
.GetChildren().GetFirst(); 
1562             // Iterate through the fragment paragraph inserting the content into this paragraph. 
1563             wxRichTextParagraph
* firstPara 
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
); 
1564             wxASSERT (firstPara 
!= NULL
); 
1566             wxRichTextObjectList::compatibility_iterator objectNode 
= firstPara
->GetChildren().GetFirst(); 
1569                 wxRichTextObject
* newObj 
= objectNode
->GetData()->Clone(); 
1574                     para
->AppendChild(newObj
); 
1578                     // Insert before nextObject 
1579                     para
->InsertChild(newObj
, nextObject
); 
1582                 objectNode 
= objectNode
->GetNext(); 
1589             // Procedure for inserting a fragment consisting of a number of 
1592             // 1. Remove and save the content that's after the insertion point, for adding 
1593             //    back once we've added the fragment. 
1594             // 2. Add the content from the first fragment paragraph to the current 
1596             // 3. Add remaining fragment paragraphs after the current paragraph. 
1597             // 4. Add back the saved content from the first paragraph. If partialParagraph 
1598             //    is true, add it to the last paragraph added and not a new one. 
1600             // 1. Remove and save objects after split point. 
1601             wxList savedObjects
; 
1603                 para
->MoveToList(nextObject
, savedObjects
); 
1605             // 2. Add the content from the 1st fragment paragraph. 
1606             wxRichTextObjectList::compatibility_iterator firstParaNode 
= fragment
.GetChildren().GetFirst(); 
1610             wxRichTextParagraph
* firstPara 
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
); 
1611             wxASSERT(firstPara 
!= NULL
); 
1613             if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
)) 
1614                 para
->SetAttributes(firstPara
->GetAttributes()); 
1616             // Save empty paragraph attributes for appending later 
1617             // These are character attributes deliberately set for a new paragraph. Without this, 
1618             // we couldn't pass default attributes when appending a new paragraph. 
1619             wxTextAttrEx emptyParagraphAttributes
; 
1621             wxRichTextObjectList::compatibility_iterator objectNode 
= firstPara
->GetChildren().GetFirst(); 
1623             if (objectNode 
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty()) 
1624                 emptyParagraphAttributes 
= objectNode
->GetData()->GetAttributes(); 
1628                 wxRichTextObject
* newObj 
= objectNode
->GetData()->Clone(); 
1631                 para
->AppendChild(newObj
); 
1633                 objectNode 
= objectNode
->GetNext(); 
1636             // 3. Add remaining fragment paragraphs after the current paragraph. 
1637             wxRichTextObjectList::compatibility_iterator nextParagraphNode 
= node
->GetNext(); 
1638             wxRichTextObject
* nextParagraph 
= NULL
; 
1639             if (nextParagraphNode
) 
1640                 nextParagraph 
= nextParagraphNode
->GetData(); 
1642             wxRichTextObjectList::compatibility_iterator i 
= fragment
.GetChildren().GetFirst()->GetNext(); 
1643             wxRichTextParagraph
* finalPara 
= para
; 
1645             bool needExtraPara 
= (!i 
|| !fragment
.GetPartialParagraph()); 
1647             // If there was only one paragraph, we need to insert a new one. 
1650                 wxRichTextParagraph
* para 
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
); 
1651                 wxASSERT( para 
!= NULL 
); 
1653                 finalPara 
= (wxRichTextParagraph
*) para
->Clone(); 
1656                     InsertChild(finalPara
, nextParagraph
); 
1658                     AppendChild(finalPara
); 
1663             // If there was only one paragraph, or we have full paragraphs in our fragment, 
1664             // we need to insert a new one. 
1667                 finalPara 
= new wxRichTextParagraph
; 
1670                     InsertChild(finalPara
, nextParagraph
); 
1672                     AppendChild(finalPara
); 
1675             // 4. Add back the remaining content. 
1679                     finalPara
->MoveFromList(savedObjects
); 
1681                 // Ensure there's at least one object 
1682                 if (finalPara
->GetChildCount() == 0) 
1684                     wxRichTextPlainText
* text 
= new wxRichTextPlainText(wxEmptyString
); 
1685                     text
->SetAttributes(emptyParagraphAttributes
); 
1687                     finalPara
->AppendChild(text
); 
1691             if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
) 
1692                 finalPara
->SetAttributes(firstPara
->GetAttributes()); 
1693             else if (finalPara 
&& finalPara 
!= para
) 
1694                 finalPara
->SetAttributes(originalAttr
); 
1702         wxRichTextObjectList::compatibility_iterator i 
= fragment
.GetChildren().GetFirst(); 
1705             wxRichTextParagraph
* para 
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
); 
1706             wxASSERT( para 
!= NULL 
); 
1708             AppendChild(para
->Clone()); 
1717 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'. 
1718 /// If there was an incomplete paragraph at the end, partialParagraph is set to true. 
1719 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
) 
1721     wxRichTextObjectList::compatibility_iterator i 
= GetChildren().GetFirst(); 
1724         wxRichTextParagraph
* para 
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
); 
1725         wxASSERT( para 
!= NULL 
); 
1727         if (!para
->GetRange().IsOutside(range
)) 
1729             fragment
.AppendChild(para
->Clone()); 
1734     // Now top and tail the first and last paragraphs in our new fragment (which might be the same). 
1735     if (!fragment
.IsEmpty()) 
1737         wxRichTextRange 
topTailRange(range
); 
1739         wxRichTextParagraph
* firstPara 
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
); 
1740         wxASSERT( firstPara 
!= NULL 
); 
1742         // Chop off the start of the paragraph 
1743         if (topTailRange
.GetStart() > firstPara
->GetRange().GetStart()) 
1745             wxRichTextRange 
r(firstPara
->GetRange().GetStart(), topTailRange
.GetStart()-1); 
1746             firstPara
->DeleteRange(r
); 
1748             // Make sure the numbering is correct 
1750             fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
); 
1752             // Now, we've deleted some positions, so adjust the range 
1754             topTailRange
.SetEnd(topTailRange
.GetEnd() - r
.GetLength()); 
1757         wxRichTextParagraph
* lastPara 
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
); 
1758         wxASSERT( lastPara 
!= NULL 
); 
1760         if (topTailRange
.GetEnd() < (lastPara
->GetRange().GetEnd()-1)) 
1762             wxRichTextRange 
r(topTailRange
.GetEnd()+1, lastPara
->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */ 
1763             lastPara
->DeleteRange(r
); 
1765             // Make sure the numbering is correct 
1767             fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
); 
1769             // We only have part of a paragraph at the end 
1770             fragment
.SetPartialParagraph(true); 
1774             if (topTailRange
.GetEnd() == (lastPara
->GetRange().GetEnd() - 1)) 
1775                 // We have a partial paragraph (don't save last new paragraph marker) 
1776                 fragment
.SetPartialParagraph(true); 
1778                 // We have a complete paragraph 
1779                 fragment
.SetPartialParagraph(false); 
1786 /// Given a position, get the number of the visible line (potentially many to a paragraph), 
1787 /// starting from zero at the start of the buffer. 
1788 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const 
1795     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1798         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1799         wxASSERT( child 
!= NULL 
); 
1801         if (child
->GetRange().Contains(pos
)) 
1803             wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
1806                 wxRichTextLine
* line 
= node2
->GetData(); 
1807                 wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
1809                 if (lineRange
.Contains(pos
) || pos 
== lineRange
.GetStart()) 
1811                     // If the caret is displayed at the end of the previous wrapped line, 
1812                     // we want to return the line it's _displayed_ at (not the actual line 
1813                     // containing the position). 
1814                     if (lineRange
.GetStart() == pos 
&& !startOfLine 
&& child
->GetRange().GetStart() != pos
) 
1815                         return lineCount 
- 1; 
1822                 node2 
= node2
->GetNext(); 
1824             // If we didn't find it in the lines, it must be 
1825             // the last position of the paragraph. So return the last line. 
1829             lineCount 
+= child
->GetLines().GetCount(); 
1831         node 
= node
->GetNext(); 
1838 /// Given a line number, get the corresponding wxRichTextLine object. 
1839 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const 
1843     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1846         wxRichTextParagraph
* child 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1847         wxASSERT(child 
!= NULL
); 
1849         if (lineNumber 
< (int) (child
->GetLines().GetCount() + lineCount
)) 
1851             wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
1854                 wxRichTextLine
* line 
= node2
->GetData(); 
1856                 if (lineCount 
== lineNumber
) 
1861                 node2 
= node2
->GetNext(); 
1865             lineCount 
+= child
->GetLines().GetCount(); 
1867         node 
= node
->GetNext(); 
1874 /// Delete range from layout. 
1875 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
) 
1877     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1879     wxRichTextParagraph
* firstPara 
= NULL
; 
1882         wxRichTextParagraph
* obj 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
1883         wxASSERT (obj 
!= NULL
); 
1885         wxRichTextObjectList::compatibility_iterator next 
= node
->GetNext(); 
1887         // Delete the range in each paragraph 
1889         if (!obj
->GetRange().IsOutside(range
)) 
1891             // Deletes the content of this object within the given range 
1892             obj
->DeleteRange(range
); 
1894             wxRichTextRange thisRange 
= obj
->GetRange(); 
1895             wxTextAttrEx thisAttr 
= obj
->GetAttributes(); 
1897             // If the whole paragraph is within the range to delete, 
1898             // delete the whole thing. 
1899             if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd()) 
1901                 // Delete the whole object 
1902                 RemoveChild(obj
, true); 
1905             else if (!firstPara
) 
1908             // If the range includes the paragraph end, we need to join this 
1909             // and the next paragraph. 
1910             if (range
.GetEnd() <= thisRange
.GetEnd()) 
1912                 // We need to move the objects from the next paragraph 
1913                 // to this paragraph 
1915                 wxRichTextParagraph
* nextParagraph 
= NULL
; 
1916                 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
) 
1917                     nextParagraph 
= obj
; 
1920                     // We're ending at the end of the paragraph, so merge the _next_ paragraph. 
1922                         nextParagraph 
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
); 
1925                 bool applyFinalParagraphStyle 
= firstPara 
&& nextParagraph 
&& nextParagraph 
!= firstPara
; 
1927                 wxTextAttrEx nextParaAttr
; 
1928                 if (applyFinalParagraphStyle
) 
1930                     // Special case when deleting the end of a paragraph - use _this_ paragraph's style, 
1931                     // not the next one. 
1932                     if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd()) 
1933                         nextParaAttr 
= thisAttr
; 
1935                         nextParaAttr 
= nextParagraph
->GetAttributes(); 
1938                 if (firstPara 
&& nextParagraph 
&& firstPara 
!= nextParagraph
) 
1940                     // Move the objects to the previous para 
1941                     wxRichTextObjectList::compatibility_iterator node1 
= nextParagraph
->GetChildren().GetFirst(); 
1945                         wxRichTextObject
* obj1 
= node1
->GetData(); 
1947                         firstPara
->AppendChild(obj1
); 
1949                         wxRichTextObjectList::compatibility_iterator next1 
= node1
->GetNext(); 
1950                         nextParagraph
->GetChildren().Erase(node1
); 
1955                     // Delete the paragraph 
1956                     RemoveChild(nextParagraph
, true); 
1959                 // Avoid empty paragraphs 
1960                 if (firstPara 
&& firstPara
->GetChildren().GetCount() == 0) 
1962                     wxRichTextPlainText
* text 
= new wxRichTextPlainText(wxEmptyString
); 
1963                     firstPara
->AppendChild(text
); 
1966                 if (applyFinalParagraphStyle
) 
1967                     firstPara
->SetAttributes(nextParaAttr
); 
1979 /// Get any text in this object for the given range 
1980 wxString 
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const 
1984     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
1987         wxRichTextObject
* child 
= node
->GetData(); 
1988         if (!child
->GetRange().IsOutside(range
)) 
1990             wxRichTextRange childRange 
= range
; 
1991             childRange
.LimitTo(child
->GetRange()); 
1993             wxString childText 
= child
->GetTextForRange(childRange
); 
1997             if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext()) 
2002         node 
= node
->GetNext(); 
2008 /// Get all the text 
2009 wxString 
wxRichTextParagraphLayoutBox::GetText() const 
2011     return GetTextForRange(GetRange()); 
2014 /// Get the paragraph by number 
2015 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const 
2017     if ((size_t) paragraphNumber 
>= GetChildCount()) 
2020     return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
); 
2023 /// Get the length of the paragraph 
2024 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const 
2026     wxRichTextParagraph
* para 
= GetParagraphAtLine(paragraphNumber
); 
2028         return para
->GetRange().GetLength() - 1; // don't include newline 
2033 /// Get the text of the paragraph 
2034 wxString 
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const 
2036     wxRichTextParagraph
* para 
= GetParagraphAtLine(paragraphNumber
); 
2038         return para
->GetTextForRange(para
->GetRange()); 
2040         return wxEmptyString
; 
2043 /// Convert zero-based line column and paragraph number to a position. 
2044 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const 
2046     wxRichTextParagraph
* para 
= GetParagraphAtLine(y
); 
2049         return para
->GetRange().GetStart() + x
; 
2055 /// Convert zero-based position to line column and paragraph number 
2056 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const 
2058     wxRichTextParagraph
* para 
= GetParagraphAtPosition(pos
); 
2062         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2065             wxRichTextObject
* child 
= node
->GetData(); 
2069             node 
= node
->GetNext(); 
2073         *x 
= pos 
- para
->GetRange().GetStart(); 
2081 /// Get the leaf object in a paragraph at this position. 
2082 /// Given a line number, get the corresponding wxRichTextLine object. 
2083 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const 
2085     wxRichTextParagraph
* para 
= GetParagraphAtPosition(position
); 
2088         wxRichTextObjectList::compatibility_iterator node 
= para
->GetChildren().GetFirst(); 
2092             wxRichTextObject
* child 
= node
->GetData(); 
2093             if (child
->GetRange().Contains(position
)) 
2096             node 
= node
->GetNext(); 
2098         if (position 
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0) 
2099             return para
->GetChildren().GetLast()->GetData(); 
2104 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes 
2105 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxTextAttr
& style
, int flags
) 
2107     bool characterStyle 
= false; 
2108     bool paragraphStyle 
= false; 
2110     if (style
.IsCharacterStyle()) 
2111         characterStyle 
= true; 
2112     if (style
.IsParagraphStyle()) 
2113         paragraphStyle 
= true; 
2115     bool withUndo 
= ((flags 
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0); 
2116     bool applyMinimal 
= ((flags 
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0); 
2117     bool parasOnly 
= ((flags 
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0); 
2118     bool charactersOnly 
= ((flags 
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0); 
2119     bool resetExistingStyle 
= ((flags 
& wxRICHTEXT_SETSTYLE_RESET
) != 0); 
2120     bool removeStyle 
= ((flags 
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0); 
2122     // Apply paragraph style first, if any 
2123     wxTextAttr 
wholeStyle(style
); 
2125     if (!removeStyle 
&& wholeStyle
.HasParagraphStyleName() && GetStyleSheet()) 
2127         wxRichTextParagraphStyleDefinition
* def 
= GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName()); 
2129             wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(GetStyleSheet())); 
2132     // Limit the attributes to be set to the content to only character attributes. 
2133     wxTextAttr 
characterAttributes(wholeStyle
); 
2134     characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
)); 
2136     if (!removeStyle 
&& characterAttributes
.HasCharacterStyleName() && GetStyleSheet()) 
2138         wxRichTextCharacterStyleDefinition
* def 
= GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName()); 
2140             wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(GetStyleSheet())); 
2143     // If we are associated with a control, make undoable; otherwise, apply immediately 
2146     bool haveControl 
= (GetRichTextCtrl() != NULL
); 
2148     wxRichTextAction
* action 
= NULL
; 
2150     if (haveControl 
&& withUndo
) 
2152         action 
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl()); 
2153         action
->SetRange(range
); 
2154         action
->SetPosition(GetRichTextCtrl()->GetCaretPosition()); 
2157     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2160         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
2161         wxASSERT (para 
!= NULL
); 
2163         if (para 
&& para
->GetChildCount() > 0) 
2165             // Stop searching if we're beyond the range of interest 
2166             if (para
->GetRange().GetStart() > range
.GetEnd()) 
2169             if (!para
->GetRange().IsOutside(range
)) 
2171                 // We'll be using a copy of the paragraph to make style changes, 
2172                 // not updating the buffer directly. 
2173                 wxRichTextParagraph
* newPara 
wxDUMMY_INITIALIZE(NULL
); 
2175                 if (haveControl 
&& withUndo
) 
2177                     newPara 
= new wxRichTextParagraph(*para
); 
2178                     action
->GetNewParagraphs().AppendChild(newPara
); 
2180                     // Also store the old ones for Undo 
2181                     action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
)); 
2186                 // If we're specifying paragraphs only, then we really mean character formatting 
2187                 // to be included in the paragraph style 
2188                 if ((paragraphStyle 
|| parasOnly
) && !charactersOnly
) 
2192                         // Removes the given style from the paragraph 
2193                         wxRichTextRemoveStyle(newPara
->GetAttributes(), style
); 
2195                     else if (resetExistingStyle
) 
2196                         newPara
->GetAttributes() = wholeStyle
; 
2201                             // Only apply attributes that will make a difference to the combined 
2202                             // style as seen on the display 
2203                             wxTextAttr 
combinedAttr(para
->GetCombinedAttributes()); 
2204                             wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
); 
2207                             wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
); 
2211                 // When applying paragraph styles dynamically, don't change the text objects' attributes 
2212                 // since they will computed as needed. Only apply the character styling if it's _only_ 
2213                 // character styling. This policy is subject to change and might be put under user control. 
2215                 // Hm. we might well be applying a mix of paragraph and character styles, in which 
2216                 // case we _do_ want to apply character styles regardless of what para styles are set. 
2217                 // But if we're applying a paragraph style, which has some character attributes, but 
2218                 // we only want the paragraphs to hold this character style, then we _don't_ want to 
2219                 // apply the character style. So we need to be able to choose. 
2221                 if (!parasOnly 
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd()) 
2223                     wxRichTextRange 
childRange(range
); 
2224                     childRange
.LimitTo(newPara
->GetRange()); 
2226                     // Find the starting position and if necessary split it so 
2227                     // we can start applying a different style. 
2228                     // TODO: check that the style actually changes or is different 
2229                     // from style outside of range 
2230                     wxRichTextObject
* firstObject 
wxDUMMY_INITIALIZE(NULL
); 
2231                     wxRichTextObject
* lastObject 
wxDUMMY_INITIALIZE(NULL
); 
2233                     if (childRange
.GetStart() == newPara
->GetRange().GetStart()) 
2234                         firstObject 
= newPara
->GetChildren().GetFirst()->GetData(); 
2236                         firstObject 
= newPara
->SplitAt(range
.GetStart()); 
2238                     // Increment by 1 because we're apply the style one _after_ the split point 
2239                     long splitPoint 
= childRange
.GetEnd(); 
2240                     if (splitPoint 
!= newPara
->GetRange().GetEnd()) 
2244                     if (splitPoint 
== newPara
->GetRange().GetEnd()) 
2245                         lastObject 
= newPara
->GetChildren().GetLast()->GetData(); 
2247                         // lastObject is set as a side-effect of splitting. It's 
2248                         // returned as the object before the new object. 
2249                         (void) newPara
->SplitAt(splitPoint
, & lastObject
); 
2251                     wxASSERT(firstObject 
!= NULL
); 
2252                     wxASSERT(lastObject 
!= NULL
); 
2254                     if (!firstObject 
|| !lastObject
) 
2257                     wxRichTextObjectList::compatibility_iterator firstNode 
= newPara
->GetChildren().Find(firstObject
); 
2258                     wxRichTextObjectList::compatibility_iterator lastNode 
= newPara
->GetChildren().Find(lastObject
); 
2260                     wxASSERT(firstNode
); 
2263                     wxRichTextObjectList::compatibility_iterator node2 
= firstNode
; 
2267                         wxRichTextObject
* child 
= node2
->GetData(); 
2271                             // Removes the given style from the paragraph 
2272                             wxRichTextRemoveStyle(child
->GetAttributes(), style
); 
2274                         else if (resetExistingStyle
) 
2275                             child
->GetAttributes() = characterAttributes
; 
2280                                 // Only apply attributes that will make a difference to the combined 
2281                                 // style as seen on the display 
2282                                 wxTextAttr 
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes())); 
2283                                 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
); 
2286                                 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
); 
2289                         if (node2 
== lastNode
) 
2292                         node2 
= node2
->GetNext(); 
2298         node 
= node
->GetNext(); 
2301     // Do action, or delay it until end of batch. 
2302     if (haveControl 
&& withUndo
) 
2303         GetRichTextCtrl()->GetBuffer().SubmitAction(action
); 
2308 void wxRichTextParagraphLayoutBox::SetImageStyle(wxRichTextImage 
*image
, const wxRichTextAnchoredObjectAttr
& attr
, int flags
) 
2310     bool withUndo 
= flags 
& wxRICHTEXT_SETSTYLE_WITH_UNDO
; 
2311     bool haveControl 
= (GetRichTextCtrl() != NULL
); 
2312     wxRichTextParagraph
* newPara 
wxDUMMY_INITIALIZE(NULL
); 
2313     wxRichTextParagraph
* para 
= GetParagraphAtPosition(image
->GetRange().GetStart()); 
2314     wxRichTextAction 
*action 
= NULL
; 
2315     wxRichTextAnchoredObjectAttr oldAttr 
= image
->GetAnchoredAttr(); 
2317     if (haveControl 
&& withUndo
) 
2319         action 
= new wxRichTextAction(NULL
, _("Change Image Style"), wxRICHTEXT_CHANGE_STYLE
, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl()); 
2320         action
->SetRange(image
->GetRange().FromInternal()); 
2321         action
->SetPosition(GetRichTextCtrl()->GetCaretPosition()); 
2322         image
->SetAnchoredAttr(attr
); 
2323         // Set the new attribute 
2324         newPara 
= new wxRichTextParagraph(*para
); 
2325         action
->GetNewParagraphs().AppendChild(newPara
); 
2326         // Change back to the old one 
2327         image
->SetAnchoredAttr(oldAttr
); 
2328         action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
)); 
2333     if (haveControl 
&& withUndo
) 
2334         GetRichTextCtrl()->GetBuffer().SubmitAction(action
); 
2337 /// Get the text attributes for this position. 
2338 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxTextAttr
& style
) 
2340     return DoGetStyle(position
, style
, true); 
2343 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxTextAttr
& style
) 
2345     return DoGetStyle(position
, style
, false); 
2348 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and 
2349 /// context attributes. 
2350 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxTextAttr
& style
, bool combineStyles
) 
2352     wxRichTextObject
* obj 
wxDUMMY_INITIALIZE(NULL
); 
2354     if (style
.IsParagraphStyle()) 
2356         obj 
= GetParagraphAtPosition(position
); 
2361                 // Start with the base style 
2362                 style 
= GetAttributes(); 
2364                 // Apply the paragraph style 
2365                 wxRichTextApplyStyle(style
, obj
->GetAttributes()); 
2368                 style 
= obj
->GetAttributes(); 
2375         obj 
= GetLeafObjectAtPosition(position
); 
2380                 wxRichTextParagraph
* para 
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
); 
2381                 style 
= para 
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes(); 
2384                 style 
= obj
->GetAttributes(); 
2392 static bool wxHasStyle(long flags
, long style
) 
2394     return (flags 
& style
) != 0; 
2397 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of 
2399 bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttr
& currentStyle
, const wxTextAttr
& style
, long& multipleStyleAttributes
, int& multipleTextEffectAttributes
, int& absentStyleAttributes
, int& absentTextEffectAttributes
) 
2401     absentStyleAttributes 
|= (~style
.GetFlags() & wxTEXT_ATTR_ALL
); 
2402     absentTextEffectAttributes 
|= (~style
.GetTextEffectFlags() & 0xFFFF); 
2404     if (style
.HasFont()) 
2406         if (style
.HasFontSize() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_FONT_SIZE
)) 
2408             if (currentStyle
.HasFontSize()) 
2410                 if (currentStyle
.GetFontSize() != style
.GetFontSize()) 
2412                     // Clash of style - mark as such 
2413                     multipleStyleAttributes 
|= wxTEXT_ATTR_FONT_SIZE
; 
2414                     currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_FONT_SIZE
); 
2419                 currentStyle
.SetFontSize(style
.GetFontSize()); 
2423         if (style
.HasFontItalic() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_FONT_ITALIC
)) 
2425             if (currentStyle
.HasFontItalic()) 
2427                 if (currentStyle
.GetFontStyle() != style
.GetFontStyle()) 
2429                     // Clash of style - mark as such 
2430                     multipleStyleAttributes 
|= wxTEXT_ATTR_FONT_ITALIC
; 
2431                     currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_FONT_ITALIC
); 
2436                 currentStyle
.SetFontStyle(style
.GetFontStyle()); 
2440         if (style
.HasFontFamily() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_FONT_FAMILY
)) 
2442             if (currentStyle
.HasFontFamily()) 
2444                 if (currentStyle
.GetFontFamily() != style
.GetFontFamily()) 
2446                     // Clash of style - mark as such 
2447                     multipleStyleAttributes 
|= wxTEXT_ATTR_FONT_FAMILY
; 
2448                     currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_FONT_FAMILY
); 
2453                 currentStyle
.SetFontFamily(style
.GetFontFamily()); 
2457         if (style
.HasFontWeight() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_FONT_WEIGHT
)) 
2459             if (currentStyle
.HasFontWeight()) 
2461                 if (currentStyle
.GetFontWeight() != style
.GetFontWeight()) 
2463                     // Clash of style - mark as such 
2464                     multipleStyleAttributes 
|= wxTEXT_ATTR_FONT_WEIGHT
; 
2465                     currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_FONT_WEIGHT
); 
2470                 currentStyle
.SetFontWeight(style
.GetFontWeight()); 
2474         if (style
.HasFontFaceName() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_FONT_FACE
)) 
2476             if (currentStyle
.HasFontFaceName()) 
2478                 wxString 
faceName1(currentStyle
.GetFontFaceName()); 
2479                 wxString 
faceName2(style
.GetFontFaceName()); 
2481                 if (faceName1 
!= faceName2
) 
2483                     // Clash of style - mark as such 
2484                     multipleStyleAttributes 
|= wxTEXT_ATTR_FONT_FACE
; 
2485                     currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_FONT_FACE
); 
2490                 currentStyle
.SetFontFaceName(style
.GetFontFaceName()); 
2494         if (style
.HasFontUnderlined() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_FONT_UNDERLINE
)) 
2496             if (currentStyle
.HasFontUnderlined()) 
2498                 if (currentStyle
.GetFontUnderlined() != style
.GetFontUnderlined()) 
2500                     // Clash of style - mark as such 
2501                     multipleStyleAttributes 
|= wxTEXT_ATTR_FONT_UNDERLINE
; 
2502                     currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_FONT_UNDERLINE
); 
2507                 currentStyle
.SetFontUnderlined(style
.GetFontUnderlined()); 
2512     if (style
.HasTextColour() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_TEXT_COLOUR
)) 
2514         if (currentStyle
.HasTextColour()) 
2516             if (currentStyle
.GetTextColour() != style
.GetTextColour()) 
2518                 // Clash of style - mark as such 
2519                 multipleStyleAttributes 
|= wxTEXT_ATTR_TEXT_COLOUR
; 
2520                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_TEXT_COLOUR
); 
2524             currentStyle
.SetTextColour(style
.GetTextColour()); 
2527     if (style
.HasBackgroundColour() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_BACKGROUND_COLOUR
)) 
2529         if (currentStyle
.HasBackgroundColour()) 
2531             if (currentStyle
.GetBackgroundColour() != style
.GetBackgroundColour()) 
2533                 // Clash of style - mark as such 
2534                 multipleStyleAttributes 
|= wxTEXT_ATTR_BACKGROUND_COLOUR
; 
2535                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
); 
2539             currentStyle
.SetBackgroundColour(style
.GetBackgroundColour()); 
2542     if (style
.HasAlignment() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_ALIGNMENT
)) 
2544         if (currentStyle
.HasAlignment()) 
2546             if (currentStyle
.GetAlignment() != style
.GetAlignment()) 
2548                 // Clash of style - mark as such 
2549                 multipleStyleAttributes 
|= wxTEXT_ATTR_ALIGNMENT
; 
2550                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_ALIGNMENT
); 
2554             currentStyle
.SetAlignment(style
.GetAlignment()); 
2557     if (style
.HasTabs() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_TABS
)) 
2559         if (currentStyle
.HasTabs()) 
2561             if (!wxRichTextTabsEq(currentStyle
.GetTabs(), style
.GetTabs())) 
2563                 // Clash of style - mark as such 
2564                 multipleStyleAttributes 
|= wxTEXT_ATTR_TABS
; 
2565                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_TABS
); 
2569             currentStyle
.SetTabs(style
.GetTabs()); 
2572     if (style
.HasLeftIndent() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_LEFT_INDENT
)) 
2574         if (currentStyle
.HasLeftIndent()) 
2576             if (currentStyle
.GetLeftIndent() != style
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != style
.GetLeftSubIndent()) 
2578                 // Clash of style - mark as such 
2579                 multipleStyleAttributes 
|= wxTEXT_ATTR_LEFT_INDENT
; 
2580                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT
); 
2584             currentStyle
.SetLeftIndent(style
.GetLeftIndent(), style
.GetLeftSubIndent()); 
2587     if (style
.HasRightIndent() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_RIGHT_INDENT
)) 
2589         if (currentStyle
.HasRightIndent()) 
2591             if (currentStyle
.GetRightIndent() != style
.GetRightIndent()) 
2593                 // Clash of style - mark as such 
2594                 multipleStyleAttributes 
|= wxTEXT_ATTR_RIGHT_INDENT
; 
2595                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_RIGHT_INDENT
); 
2599             currentStyle
.SetRightIndent(style
.GetRightIndent()); 
2602     if (style
.HasParagraphSpacingAfter() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_PARA_SPACING_AFTER
)) 
2604         if (currentStyle
.HasParagraphSpacingAfter()) 
2606             if (currentStyle
.GetParagraphSpacingAfter() != style
.GetParagraphSpacingAfter()) 
2608                 // Clash of style - mark as such 
2609                 multipleStyleAttributes 
|= wxTEXT_ATTR_PARA_SPACING_AFTER
; 
2610                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_AFTER
); 
2614             currentStyle
.SetParagraphSpacingAfter(style
.GetParagraphSpacingAfter()); 
2617     if (style
.HasParagraphSpacingBefore() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_PARA_SPACING_BEFORE
)) 
2619         if (currentStyle
.HasParagraphSpacingBefore()) 
2621             if (currentStyle
.GetParagraphSpacingBefore() != style
.GetParagraphSpacingBefore()) 
2623                 // Clash of style - mark as such 
2624                 multipleStyleAttributes 
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
; 
2625                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_BEFORE
); 
2629             currentStyle
.SetParagraphSpacingBefore(style
.GetParagraphSpacingBefore()); 
2632     if (style
.HasLineSpacing() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_LINE_SPACING
)) 
2634         if (currentStyle
.HasLineSpacing()) 
2636             if (currentStyle
.GetLineSpacing() != style
.GetLineSpacing()) 
2638                 // Clash of style - mark as such 
2639                 multipleStyleAttributes 
|= wxTEXT_ATTR_LINE_SPACING
; 
2640                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_LINE_SPACING
); 
2644             currentStyle
.SetLineSpacing(style
.GetLineSpacing()); 
2647     if (style
.HasCharacterStyleName() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
)) 
2649         if (currentStyle
.HasCharacterStyleName()) 
2651             if (currentStyle
.GetCharacterStyleName() != style
.GetCharacterStyleName()) 
2653                 // Clash of style - mark as such 
2654                 multipleStyleAttributes 
|= wxTEXT_ATTR_CHARACTER_STYLE_NAME
; 
2655                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_CHARACTER_STYLE_NAME
); 
2659             currentStyle
.SetCharacterStyleName(style
.GetCharacterStyleName()); 
2662     if (style
.HasParagraphStyleName() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
)) 
2664         if (currentStyle
.HasParagraphStyleName()) 
2666             if (currentStyle
.GetParagraphStyleName() != style
.GetParagraphStyleName()) 
2668                 // Clash of style - mark as such 
2669                 multipleStyleAttributes 
|= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
; 
2670                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
); 
2674             currentStyle
.SetParagraphStyleName(style
.GetParagraphStyleName()); 
2677     if (style
.HasListStyleName() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_LIST_STYLE_NAME
)) 
2679         if (currentStyle
.HasListStyleName()) 
2681             if (currentStyle
.GetListStyleName() != style
.GetListStyleName()) 
2683                 // Clash of style - mark as such 
2684                 multipleStyleAttributes 
|= wxTEXT_ATTR_LIST_STYLE_NAME
; 
2685                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_LIST_STYLE_NAME
); 
2689             currentStyle
.SetListStyleName(style
.GetListStyleName()); 
2692     if (style
.HasBulletStyle() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_BULLET_STYLE
)) 
2694         if (currentStyle
.HasBulletStyle()) 
2696             if (currentStyle
.GetBulletStyle() != style
.GetBulletStyle()) 
2698                 // Clash of style - mark as such 
2699                 multipleStyleAttributes 
|= wxTEXT_ATTR_BULLET_STYLE
; 
2700                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_BULLET_STYLE
); 
2704             currentStyle
.SetBulletStyle(style
.GetBulletStyle()); 
2707     if (style
.HasBulletNumber() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_BULLET_NUMBER
)) 
2709         if (currentStyle
.HasBulletNumber()) 
2711             if (currentStyle
.GetBulletNumber() != style
.GetBulletNumber()) 
2713                 // Clash of style - mark as such 
2714                 multipleStyleAttributes 
|= wxTEXT_ATTR_BULLET_NUMBER
; 
2715                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_BULLET_NUMBER
); 
2719             currentStyle
.SetBulletNumber(style
.GetBulletNumber()); 
2722     if (style
.HasBulletText() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_BULLET_TEXT
)) 
2724         if (currentStyle
.HasBulletText()) 
2726             if (currentStyle
.GetBulletText() != style
.GetBulletText()) 
2728                 // Clash of style - mark as such 
2729                 multipleStyleAttributes 
|= wxTEXT_ATTR_BULLET_TEXT
; 
2730                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_BULLET_TEXT
); 
2735             currentStyle
.SetBulletText(style
.GetBulletText()); 
2736             currentStyle
.SetBulletFont(style
.GetBulletFont()); 
2740     if (style
.HasBulletName() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_BULLET_NAME
)) 
2742         if (currentStyle
.HasBulletName()) 
2744             if (currentStyle
.GetBulletName() != style
.GetBulletName()) 
2746                 // Clash of style - mark as such 
2747                 multipleStyleAttributes 
|= wxTEXT_ATTR_BULLET_NAME
; 
2748                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_BULLET_NAME
); 
2753             currentStyle
.SetBulletName(style
.GetBulletName()); 
2757     if (style
.HasURL() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_URL
)) 
2759         if (currentStyle
.HasURL()) 
2761             if (currentStyle
.GetURL() != style
.GetURL()) 
2763                 // Clash of style - mark as such 
2764                 multipleStyleAttributes 
|= wxTEXT_ATTR_URL
; 
2765                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_URL
); 
2770             currentStyle
.SetURL(style
.GetURL()); 
2774     if (style
.HasTextEffects() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_EFFECTS
)) 
2776         if (currentStyle
.HasTextEffects()) 
2778             // We need to find the bits in the new style that are different: 
2779             // just look at those bits that are specified by the new style. 
2781             // We need to remove the bits and flags that are not common between current style 
2782             // and new style. In so doing we need to take account of the styles absent from one or more of the 
2785             int currentRelevantTextEffects 
= currentStyle
.GetTextEffects() & style
.GetTextEffectFlags(); 
2786             int newRelevantTextEffects 
= style
.GetTextEffects() & style
.GetTextEffectFlags(); 
2788             if (currentRelevantTextEffects 
!= newRelevantTextEffects
) 
2790                 // Find the text effects that were different, using XOR 
2791                 int differentEffects 
= currentRelevantTextEffects 
^ newRelevantTextEffects
; 
2793                 // Clash of style - mark as such 
2794                 multipleTextEffectAttributes 
|= differentEffects
; 
2795                 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
); 
2800             currentStyle
.SetTextEffects(style
.GetTextEffects()); 
2801             currentStyle
.SetTextEffectFlags(style
.GetTextEffectFlags()); 
2804         // Mask out the flags and values that cannot be common because they were absent in one or more objecrs 
2805         // that we've looked at so far 
2806         currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentTextEffectAttributes
); 
2807         currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentTextEffectAttributes
); 
2809         if (currentStyle
.GetTextEffectFlags() == 0) 
2810             currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_EFFECTS
); 
2813     if (style
.HasOutlineLevel() && !wxHasStyle(multipleStyleAttributes
|absentStyleAttributes
, wxTEXT_ATTR_OUTLINE_LEVEL
)) 
2815         if (currentStyle
.HasOutlineLevel()) 
2817             if (currentStyle
.GetOutlineLevel() != style
.GetOutlineLevel()) 
2819                 // Clash of style - mark as such 
2820                 multipleStyleAttributes 
|= wxTEXT_ATTR_OUTLINE_LEVEL
; 
2821                 currentStyle
.SetFlags(currentStyle
.GetFlags() & ~wxTEXT_ATTR_OUTLINE_LEVEL
); 
2825             currentStyle
.SetOutlineLevel(style
.GetOutlineLevel()); 
2831 /// Get the combined style for a range - if any attribute is different within the range, 
2832 /// that attribute is not present within the flags. 
2833 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself 
2835 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxTextAttr
& style
) 
2837     style 
= wxTextAttr(); 
2839     // The attributes that aren't valid because of multiple styles within the range 
2840     long multipleStyleAttributes 
= 0; 
2841     int multipleTextEffectAttributes 
= 0; 
2843     int absentStyleAttributesPara 
= 0; 
2844     int absentStyleAttributesChar 
= 0; 
2845     int absentTextEffectAttributesPara 
= 0; 
2846     int absentTextEffectAttributesChar 
= 0; 
2848     wxRichTextObjectList::compatibility_iterator node 
= GetChildren().GetFirst(); 
2851         wxRichTextParagraph
* para 
= (wxRichTextParagraph
*) node
->GetData(); 
2852         if (!(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart())) 
2854             if (para
->GetChildren().GetCount() == 0) 
2856                 wxTextAttr paraStyle 
= para
->GetCombinedAttributes(); 
2858                 CollectStyle(style
, paraStyle
, multipleStyleAttributes
, multipleTextEffectAttributes
, absentStyleAttributesPara
, absentTextEffectAttributesPara
); 
2862                 wxRichTextRange 
paraRange(para
->GetRange()); 
2863                 paraRange
.LimitTo(range
); 
2865                 // First collect paragraph attributes only 
2866                 wxTextAttr paraStyle 
= para
->GetCombinedAttributes(); 
2867                 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
); 
2868                 CollectStyle(style
, paraStyle
, multipleStyleAttributes
, multipleTextEffectAttributes
, absentStyleAttributesPara
, absentTextEffectAttributesPara
); 
2870                 wxRichTextObjectList::compatibility_iterator childNode 
= para
->GetChildren().GetFirst(); 
2874                     wxRichTextObject
* child 
= childNode
->GetData(); 
2875                     if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart())) 
2877                         wxTextAttr childStyle 
= para
->GetCombinedAttributes(child
->GetAttributes()); 
2879                         // Now collect character attributes only 
2880                         childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
); 
2882                         CollectStyle(style
, childStyle
, multipleStyleAttributes
, multipleTextEffectAttributes
, absentStyleAttributesChar
, absentTextEffectAttributesChar
); 
2885                     childNode 
= childNode
->GetNext(); 
2889         node 
= node
->GetNext(); 
2894 /// Set default style 
2895 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttr
& style
) 
2897     m_defaultAttributes 
= style
; 
2901 /// Test if this whole range has character attributes of the specified kind. If any 
2902 /// of the attributes are different within the range, the test fails. You 
2903 /// can use this to implement, for example, bold button updating. style must have 
2904 /// flags indicating which attributes are of interest. 
2905 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxTextAttr
& style
) const 
2908     int matchingCount 
= 0; 
2910     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2913         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
2914         wxASSERT (para 
!= NULL
); 
2918             // Stop searching if we're beyond the range of interest 
2919             if (para
->GetRange().GetStart() > range
.GetEnd()) 
2920                 return foundCount 
== matchingCount 
&& foundCount 
!= 0; 
2922             if (!para
->GetRange().IsOutside(range
)) 
2924                 wxRichTextObjectList::compatibility_iterator node2 
= para
->GetChildren().GetFirst(); 
2928                     wxRichTextObject
* child 
= node2
->GetData(); 
2929                     // Allow for empty string if no buffer 
2930                     wxRichTextRange childRange 
= child
->GetRange(); 
2931                     if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1) 
2932                         childRange
.SetEnd(childRange
.GetEnd()+1); 
2934                     if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
))) 
2937                         wxTextAttr textAttr 
= para
->GetCombinedAttributes(child
->GetAttributes()); 
2939                         if (wxTextAttrEqPartial(textAttr
, style
, style
.GetFlags())) 
2943                     node2 
= node2
->GetNext(); 
2948         node 
= node
->GetNext(); 
2951     return foundCount 
== matchingCount 
&& foundCount 
!= 0; 
2954 /// Test if this whole range has paragraph attributes of the specified kind. If any 
2955 /// of the attributes are different within the range, the test fails. You 
2956 /// can use this to implement, for example, centering button updating. style must have 
2957 /// flags indicating which attributes are of interest. 
2958 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxTextAttr
& style
) const 
2961     int matchingCount 
= 0; 
2963     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
2966         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
2967         wxASSERT (para 
!= NULL
); 
2971             // Stop searching if we're beyond the range of interest 
2972             if (para
->GetRange().GetStart() > range
.GetEnd()) 
2973                 return foundCount 
== matchingCount 
&& foundCount 
!= 0; 
2975             if (!para
->GetRange().IsOutside(range
)) 
2977                 wxTextAttr textAttr 
= GetAttributes(); 
2978                 // Apply the paragraph style 
2979                 wxRichTextApplyStyle(textAttr
, para
->GetAttributes()); 
2982                 if (wxTextAttrEqPartial(textAttr
, style
, style
.GetFlags())) 
2987         node 
= node
->GetNext(); 
2989     return foundCount 
== matchingCount 
&& foundCount 
!= 0; 
2992 void wxRichTextParagraphLayoutBox::Clear() 
2997 void wxRichTextParagraphLayoutBox::Reset() 
3001     wxRichTextBuffer
* buffer 
= wxDynamicCast(this, wxRichTextBuffer
); 
3002     if (buffer 
&& GetRichTextCtrl()) 
3004         wxRichTextEvent 
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, GetRichTextCtrl()->GetId()); 
3005         event
.SetEventObject(GetRichTextCtrl()); 
3007         buffer
->SendEvent(event
, true); 
3010     AddParagraph(wxEmptyString
); 
3012     Invalidate(wxRICHTEXT_ALL
); 
3015 /// Invalidate the buffer. With no argument, invalidates whole buffer. 
3016 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
) 
3020     if (invalidRange 
== wxRICHTEXT_ALL
) 
3022         m_invalidRange 
= wxRICHTEXT_ALL
; 
3026     // Already invalidating everything 
3027     if (m_invalidRange 
== wxRICHTEXT_ALL
) 
3030     if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1) 
3031         m_invalidRange
.SetStart(invalidRange
.GetStart()); 
3032     if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd()) 
3033         m_invalidRange
.SetEnd(invalidRange
.GetEnd()); 
3036 /// Get invalid range, rounding to entire paragraphs if argument is true. 
3037 wxRichTextRange 
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const 
3039     if (m_invalidRange 
== wxRICHTEXT_ALL 
|| m_invalidRange 
== wxRICHTEXT_NONE
) 
3040         return m_invalidRange
; 
3042     wxRichTextRange range 
= m_invalidRange
; 
3044     if (wholeParagraphs
) 
3046         wxRichTextParagraph
* para1 
= GetParagraphAtPosition(range
.GetStart()); 
3048             range
.SetStart(para1
->GetRange().GetStart()); 
3049         // floating layout make all child should be relayout 
3050         range
.SetEnd(GetRange().GetEnd()); 
3055 /// Apply the style sheet to the buffer, for example if the styles have changed. 
3056 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
) 
3058     wxASSERT(styleSheet 
!= NULL
); 
3064     wxRichTextAttr 
attr(GetBasicStyle()); 
3065     if (GetBasicStyle().HasParagraphStyleName()) 
3067         wxRichTextParagraphStyleDefinition
* paraDef 
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName()); 
3070             attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
)); 
3071             SetBasicStyle(attr
); 
3076     if (GetBasicStyle().HasCharacterStyleName()) 
3078         wxRichTextCharacterStyleDefinition
* charDef 
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName()); 
3081             attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
)); 
3082             SetBasicStyle(attr
); 
3087     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
3090         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
3091         wxASSERT (para 
!= NULL
); 
3095             // Combine paragraph and list styles. If there is a list style in the original attributes, 
3096             // the current indentation overrides anything else and is used to find the item indentation. 
3097             // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have, 
3098             // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list 
3099             // exception as above). 
3100             // Problem: when changing from one list style to another, there's a danger that the level info will get lost. 
3101             // So when changing a list style interactively, could retrieve level based on current style, then 
3102             // set appropriate indent and apply new style. 
3106             if (para
->GetAttributes().HasOutlineLevel()) 
3107                 outline 
= para
->GetAttributes().GetOutlineLevel(); 
3108             if (para
->GetAttributes().HasBulletNumber()) 
3109                 num 
= para
->GetAttributes().GetBulletNumber(); 
3111             if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty()) 
3113                 int currentIndent 
= para
->GetAttributes().GetLeftIndent(); 
3115                 wxRichTextParagraphStyleDefinition
* paraDef 
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName()); 
3116                 wxRichTextListStyleDefinition
* listDef 
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName()); 
3117                 if (paraDef 
&& !listDef
) 
3119                     para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
); 
3122                 else if (listDef 
&& !paraDef
) 
3124                     // Set overall style defined for the list style definition 
3125                     para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
); 
3127                     // Apply the style for this level 
3128                     wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
))); 
3131                 else if (listDef 
&& paraDef
) 
3133                     // Combines overall list style, style for level, and paragraph style 
3134                     para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
)); 
3138             else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty()) 
3140                 int currentIndent 
= para
->GetAttributes().GetLeftIndent(); 
3142                 wxRichTextListStyleDefinition
* listDef 
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName()); 
3144                 // Overall list definition style 
3145                 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
); 
3147                 // Style for this level 
3148                 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
))); 
3152             else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty()) 
3154                 wxRichTextParagraphStyleDefinition
* def 
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName()); 
3157                     para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
); 
3163                 para
->GetAttributes().SetOutlineLevel(outline
); 
3165                 para
->GetAttributes().SetBulletNumber(num
); 
3168         node 
= node
->GetNext(); 
3170     return foundCount 
!= 0; 
3174 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
) 
3176     wxRichTextStyleSheet
* styleSheet 
= GetStyleSheet(); 
3178     bool withUndo 
= ((flags 
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0); 
3179     // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0); 
3180     bool specifyLevel 
= ((flags 
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0); 
3181     bool renumber 
= ((flags 
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0); 
3183     // Current number, if numbering 
3186     wxASSERT (!specifyLevel 
|| (specifyLevel 
&& (specifiedLevel 
>= 0))); 
3188     // If we are associated with a control, make undoable; otherwise, apply immediately 
3191     bool haveControl 
= (GetRichTextCtrl() != NULL
); 
3193     wxRichTextAction
* action 
= NULL
; 
3195     if (haveControl 
&& withUndo
) 
3197         action 
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl()); 
3198         action
->SetRange(range
); 
3199         action
->SetPosition(GetRichTextCtrl()->GetCaretPosition()); 
3202     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
3205         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
3206         wxASSERT (para 
!= NULL
); 
3208         if (para 
&& para
->GetChildCount() > 0) 
3210             // Stop searching if we're beyond the range of interest 
3211             if (para
->GetRange().GetStart() > range
.GetEnd()) 
3214             if (!para
->GetRange().IsOutside(range
)) 
3216                 // We'll be using a copy of the paragraph to make style changes, 
3217                 // not updating the buffer directly. 
3218                 wxRichTextParagraph
* newPara 
wxDUMMY_INITIALIZE(NULL
); 
3220                 if (haveControl 
&& withUndo
) 
3222                     newPara 
= new wxRichTextParagraph(*para
); 
3223                     action
->GetNewParagraphs().AppendChild(newPara
); 
3225                     // Also store the old ones for Undo 
3226                     action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
)); 
3233                     int thisIndent 
= newPara
->GetAttributes().GetLeftIndent(); 
3234                     int thisLevel 
= specifyLevel 
? specifiedLevel 
: def
->FindLevelForIndent(thisIndent
); 
3236                     // How is numbering going to work? 
3237                     // If we are renumbering, or numbering for the first time, we need to keep 
3238                     // track of the number for each level. But we might be simply applying a different 
3240                     // In Word, applying a style to several paragraphs, even if at different levels, 
3241                     // reverts the level back to the same one. So we could do the same here. 
3242                     // Renumbering will need to be done when we promote/demote a paragraph. 
3244                     // Apply the overall list style, and item style for this level 
3245                     wxTextAttr 
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
)); 
3246                     wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
); 
3248                     // Now we need to do numbering 
3251                         newPara
->GetAttributes().SetBulletNumber(n
); 
3256                 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty()) 
3258                     // if def is NULL, remove list style, applying any associated paragraph style 
3259                     // to restore the attributes 
3261                     newPara
->GetAttributes().SetListStyleName(wxEmptyString
); 
3262                     newPara
->GetAttributes().SetLeftIndent(0, 0); 
3263                     newPara
->GetAttributes().SetBulletText(wxEmptyString
); 
3265                     // Eliminate the main list-related attributes 
3266                     newPara
->GetAttributes().SetFlags(newPara
->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT 
& ~wxTEXT_ATTR_BULLET_STYLE 
& ~wxTEXT_ATTR_BULLET_NUMBER 
& ~wxTEXT_ATTR_BULLET_TEXT 
& wxTEXT_ATTR_LIST_STYLE_NAME
); 
3268                     if (styleSheet 
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty()) 
3270                         wxRichTextParagraphStyleDefinition
* def 
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName()); 
3273                             newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
); 
3280         node 
= node
->GetNext(); 
3283     // Do action, or delay it until end of batch. 
3284     if (haveControl 
&& withUndo
) 
3285         GetRichTextCtrl()->GetBuffer().SubmitAction(action
); 
3290 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
) 
3292     if (GetStyleSheet()) 
3294         wxRichTextListStyleDefinition
* def 
= GetStyleSheet()->FindListStyle(defName
); 
3296             return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
); 
3301 /// Clear list for given range 
3302 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
) 
3304     return SetListStyle(range
, NULL
, flags
); 
3307 /// Number/renumber any list elements in the given range 
3308 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
) 
3310     return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
); 
3313 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified 
3314 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
, 
3315                                                 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
) 
3317     wxRichTextStyleSheet
* styleSheet 
= GetStyleSheet(); 
3319     bool withUndo 
= ((flags 
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0); 
3320     // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0); 
3322     bool specifyLevel 
= ((flags 
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0); 
3325     bool renumber 
= ((flags 
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0); 
3327     // Max number of levels 
3328     const int maxLevels 
= 10; 
3330     // The level we're looking at now 
3331     int currentLevel 
= -1; 
3333     // The item number for each level 
3334     int levels
[maxLevels
]; 
3337     // Reset all numbering 
3338     for (i 
= 0; i 
< maxLevels
; i
++) 
3340         if (startFrom 
!= -1) 
3341             levels
[i
] = startFrom
-1; 
3342         else if (renumber
) // start again 
3345             levels
[i
] = -1; // start from the number we found, if any 
3348     wxASSERT(!specifyLevel 
|| (specifyLevel 
&& (specifiedLevel 
>= 0))); 
3350     // If we are associated with a control, make undoable; otherwise, apply immediately 
3353     bool haveControl 
= (GetRichTextCtrl() != NULL
); 
3355     wxRichTextAction
* action 
= NULL
; 
3357     if (haveControl 
&& withUndo
) 
3359         action 
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl()); 
3360         action
->SetRange(range
); 
3361         action
->SetPosition(GetRichTextCtrl()->GetCaretPosition()); 
3364     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
3367         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
3368         wxASSERT (para 
!= NULL
); 
3370         if (para 
&& para
->GetChildCount() > 0) 
3372             // Stop searching if we're beyond the range of interest 
3373             if (para
->GetRange().GetStart() > range
.GetEnd()) 
3376             if (!para
->GetRange().IsOutside(range
)) 
3378                 // We'll be using a copy of the paragraph to make style changes, 
3379                 // not updating the buffer directly. 
3380                 wxRichTextParagraph
* newPara 
wxDUMMY_INITIALIZE(NULL
); 
3382                 if (haveControl 
&& withUndo
) 
3384                     newPara 
= new wxRichTextParagraph(*para
); 
3385                     action
->GetNewParagraphs().AppendChild(newPara
); 
3387                     // Also store the old ones for Undo 
3388                     action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
)); 
3393                 wxRichTextListStyleDefinition
* defToUse 
= def
; 
3396                     if (styleSheet 
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty()) 
3397                         defToUse 
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName()); 
3402                     int thisIndent 
= newPara
->GetAttributes().GetLeftIndent(); 
3403                     int thisLevel 
= defToUse
->FindLevelForIndent(thisIndent
); 
3405                     // If we've specified a level to apply to all, change the level. 
3406                     if (specifiedLevel 
!= -1) 
3407                         thisLevel 
= specifiedLevel
; 
3409                     // Do promotion if specified 
3410                     if ((promoteBy 
!= 0) && !para
->GetRange().IsOutside(promotionRange
)) 
3412                         thisLevel 
= thisLevel 
- promoteBy
; 
3419                     // Apply the overall list style, and item style for this level 
3420                     wxTextAttr 
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
)); 
3421                     wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
); 
3423                     // OK, we've (re)applied the style, now let's get the numbering right. 
3425                     if (currentLevel 
== -1) 
3426                         currentLevel 
= thisLevel
; 
3428                     // Same level as before, do nothing except increment level's number afterwards 
3429                     if (currentLevel 
== thisLevel
) 
3432                     // A deeper level: start renumbering all levels after current level 
3433                     else if (thisLevel 
> currentLevel
) 
3435                         for (i 
= currentLevel
+1; i 
<= thisLevel
; i
++) 
3439                         currentLevel 
= thisLevel
; 
3441                     else if (thisLevel 
< currentLevel
) 
3443                         currentLevel 
= thisLevel
; 
3446                     // Use the current numbering if -1 and we have a bullet number already 
3447                     if (levels
[currentLevel
] == -1) 
3449                         if (newPara
->GetAttributes().HasBulletNumber()) 
3450                             levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber(); 
3452                             levels
[currentLevel
] = 1; 
3456                         levels
[currentLevel
] ++; 
3459                     newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]); 
3461                     // Create the bullet text if an outline list 
3462                     if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) 
3465                         for (i 
= 0; i 
<= currentLevel
; i
++) 
3467                             if (!text
.IsEmpty()) 
3469                             text 
+= wxString::Format(wxT("%d"), levels
[i
]); 
3471                         newPara
->GetAttributes().SetBulletText(text
); 
3477         node 
= node
->GetNext(); 
3480     // Do action, or delay it until end of batch. 
3481     if (haveControl 
&& withUndo
) 
3482         GetRichTextCtrl()->GetBuffer().SubmitAction(action
); 
3487 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
) 
3489     if (GetStyleSheet()) 
3491         wxRichTextListStyleDefinition
* def 
= NULL
; 
3492         if (!defName
.IsEmpty()) 
3493             def 
= GetStyleSheet()->FindListStyle(defName
); 
3494         return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
); 
3499 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1 
3500 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
) 
3503     // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges 
3504     // to NumberList with a flag indicating promotion is required within one of the ranges. 
3505     // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find 
3506     // a paragraph that either has no list style, or has one that is different or whose indentation is less. 
3507     // We start renumbering from the para after that different para we found. We specify that the numbering of that 
3508     // list position will start from 1. 
3509     // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style). 
3510     // We can end the renumbering at this point. 
3512     // For now, only renumber within the promotion range. 
3514     return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
); 
3517 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
) 
3519     if (GetStyleSheet()) 
3521         wxRichTextListStyleDefinition
* def 
= NULL
; 
3522         if (!defName
.IsEmpty()) 
3523             def 
= GetStyleSheet()->FindListStyle(defName
); 
3524         return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
); 
3529 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the 
3530 /// position of the paragraph that it had to start looking from. 
3531 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxTextAttr
& attr
) const 
3533     if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) 
3536     wxRichTextStyleSheet
* styleSheet 
= GetStyleSheet(); 
3537     if (styleSheet 
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty()) 
3539         wxRichTextListStyleDefinition
* def 
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName()); 
3542             // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent(); 
3543             // int thisLevel = def->FindLevelForIndent(thisIndent); 
3545             bool isOutline 
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0; 
3547             attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
)); 
3548             if (previousParagraph
->GetAttributes().HasBulletName()) 
3549                 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName()); 
3550             attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle()); 
3551             attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName()); 
3553             int nextNumber 
= previousParagraph
->GetAttributes().GetBulletNumber() + 1; 
3554             attr
.SetBulletNumber(nextNumber
); 
3558                 wxString text 
= previousParagraph
->GetAttributes().GetBulletText(); 
3559                 if (!text
.IsEmpty()) 
3561                     int pos 
= text
.Find(wxT('.'), true); 
3562                     if (pos 
!= wxNOT_FOUND
) 
3564                         text 
= text
.Mid(0, text
.Length() - pos 
- 1); 
3567                         text 
= wxEmptyString
; 
3568                     if (!text
.IsEmpty()) 
3570                     text 
+= wxString::Format(wxT("%d"), nextNumber
); 
3571                     attr
.SetBulletText(text
); 
3585  * wxRichTextParagraph 
3586  * This object represents a single paragraph (or in a straight text editor, a line). 
3589 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextBox
) 
3591 wxArrayInt 
wxRichTextParagraph::sm_defaultTabs
; 
3593 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxTextAttr
* style
): 
3594     wxRichTextBox(parent
) 
3597         SetAttributes(*style
); 
3600 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxTextAttr
* paraStyle
, wxTextAttr
* charStyle
): 
3601     wxRichTextBox(parent
) 
3604         SetAttributes(*paraStyle
); 
3606     AppendChild(new wxRichTextPlainText(text
, this, charStyle
)); 
3609 wxRichTextParagraph::~wxRichTextParagraph() 
3615 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int WXUNUSED(descent
), int style
) 
3617     wxTextAttr attr 
= GetCombinedAttributes(); 
3619     // Draw the bullet, if any 
3620     if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
) 
3622         if (attr
.GetLeftSubIndent() != 0) 
3624             int spaceBeforePara 
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore()); 
3625             int leftIndent 
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent()); 
3627             wxTextAttr 
bulletAttr(GetCombinedAttributes()); 
3629             // Combine with the font of the first piece of content, if one is specified 
3630             if (GetChildren().GetCount() > 0) 
3632                 wxRichTextObject
* firstObj 
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData(); 
3633                 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont()) 
3635                     wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes()); 
3639             // Get line height from first line, if any 
3640             wxRichTextLine
* line 
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
; 
3643             int lineHeight 
wxDUMMY_INITIALIZE(0); 
3646                 lineHeight 
= line
->GetSize().y
; 
3647                 linePos 
= line
->GetPosition() + GetPosition(); 
3652                 if (bulletAttr
.HasFont() && GetBuffer()) 
3653                     font 
= GetBuffer()->GetFontTable().FindFont(bulletAttr
); 
3655                     font 
= (*wxNORMAL_FONT
); 
3657                 wxCheckSetFont(dc
, font
); 
3659                 lineHeight 
= dc
.GetCharHeight(); 
3660                 linePos 
= GetPosition(); 
3661                 linePos
.y 
+= spaceBeforePara
; 
3664             wxRect 
bulletRect(GetPosition().x 
+ leftIndent
, linePos
.y
, linePos
.x 
- (GetPosition().x 
+ leftIndent
), lineHeight
); 
3666             if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
) 
3668                 if (wxRichTextBuffer::GetRenderer()) 
3669                     wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
); 
3671             else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
) 
3673                 if (wxRichTextBuffer::GetRenderer()) 
3674                     wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
); 
3678                 wxString bulletText 
= GetBulletText(); 
3680                 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer()) 
3681                     wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
); 
3686     // Draw the range for each line, one object at a time. 
3688     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
3691         wxRichTextLine
* line 
= node
->GetData(); 
3692         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
3694         // Lines are specified relative to the paragraph 
3696         wxPoint linePosition 
= line
->GetPosition() + GetPosition(); 
3698         // Don't draw if off the screen 
3699         if (((style 
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y 
+ line
->GetSize().y
) >= rect
.y 
&& linePosition
.y 
<= rect
.y 
+ rect
.height
)) 
3701             wxPoint objectPosition 
= linePosition
; 
3702             int maxDescent 
= line
->GetDescent(); 
3704             // Loop through objects until we get to the one within range 
3705             wxRichTextObjectList::compatibility_iterator node2 
= m_children
.GetFirst(); 
3710                 wxRichTextObject
* child 
= node2
->GetData(); 
3712                 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
)) 
3714                     // Draw this part of the line at the correct position 
3715                     wxRichTextRange 
objectRange(child
->GetRange()); 
3716                     objectRange
.LimitTo(lineRange
); 
3719 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
3720                     if (i 
< (int) line
->GetObjectSizes().GetCount()) 
3722                         objectSize
.x 
= line
->GetObjectSizes()[(size_t) i
]; 
3728                         child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
); 
3731                     // Use the child object's width, but the whole line's height 
3732                     wxRect 
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
)); 
3733                     child
->Draw(dc
, objectRange
, selectionRange
, childRect
, maxDescent
, style
); 
3735                     objectPosition
.x 
+= objectSize
.x
; 
3738                 else if (child
->GetRange().GetStart() > lineRange
.GetEnd()) 
3739                     // Can break out of inner loop now since we've passed this line's range 
3742                 node2 
= node2
->GetNext(); 
3746         node 
= node
->GetNext(); 
3752 // Get the range width using partial extents calculated for the whole paragraph. 
3753 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
) 
3755     wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength()); 
3757     if (partialExtents
.GetCount() < (size_t) range
.GetLength()) 
3760     int leftMostPos 
= 0; 
3761     if (range
.GetStart() - para
.GetRange().GetStart() > 0) 
3762         leftMostPos 
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1]; 
3764     int rightMostPos 
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()]; 
3766     int w 
= rightMostPos 
- leftMostPos
; 
3771 /// Lay the item out 
3772 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
) 
3774     // Deal with floating objects firstly before the normal layout 
3775     wxRichTextBuffer
* buffer 
= GetBuffer(); 
3777     wxRichTextFloatCollector
* collector 
= buffer
->GetFloatCollector(); 
3778     wxASSERT(collector
); 
3779     LayoutFloat(dc
, rect
, style
, collector
); 
3781     wxTextAttr attr 
= GetCombinedAttributes(); 
3785     // Increase the size of the paragraph due to spacing 
3786     int spaceBeforePara 
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore()); 
3787     int spaceAfterPara 
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter()); 
3788     int leftIndent 
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent()); 
3789     int leftSubIndent 
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent()); 
3790     int rightIndent 
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent()); 
3792     int lineSpacing 
= 0; 
3794     // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc. 
3795     if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().Ok()) 
3797         wxCheckSetFont(dc
, attr
.GetFont()); 
3798         lineSpacing 
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0)); 
3801     // Start position for each line relative to the paragraph 
3802     int startPositionFirstLine 
= leftIndent
; 
3803     int startPositionSubsequentLines 
= leftIndent 
+ leftSubIndent
; 
3804     wxRect availableRect
; 
3806     // If we have a bullet in this paragraph, the start position for the first line's text 
3807     // is actually leftIndent + leftSubIndent. 
3808     if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
) 
3809         startPositionFirstLine 
= startPositionSubsequentLines
; 
3811     long lastEndPos 
= GetRange().GetStart()-1; 
3812     long lastCompletedEndPos 
= lastEndPos
; 
3814     int currentWidth 
= 0; 
3815     SetPosition(rect
.GetPosition()); 
3817     wxPoint 
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph 
3824     int lineDescent 
= 0; 
3826     wxRichTextObjectList::compatibility_iterator node
; 
3828 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
3830     wxArrayInt partialExtents
; 
3835     // This calculates the partial text extents 
3836     GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
); 
3838     node 
= m_children
.GetFirst(); 
3841         wxRichTextObject
* child 
= node
->GetData(); 
3843         child
->SetCachedSize(wxDefaultSize
); 
3844         child
->Layout(dc
, rect
, style
); 
3846         node 
= node
->GetNext(); 
3853     // We may need to go back to a previous child, in which case create the new line, 
3854     // find the child corresponding to the start position of the string, and 
3857     node 
= m_children
.GetFirst(); 
3860         wxRichTextObject
* child 
= node
->GetData(); 
3862         // If floating, ignore. We already laid out floats. 
3863         if (child
->IsFloating() || child
->GetRange().GetLength() == 0) 
3865             node 
= node
->GetNext(); 
3869         // If this is e.g. a composite text box, it will need to be laid out itself. 
3870         // But if just a text fragment or image, for example, this will 
3871         // do nothing. NB: won't we need to set the position after layout? 
3872         // since for example if position is dependent on vertical line size, we 
3873         // can't tell the position until the size is determined. So possibly introduce 
3874         // another layout phase. 
3876         // We may only be looking at part of a child, if we searched back for wrapping 
3877         // and found a suitable point some way into the child. So get the size for the fragment 
3880         long nextBreakPos 
= GetFirstLineBreakPosition(lastEndPos
+1); 
3881         long lastPosToUse 
= child
->GetRange().GetEnd(); 
3882         bool lineBreakInThisObject 
= (nextBreakPos 
> -1 && nextBreakPos 
<= child
->GetRange().GetEnd()); 
3884         if (lineBreakInThisObject
) 
3885             lastPosToUse 
= nextBreakPos
; 
3888         int childDescent 
= 0; 
3890         if ((nextBreakPos 
== -1) && (lastEndPos 
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing 
3892             childSize 
= child
->GetCachedSize(); 
3893             childDescent 
= child
->GetDescent(); 
3897 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
3898             // Get height only, then the width using the partial extents 
3899             GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
); 
3900             childSize
.x 
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
); 
3902             GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition()); 
3906         // Available width depends on the floating objects and the line height 
3907         // Note: the floating objects may be placed vertically along the two side of 
3908         //       buffer, so we may have different available line width with different 
3909         //       [startY, endY]. So, we should can't determine how wide the available 
3910         //       space is until we know the exact line height. 
3911         lineDescent 
= wxMax(childDescent
, maxDescent
); 
3912         lineAscent 
= wxMax(childSize
.y
-childDescent
, maxAscent
); 
3913         lineHeight 
= lineDescent 
+ lineAscent
; 
3914         availableRect 
= collector
->GetAvailableRect(rect
.y 
+ currentPosition
.y
, rect
.y 
+ currentPosition
.y 
+ lineHeight
); 
3916         currentPosition
.x 
= (lineCount 
== 0 ? availableRect
.x 
+ startPositionFirstLine 
: availableRect
.x 
+ startPositionSubsequentLines
); 
3919         // 1) There was a line break BEFORE the natural break 
3920         // 2) There was a line break AFTER the natural break 
3921         // 3) The child still fits (carry on) 
3923         if ((lineBreakInThisObject 
&& (childSize
.x 
+ currentWidth 
<= availableRect
.width
)) || 
3924             (childSize
.x 
+ currentWidth 
> availableRect
.width
)) 
3926             long wrapPosition 
= 0; 
3928             int indent 
= lineCount 
== 0 ? startPositionFirstLine 
: startPositionSubsequentLines
; 
3929             indent 
+= rightIndent
; 
3930             // Find a place to wrap. This may walk back to previous children, 
3931             // for example if a word spans several objects. 
3932             // Note: one object must contains only one wxTextAtrr, so the line height will not 
3933             //       change inside one object. Thus, we can pass the remain line width to the 
3934             //       FindWrapPosition function. 
3935             if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width 
- indent
, wrapPosition
, & partialExtents
)) 
3937                 // If the function failed, just cut it off at the end of this child. 
3938                 wrapPosition 
= child
->GetRange().GetEnd(); 
3941             // FindWrapPosition can still return a value that will put us in an endless wrapping loop 
3942             if (wrapPosition 
<= lastCompletedEndPos
) 
3943                 wrapPosition 
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd()); 
3945             // wxLogDebug(wxT("Split at %ld"), wrapPosition); 
3947             // Let's find the actual size of the current line now 
3949             wxRichTextRange 
actualRange(lastCompletedEndPos
+1, wrapPosition
); 
3951             /// Use previous descent, not the wrapping descent we just found, since this may be too big 
3952             /// for the fragment we're about to add. 
3953             childDescent 
= maxDescent
; 
3955 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
3956             // Get height only, then the width using the partial extents 
3957             GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
); 
3958             actualSize
.x 
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
); 
3960             GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
); 
3963             currentWidth 
= actualSize
.x
; 
3964             maxDescent 
= wxMax(childDescent
, maxDescent
); 
3965             maxAscent 
= wxMax(actualSize
.y
-childDescent
, maxAscent
); 
3966             lineHeight 
= maxDescent 
+ maxAscent
; 
3969             wxRichTextLine
* line 
= AllocateLine(lineCount
); 
3971             // Set relative range so we won't have to change line ranges when paragraphs are moved 
3972             line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart())); 
3973             line
->SetPosition(currentPosition
); 
3974             line
->SetSize(wxSize(currentWidth
, lineHeight
)); 
3975             line
->SetDescent(maxDescent
); 
3977             // Now move down a line. TODO: add margins, spacing 
3978             currentPosition
.y 
+= lineHeight
; 
3979             currentPosition
.y 
+= lineSpacing
; 
3983             maxWidth 
= wxMax(maxWidth
, currentWidth
); 
3987             // TODO: account for zero-length objects, such as fields 
3988             wxASSERT(wrapPosition 
> lastCompletedEndPos
); 
3990             lastEndPos 
= wrapPosition
; 
3991             lastCompletedEndPos 
= lastEndPos
; 
3995             // May need to set the node back to a previous one, due to searching back in wrapping 
3996             wxRichTextObject
* childAfterWrapPosition 
= FindObjectAtPosition(wrapPosition
+1); 
3997             if (childAfterWrapPosition
) 
3998                 node 
= m_children
.Find(childAfterWrapPosition
); 
4000                 node 
= node
->GetNext(); 
4004             // We still fit, so don't add a line, and keep going 
4005             currentWidth 
+= childSize
.x
; 
4006             maxDescent 
= wxMax(childDescent
, maxDescent
); 
4007             maxAscent 
= wxMax(childSize
.y
-childDescent
, maxAscent
); 
4008             lineHeight 
= maxDescent 
+ maxAscent
; 
4010             maxWidth 
= wxMax(maxWidth
, currentWidth
); 
4011             lastEndPos 
= child
->GetRange().GetEnd(); 
4013             node 
= node
->GetNext(); 
4017     // Add the last line - it's the current pos -> last para pos 
4018     // Substract -1 because the last position is always the end-paragraph position. 
4019     if (lastCompletedEndPos 
<= GetRange().GetEnd()-1) 
4021         currentPosition
.x 
= (lineCount 
== 0 ? availableRect
.x 
+ startPositionFirstLine 
: availableRect
.x 
+ startPositionSubsequentLines
); 
4023         wxRichTextLine
* line 
= AllocateLine(lineCount
); 
4025         wxRichTextRange 
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1); 
4027         // Set relative range so we won't have to change line ranges when paragraphs are moved 
4028         line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart())); 
4030         line
->SetPosition(currentPosition
); 
4032         if (lineHeight 
== 0 && GetBuffer()) 
4034             wxFont 
font(GetBuffer()->GetFontTable().FindFont(attr
)); 
4035             wxCheckSetFont(dc
, font
); 
4036             lineHeight 
= dc
.GetCharHeight(); 
4038         if (maxDescent 
== 0) 
4041             dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
); 
4044         line
->SetSize(wxSize(currentWidth
, lineHeight
)); 
4045         line
->SetDescent(maxDescent
); 
4046         currentPosition
.y 
+= lineHeight
; 
4047         currentPosition
.y 
+= lineSpacing
; 
4051     // Remove remaining unused line objects, if any 
4052     ClearUnusedLines(lineCount
); 
4054     // Apply styles to wrapped lines 
4055     ApplyParagraphStyle(attr
, rect
, dc
); 
4057     SetCachedSize(wxSize(maxWidth
, currentPosition
.y 
+ spaceAfterPara
)); 
4061 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
4062 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING 
4063     // Use the text extents to calculate the size of each fragment in each line 
4064     wxRichTextLineList::compatibility_iterator lineNode 
= m_cachedLines
.GetFirst(); 
4067         wxRichTextLine
* line 
= lineNode
->GetData(); 
4068         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
4070         // Loop through objects until we get to the one within range 
4071         wxRichTextObjectList::compatibility_iterator node2 
= m_children
.GetFirst(); 
4075             wxRichTextObject
* child 
= node2
->GetData(); 
4077             if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
)) 
4079                 wxRichTextRange rangeToUse 
= lineRange
; 
4080                 rangeToUse
.LimitTo(child
->GetRange()); 
4082                 // Find the size of the child from the text extents, and store in an array 
4083                 // for drawing later 
4085                 if (rangeToUse
.GetStart() > GetRange().GetStart()) 
4086                     left 
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()]; 
4087                 int right 
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()]; 
4088                 int sz 
= right 
- left
; 
4089                 line
->GetObjectSizes().Add(sz
); 
4091             else if (child
->GetRange().GetStart() > lineRange
.GetEnd()) 
4092                 // Can break out of inner loop now since we've passed this line's range 
4095             node2 
= node2
->GetNext(); 
4098         lineNode 
= lineNode
->GetNext(); 
4106 /// Apply paragraph styles, such as centering, to wrapped lines 
4107 void wxRichTextParagraph::ApplyParagraphStyle(const wxTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
) 
4109     if (!attr
.HasAlignment()) 
4112     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
4115         wxRichTextLine
* line 
= node
->GetData(); 
4117         wxPoint pos 
= line
->GetPosition(); 
4118         wxSize size 
= line
->GetSize(); 
4120         // centering, right-justification 
4121         if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
) 
4123             int rightIndent 
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent()); 
4124             pos
.x 
= (rect
.GetWidth() - pos
.x 
- rightIndent 
- size
.x
)/2 + pos
.x
; 
4125             line
->SetPosition(pos
); 
4127         else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
) 
4129             int rightIndent 
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent()); 
4130             pos
.x 
= rect
.GetWidth() - size
.x 
- rightIndent
; 
4131             line
->SetPosition(pos
); 
4134         node 
= node
->GetNext(); 
4138 /// Insert text at the given position 
4139 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
) 
4141     wxRichTextObject
* childToUse 
= NULL
; 
4142     wxRichTextObjectList::compatibility_iterator nodeToUse 
= wxRichTextObjectList::compatibility_iterator(); 
4144     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
4147         wxRichTextObject
* child 
= node
->GetData(); 
4148         if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0) 
4155         node 
= node
->GetNext(); 
4160         wxRichTextPlainText
* textObject 
= wxDynamicCast(childToUse
, wxRichTextPlainText
); 
4163             int posInString 
= pos 
- textObject
->GetRange().GetStart(); 
4165             wxString newText 
= textObject
->GetText().Mid(0, posInString
) + 
4166                                text 
+ textObject
->GetText().Mid(posInString
); 
4167             textObject
->SetText(newText
); 
4169             int textLength 
= text
.length(); 
4171             textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(), 
4172                                                  textObject
->GetRange().GetEnd() + textLength
)); 
4174             // Increment the end range of subsequent fragments in this paragraph. 
4175             // We'll set the paragraph range itself at a higher level. 
4177             wxRichTextObjectList::compatibility_iterator node 
= nodeToUse
->GetNext(); 
4180                 wxRichTextObject
* child 
= node
->GetData(); 
4181                 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
, 
4182                                                  textObject
->GetRange().GetEnd() + textLength
)); 
4184                 node 
= node
->GetNext(); 
4191             // TODO: if not a text object, insert at closest position, e.g. in front of it 
4197         // Don't pass parent initially to suppress auto-setting of parent range. 
4198         // We'll do that at a higher level. 
4199         wxRichTextPlainText
* textObject 
= new wxRichTextPlainText(text
, this); 
4201         AppendChild(textObject
); 
4208 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
) 
4210     wxRichTextBox::Copy(obj
); 
4213 /// Clear the cached lines 
4214 void wxRichTextParagraph::ClearLines() 
4216     WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
); 
4219 /// Get/set the object size for the given range. Returns false if the range 
4220 /// is invalid for this object. 
4221 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const 
4223     if (!range
.IsWithin(GetRange())) 
4226     if (flags 
& wxRICHTEXT_UNFORMATTED
) 
4228         // Just use unformatted data, assume no line breaks 
4229         // TODO: take into account line breaks 
4233         wxArrayInt childExtents
; 
4240         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
4244             wxRichTextObject
* child 
= node
->GetData(); 
4245             if (!child
->GetRange().IsOutside(range
)) 
4247                 // Floating objects have a zero size within the paragraph. 
4248                 if (child
->IsFloating()) 
4253                         if (partialExtents
->GetCount() > 0) 
4254                             lastSize 
= (*partialExtents
)[partialExtents
->GetCount()-1]; 
4258                         partialExtents
->Add(0 /* zero size */ + lastSize
); 
4265                 wxRichTextRange rangeToUse 
= range
; 
4266                 rangeToUse
.LimitTo(child
->GetRange()); 
4267                 int childDescent 
= 0; 
4269                 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size, 
4270                 // but it's only going to be used after caching has taken place. 
4271                 if ((flags 
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y 
!= 0) 
4273                     childDescent 
= child
->GetDescent(); 
4274                     childSize 
= child
->GetCachedSize(); 
4276                     sz
.y 
= wxMax(sz
.y
, childSize
.y
); 
4277                     sz
.x 
+= childSize
.x
; 
4278                     descent 
= wxMax(descent
, childDescent
); 
4280                 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x 
+ sz
.x
, position
.y
), p
)) 
4282                     sz
.y 
= wxMax(sz
.y
, childSize
.y
); 
4283                     sz
.x 
+= childSize
.x
; 
4284                     descent 
= wxMax(descent
, childDescent
); 
4286                     if ((flags 
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse 
== child
->GetRange())) 
4288                         child
->SetCachedSize(childSize
); 
4289                         child
->SetDescent(childDescent
); 
4295                         if (partialExtents
->GetCount() > 0) 
4296                             lastSize 
= (*partialExtents
)[partialExtents
->GetCount()-1]; 
4301                         for (i 
= 0; i 
< childExtents
.GetCount(); i
++) 
4303                             partialExtents
->Add(childExtents
[i
] + lastSize
); 
4313             node 
= node
->GetNext(); 
4319         // Use formatted data, with line breaks 
4322         // We're going to loop through each line, and then for each line, 
4323         // call GetRangeSize for the fragment that comprises that line. 
4324         // Only we have to do that multiple times within the line, because 
4325         // the line may be broken into pieces. For now ignore line break commands 
4326         // (so we can assume that getting the unformatted size for a fragment 
4327         // within a line is the actual size) 
4329         wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
4332             wxRichTextLine
* line 
= node
->GetData(); 
4333             wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
4334             if (!lineRange
.IsOutside(range
)) 
4338                 wxRichTextObjectList::compatibility_iterator node2 
= m_children
.GetFirst(); 
4341                     wxRichTextObject
* child 
= node2
->GetData(); 
4343                     if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
)) 
4345                         wxRichTextRange rangeToUse 
= lineRange
; 
4346                         rangeToUse
.LimitTo(child
->GetRange()); 
4349                         int childDescent 
= 0; 
4350                         if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x 
+ sz
.x
, position
.y
))) 
4352                             lineSize
.y 
= wxMax(lineSize
.y
, childSize
.y
); 
4353                             lineSize
.x 
+= childSize
.x
; 
4355                         descent 
= wxMax(descent
, childDescent
); 
4358                     node2 
= node2
->GetNext(); 
4361                 // Increase size by a line (TODO: paragraph spacing) 
4363                 sz
.x 
= wxMax(sz
.x
, lineSize
.x
); 
4365             node 
= node
->GetNext(); 
4372 /// Finds the absolute position and row height for the given character position 
4373 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
) 
4377         wxRichTextLine
* line 
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0); 
4379             *height 
= line
->GetSize().y
; 
4381             *height 
= dc
.GetCharHeight(); 
4383         // -1 means 'the start of the buffer'. 
4386             pt 
= pt 
+ line
->GetPosition(); 
4391     // The final position in a paragraph is taken to mean the position 
4392     // at the start of the next paragraph. 
4393     if (index 
== GetRange().GetEnd()) 
4395         wxRichTextParagraphLayoutBox
* parent 
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
); 
4396         wxASSERT( parent 
!= NULL 
); 
4398         // Find the height at the next paragraph, if any 
4399         wxRichTextLine
* line 
= parent
->GetLineAtPosition(index 
+ 1); 
4402             *height 
= line
->GetSize().y
; 
4403             pt 
= line
->GetAbsolutePosition(); 
4407             *height 
= dc
.GetCharHeight(); 
4408             int indent 
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent()); 
4409             pt 
= wxPoint(indent
, GetCachedSize().y
); 
4415     if (index 
< GetRange().GetStart() || index 
> GetRange().GetEnd()) 
4418     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
4421         wxRichTextLine
* line 
= node
->GetData(); 
4422         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
4423         if (index 
>= lineRange
.GetStart() && index 
<= lineRange
.GetEnd()) 
4425             // If this is the last point in the line, and we're forcing the 
4426             // returned value to be the start of the next line, do the required 
4428             if (index 
== lineRange
.GetEnd() && forceLineStart
) 
4430                 if (node
->GetNext()) 
4432                     wxRichTextLine
* nextLine 
= node
->GetNext()->GetData(); 
4433                     *height 
= nextLine
->GetSize().y
; 
4434                     pt 
= nextLine
->GetAbsolutePosition(); 
4439             pt
.y 
= line
->GetPosition().y 
+ GetPosition().y
; 
4441             wxRichTextRange 
r(lineRange
.GetStart(), index
); 
4445             // We find the size of the line up to this point, 
4446             // then we can add this size to the line start position and 
4447             // paragraph start position to find the actual position. 
4449             if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition())) 
4451                 pt
.x 
= line
->GetPosition().x 
+ GetPosition().x 
+ rangeSize
.x
; 
4452                 *height 
= line
->GetSize().y
; 
4459         node 
= node
->GetNext(); 
4465 /// Hit-testing: returns a flag indicating hit test details, plus 
4466 /// information about position 
4467 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
) 
4469     wxPoint paraPos 
= GetPosition(); 
4471     wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetFirst(); 
4474         wxRichTextLine
* line 
= node
->GetData(); 
4475         wxPoint linePos 
= paraPos 
+ line
->GetPosition(); 
4476         wxSize lineSize 
= line
->GetSize(); 
4477         wxRichTextRange lineRange 
= line
->GetAbsoluteRange(); 
4479         if (pt
.y 
<= linePos
.y 
+ lineSize
.y
) 
4481             if (pt
.x 
< linePos
.x
) 
4483                 textPosition 
= lineRange
.GetStart(); 
4484                 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
; 
4486             else if (pt
.x 
>= (linePos
.x 
+ lineSize
.x
)) 
4488                 textPosition 
= lineRange
.GetEnd(); 
4489                 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
; 
4493 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
4494                 wxArrayInt partialExtents
; 
4499                 // This calculates the partial text extents 
4500                 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), & partialExtents
); 
4502                 int lastX 
= linePos
.x
; 
4504                 for (i 
= 0; i 
< partialExtents
.GetCount(); i
++) 
4506                     int nextX 
= partialExtents
[i
] + linePos
.x
; 
4508                     if (pt
.x 
>= lastX 
&& pt
.x 
<= nextX
) 
4510                         textPosition 
= i 
+ lineRange
.GetStart(); // minus 1? 
4512                         // So now we know it's between i-1 and i. 
4513                         // Let's see if we can be more precise about 
4514                         // which side of the position it's on. 
4516                         int midPoint 
= (nextX 
+ lastX
)/2; 
4517                         if (pt
.x 
>= midPoint
) 
4518                             return wxRICHTEXT_HITTEST_AFTER
; 
4520                             return wxRICHTEXT_HITTEST_BEFORE
; 
4527                 int lastX 
= linePos
.x
; 
4528                 for (i 
= lineRange
.GetStart(); i 
<= lineRange
.GetEnd(); i
++) 
4533                     wxRichTextRange 
rangeToUse(lineRange
.GetStart(), i
); 
4535                     GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
); 
4537                     int nextX 
= childSize
.x 
+ linePos
.x
; 
4539                     if (pt
.x 
>= lastX 
&& pt
.x 
<= nextX
) 
4543                         // So now we know it's between i-1 and i. 
4544                         // Let's see if we can be more precise about 
4545                         // which side of the position it's on. 
4547                         int midPoint 
= (nextX 
+ lastX
)/2; 
4548                         if (pt
.x 
>= midPoint
) 
4549                             return wxRICHTEXT_HITTEST_AFTER
; 
4551                             return wxRICHTEXT_HITTEST_BEFORE
; 
4562         node 
= node
->GetNext(); 
4565     return wxRICHTEXT_HITTEST_NONE
; 
4568 /// Split an object at this position if necessary, and return 
4569 /// the previous object, or NULL if inserting at beginning. 
4570 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
) 
4572     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
4575         wxRichTextObject
* child 
= node
->GetData(); 
4577         if (pos 
== child
->GetRange().GetStart()) 
4581                 if (node
->GetPrevious()) 
4582                     *previousObject 
= node
->GetPrevious()->GetData(); 
4584                     *previousObject 
= NULL
; 
4590         if (child
->GetRange().Contains(pos
)) 
4592             // This should create a new object, transferring part of 
4593             // the content to the old object and the rest to the new object. 
4594             wxRichTextObject
* newObject 
= child
->DoSplit(pos
); 
4596             // If we couldn't split this object, just insert in front of it. 
4599                 // Maybe this is an empty string, try the next one 
4604                 // Insert the new object after 'child' 
4605                 if (node
->GetNext()) 
4606                     m_children
.Insert(node
->GetNext(), newObject
); 
4608                     m_children
.Append(newObject
); 
4609                 newObject
->SetParent(this); 
4612                     *previousObject 
= child
; 
4618         node 
= node
->GetNext(); 
4621         *previousObject 
= NULL
; 
4625 /// Move content to a list from obj on 
4626 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
) 
4628     wxRichTextObjectList::compatibility_iterator node 
= m_children
.Find(obj
); 
4631         wxRichTextObject
* child 
= node
->GetData(); 
4634         wxRichTextObjectList::compatibility_iterator oldNode 
= node
; 
4636         node 
= node
->GetNext(); 
4638         m_children
.DeleteNode(oldNode
); 
4642 /// Add content back from list 
4643 void wxRichTextParagraph::MoveFromList(wxList
& list
) 
4645     for (wxList::compatibility_iterator node 
= list
.GetFirst(); node
; node 
= node
->GetNext()) 
4647         AppendChild((wxRichTextObject
*) node
->GetData()); 
4652 void wxRichTextParagraph::CalculateRange(long start
, long& end
) 
4654     wxRichTextCompositeObject::CalculateRange(start
, end
); 
4656     // Add one for end of paragraph 
4659     m_range
.SetRange(start
, end
); 
4662 /// Find the object at the given position 
4663 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
) 
4665     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
4668         wxRichTextObject
* obj 
= node
->GetData(); 
4669         if (obj
->GetRange().Contains(position
)) 
4672         node 
= node
->GetNext(); 
4677 /// Get the plain text searching from the start or end of the range. 
4678 /// The resulting string may be shorter than the range given. 
4679 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
) 
4681     text 
= wxEmptyString
; 
4685         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
4688             wxRichTextObject
* obj 
= node
->GetData(); 
4689             if (!obj
->GetRange().IsOutside(range
)) 
4691                 wxRichTextPlainText
* textObj 
= wxDynamicCast(obj
, wxRichTextPlainText
); 
4694                     text 
+= textObj
->GetTextForRange(range
); 
4702             node 
= node
->GetNext(); 
4707         wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetLast(); 
4710             wxRichTextObject
* obj 
= node
->GetData(); 
4711             if (!obj
->GetRange().IsOutside(range
)) 
4713                 wxRichTextPlainText
* textObj 
= wxDynamicCast(obj
, wxRichTextPlainText
); 
4716                     text 
= textObj
->GetTextForRange(range
) + text
; 
4720                     text 
= wxT(" ") + text
; 
4724             node 
= node
->GetPrevious(); 
4731 /// Find a suitable wrap position. 
4732 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
) 
4734     if (range
.GetLength() <= 0) 
4737     // Find the first position where the line exceeds the available space. 
4739     long breakPosition 
= range
.GetEnd(); 
4741 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 
4742     if (partialExtents 
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline 
4746         if (range
.GetStart() > GetRange().GetStart()) 
4747             widthBefore 
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1]; 
4752         for (i 
= (size_t) range
.GetStart(); i 
<= (size_t) range
.GetEnd(); i
++) 
4754             int widthFromStartOfThisRange 
= (*partialExtents
)[i 
- GetRange().GetStart()] - widthBefore
; 
4756             if (widthFromStartOfThisRange 
> availableSpace
) 
4758                 breakPosition 
= i
-1; 
4766         // Binary chop for speed 
4767         long minPos 
= range
.GetStart(); 
4768         long maxPos 
= range
.GetEnd(); 
4771             if (minPos 
== maxPos
) 
4774                 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
4776                 if (sz
.x 
> availableSpace
) 
4777                     breakPosition 
= minPos 
- 1; 
4780             else if ((maxPos 
- minPos
) == 1) 
4783                 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
4785                 if (sz
.x 
> availableSpace
) 
4786                     breakPosition 
= minPos 
- 1; 
4789                     GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
4790                     if (sz
.x 
> availableSpace
) 
4791                         breakPosition 
= maxPos
-1; 
4797                 long nextPos 
= minPos 
+ ((maxPos 
- minPos
) / 2); 
4800                 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
); 
4802                 if (sz
.x 
> availableSpace
) 
4814     // Now we know the last position on the line. 
4815     // Let's try to find a word break. 
4818     if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false)) 
4820         int newLinePos 
= plainText
.Find(wxRichTextLineBreakChar
); 
4821         if (newLinePos 
!= wxNOT_FOUND
) 
4823             breakPosition 
= wxMax(0, range
.GetStart() + newLinePos
); 
4827             int spacePos 
= plainText
.Find(wxT(' '), true); 
4828             int tabPos 
= plainText
.Find(wxT('\t'), true); 
4829             int pos 
= wxMax(spacePos
, tabPos
); 
4830             if (pos 
!= wxNOT_FOUND
) 
4832                 int positionsFromEndOfString 
= plainText
.length() - pos 
- 1; 
4833                 breakPosition 
= breakPosition 
- positionsFromEndOfString
; 
4838     wrapPosition 
= breakPosition
; 
4843 /// Get the bullet text for this paragraph. 
4844 wxString 
wxRichTextParagraph::GetBulletText() 
4846     if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE 
|| 
4847         (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)) 
4848         return wxEmptyString
; 
4850     int number 
= GetAttributes().GetBulletNumber(); 
4853     if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)) 
4855         text
.Printf(wxT("%d"), number
); 
4857     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
) 
4859         // TODO: Unicode, and also check if number > 26 
4860         text
.Printf(wxT("%c"), (wxChar
) (number
+64)); 
4862     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
) 
4864         // TODO: Unicode, and also check if number > 26 
4865         text
.Printf(wxT("%c"), (wxChar
) (number
+96)); 
4867     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
) 
4869         text 
= wxRichTextDecimalToRoman(number
); 
4871     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
) 
4873         text 
= wxRichTextDecimalToRoman(number
); 
4876     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) 
4878         text 
= GetAttributes().GetBulletText(); 
4881     if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) 
4883         // The outline style relies on the text being computed statically, 
4884         // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text 
4885         // should be stored in the attributes; if not, just use the number for this 
4886         // level, as previously computed. 
4887         if (!GetAttributes().GetBulletText().IsEmpty()) 
4888             text 
= GetAttributes().GetBulletText(); 
4891     if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
) 
4893         text 
= wxT("(") + text 
+ wxT(")"); 
4895     else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
) 
4897         text 
= text 
+ wxT(")"); 
4900     if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
) 
4908 /// Allocate or reuse a line object 
4909 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
) 
4911     if (pos 
< (int) m_cachedLines
.GetCount()) 
4913         wxRichTextLine
* line 
= m_cachedLines
.Item(pos
)->GetData(); 
4919         wxRichTextLine
* line 
= new wxRichTextLine(this); 
4920         m_cachedLines
.Append(line
); 
4925 /// Clear remaining unused line objects, if any 
4926 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
) 
4928     int cachedLineCount 
= m_cachedLines
.GetCount(); 
4929     if ((int) cachedLineCount 
> lineCount
) 
4931         for (int i 
= 0; i 
< (int) (cachedLineCount 
- lineCount
); i 
++) 
4933             wxRichTextLineList::compatibility_iterator node 
= m_cachedLines
.GetLast(); 
4934             wxRichTextLine
* line 
= node
->GetData(); 
4935             m_cachedLines
.Erase(node
); 
4942 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically 
4943 /// retrieve the actual style. 
4944 wxTextAttr 
wxRichTextParagraph::GetCombinedAttributes(const wxTextAttr
& contentStyle
) const 
4947     wxRichTextBuffer
* buf 
= wxDynamicCast(GetParent(), wxRichTextBuffer
); 
4950         attr 
= buf
->GetBasicStyle(); 
4951         wxRichTextApplyStyle(attr
, GetAttributes()); 
4954         attr 
= GetAttributes(); 
4956     wxRichTextApplyStyle(attr
, contentStyle
); 
4960 /// Get combined attributes of the base style and paragraph style. 
4961 wxTextAttr 
wxRichTextParagraph::GetCombinedAttributes() const 
4964     wxRichTextBuffer
* buf 
= wxDynamicCast(GetParent(), wxRichTextBuffer
); 
4967         attr 
= buf
->GetBasicStyle(); 
4968         wxRichTextApplyStyle(attr
, GetAttributes()); 
4971         attr 
= GetAttributes(); 
4976 /// Create default tabstop array 
4977 void wxRichTextParagraph::InitDefaultTabs() 
4979     // create a default tab list at 10 mm each. 
4980     for (int i 
= 0; i 
< 20; ++i
) 
4982         sm_defaultTabs
.Add(i
*100); 
4986 /// Clear default tabstop array 
4987 void wxRichTextParagraph::ClearDefaultTabs() 
4989     sm_defaultTabs
.Clear(); 
4992 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
) 
4994     wxRichTextObjectList::compatibility_iterator node 
= GetChildren().GetFirst(); 
4997         wxRichTextAnchoredObject
* anchored 
= wxDynamicCast(node
->GetData(), wxRichTextAnchoredObject
); 
4998         if (anchored 
&& anchored
->IsFloating()) 
5002             anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
); 
5003             wxRichTextAnchoredObjectAttr attr 
= anchored
->GetAnchoredAttr(); 
5004             int pos 
= floatCollector
->GetFitPosition(attr
.m_floating
, rect
.y 
+ attr
.m_offset
, size
.y
); 
5006             /* Update the offset */ 
5007             attr
.m_offset 
= pos 
- rect
.y
; 
5008             anchored
->SetAnchoredAttr(attr
); 
5010             if (attr
.m_floating 
== wxRICHTEXT_FLOAT_LEFT
) 
5012             else if (attr
.m_floating 
== wxRICHTEXT_FLOAT_RIGHT
) 
5013                 x 
= rect
.width 
- size
.x
; 
5014             anchored
->SetPosition(wxPoint(x
, pos
)); 
5015             anchored
->SetCachedSize(size
); 
5016             floatCollector
->CollectFloat(this, anchored
); 
5019         node 
= node
->GetNext(); 
5023 /// Get the first position from pos that has a line break character. 
5024 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
) 
5026     wxRichTextObjectList::compatibility_iterator node 
= m_children
.GetFirst(); 
5029         wxRichTextObject
* obj 
= node
->GetData(); 
5030         if (pos 
>= obj
->GetRange().GetStart() && pos 
<= obj
->GetRange().GetEnd()) 
5032             wxRichTextPlainText
* textObj 
= wxDynamicCast(obj
, wxRichTextPlainText
); 
5035                 long breakPos 
= textObj
->GetFirstLineBreakPosition(pos
); 
5040         node 
= node
->GetNext(); 
5047  * This object represents a line in a paragraph, and stores 
5048  * offsets from the start of the paragraph representing the 
5049  * start and end positions of the line. 
5052 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
) 
5058 void wxRichTextLine::Init(wxRichTextParagraph
* parent
) 
5061     m_range
.SetRange(-1, -1); 
5062     m_pos 
= wxPoint(0, 0); 
5063     m_size 
= wxSize(0, 0); 
5065 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING 
5066     m_objectSizes
.Clear(); 
5071 void wxRichTextLine::Copy(const wxRichTextLine
& obj
) 
5073     m_range 
= obj
.m_range
; 
5074 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING 
5075     m_objectSizes 
= obj
.m_objectSizes
; 
5079 /// Get the absolute object position 
5080 wxPoint 
wxRichTextLine::GetAbsolutePosition() const 
5082     return m_parent
->GetPosition() + m_pos
; 
5085 /// Get the absolute range 
5086 wxRichTextRange 
wxRichTextLine::GetAbsoluteRange() const 
5088     wxRichTextRange 
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0); 
5089     range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1); 
5094  * wxRichTextPlainText 
5095  * This object represents a single piece of text. 
5098 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
) 
5100 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxTextAttr
* style
): 
5101     wxRichTextObject(parent
) 
5104         SetAttributes(*style
); 
5109 #define USE_KERNING_FIX 1 
5111 // If insufficient tabs are defined, this is the tab width used 
5112 #define WIDTH_FOR_DEFAULT_TABS 50 
5115 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int descent
, int WXUNUSED(style
)) 
5117     wxRichTextParagraph
* para 
= wxDynamicCast(GetParent(), wxRichTextParagraph
); 
5118     wxASSERT (para 
!= NULL
); 
5120     wxTextAttr 
textAttr(para 
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes()); 
5122     int offset 
= GetRange().GetStart(); 
5124     // Replace line break characters with spaces 
5125     wxString str 
= m_text
; 
5126     wxString toRemove 
= wxRichTextLineBreakChar
; 
5127     str
.Replace(toRemove
, wxT(" ")); 
5128     if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
)) 
5131     long len 
= range
.GetLength(); 
5132     wxString stringChunk 
= str
.Mid(range
.GetStart() - offset
, (size_t) len
); 
5134     // Test for the optimized situations where all is selected, or none 
5137     wxFont 
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
)); 
5138     wxCheckSetFont(dc
, textFont
); 
5139     int charHeight 
= dc
.GetCharHeight(); 
5142     if ( textFont
.Ok() ) 
5144         if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) ) 
5146             double size 
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
; 
5147             textFont
.SetPointSize( static_cast<int>(size
) ); 
5150             wxCheckSetFont(dc
, textFont
); 
5152         else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) 
5154             double size 
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
; 
5155             textFont
.SetPointSize( static_cast<int>(size
) ); 
5157             int sub_height 
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
); 
5158             y 
= rect
.y 
+ (rect
.height 
- sub_height 
+ (descent 
- m_descent
)); 
5159             wxCheckSetFont(dc
, textFont
); 
5164             y 
= rect
.y 
+ (rect
.height 
- charHeight 
- (descent 
- m_descent
)); 
5170         y 
= rect
.y 
+ (rect
.height 
- charHeight 
- (descent 
- m_descent
)); 
5173     // (a) All selected. 
5174     if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd()) 
5176         DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true); 
5178     // (b) None selected. 
5179     else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd()) 
5181         // Draw all unselected 
5182         DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false); 
5186         // (c) Part selected, part not 
5187         // Let's draw unselected chunk, selected chunk, then unselected chunk. 
5189         dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
); 
5191         // 1. Initial unselected chunk, if any, up until start of selection. 
5192         if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd()) 
5194             int r1 
= range
.GetStart(); 
5195             int s1 
= selectionRange
.GetStart()-1; 
5196             int fragmentLen 
= s1 
- r1 
+ 1; 
5197             if (fragmentLen 
< 0) 
5199                 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 
- offset
), (int)fragmentLen
); 
5201             wxString stringFragment 
= str
.Mid(r1 
- offset
, fragmentLen
); 
5203             DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false); 
5206             if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
) 
5208                 // Compensate for kerning difference 
5209                 wxString 
stringFragment2(str
.Mid(r1 
- offset
, fragmentLen
+1)); 
5210                 wxString 
stringFragment3(str
.Mid(r1 
- offset 
+ fragmentLen
, 1)); 
5212                 wxCoord w1
, h1
, w2
, h2
, w3
, h3
; 
5213                 dc
.GetTextExtent(stringFragment
,  & w1
, & h1
); 
5214                 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
); 
5215                 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
); 
5217                 int kerningDiff 
= (w1 
+ w3
) - w2
; 
5218                 x 
= x 
- kerningDiff
; 
5223         // 2. Selected chunk, if any. 
5224         if (selectionRange
.GetEnd() >= range
.GetStart()) 
5226             int s1 
= wxMax(selectionRange
.GetStart(), range
.GetStart()); 
5227             int s2 
= wxMin(selectionRange
.GetEnd(), range
.GetEnd()); 
5229             int fragmentLen 
= s2 
- s1 
+ 1; 
5230             if (fragmentLen 
< 0) 
5232                 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 
- offset
), (int)fragmentLen
); 
5234             wxString stringFragment 
= str
.Mid(s1 
- offset
, fragmentLen
); 
5236             DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true); 
5239             if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
) 
5241                 // Compensate for kerning difference 
5242                 wxString 
stringFragment2(str
.Mid(s1 
- offset
, fragmentLen
+1)); 
5243                 wxString 
stringFragment3(str
.Mid(s1 
- offset 
+ fragmentLen
, 1)); 
5245                 wxCoord w1
, h1
, w2
, h2
, w3
, h3
; 
5246                 dc
.GetTextExtent(stringFragment
,  & w1
, & h1
); 
5247                 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
); 
5248                 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
); 
5250                 int kerningDiff 
= (w1 
+ w3
) - w2
; 
5251                 x 
= x 
- kerningDiff
; 
5256         // 3. Remaining unselected chunk, if any 
5257         if (selectionRange
.GetEnd() < range
.GetEnd()) 
5259             int s2 
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd()); 
5260             int r2 
= range
.GetEnd(); 
5262             int fragmentLen 
= r2 
- s2 
+ 1; 
5263             if (fragmentLen 
< 0) 
5265                 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 
- offset
), (int)fragmentLen
); 
5267             wxString stringFragment 
= str
.Mid(s2 
- offset
, fragmentLen
); 
5269             DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false); 
5276 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
) 
5278     bool hasTabs 
= (str
.Find(wxT('\t')) != wxNOT_FOUND
); 
5280     wxArrayInt tabArray
; 
5284         if (attr
.GetTabs().IsEmpty()) 
5285             tabArray 
= wxRichTextParagraph::GetDefaultTabs(); 
5287             tabArray 
= attr
.GetTabs(); 
5288         tabCount 
= tabArray
.GetCount(); 
5290         for (int i 
= 0; i 
< tabCount
; ++i
) 
5292             int pos 
= tabArray
[i
]; 
5293             pos 
= ConvertTenthsMMToPixels(dc
, pos
); 
5300     int nextTabPos 
= -1; 
5306         wxColour 
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
)); 
5307         wxColour 
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
)); 
5309         wxCheckSetBrush(dc
, wxBrush(highlightColour
)); 
5310         wxCheckSetPen(dc
, wxPen(highlightColour
)); 
5311         dc
.SetTextForeground(highlightTextColour
); 
5312         dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
); 
5316         dc
.SetTextForeground(attr
.GetTextColour()); 
5318         if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk()) 
5320             dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
); 
5321             dc
.SetTextBackground(attr
.GetBackgroundColour()); 
5324             dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
); 
5330         // the string has a tab 
5331         // break up the string at the Tab 
5332         wxString stringChunk 
= str
.BeforeFirst(wxT('\t')); 
5333         str 
= str
.AfterFirst(wxT('\t')); 
5334         dc
.GetTextExtent(stringChunk
, & w
, & h
); 
5336         bool not_found 
= true; 
5337         for (int i 
= 0; i 
< tabCount 
&& not_found
; ++i
) 
5339             nextTabPos 
= tabArray
.Item(i
) + x_orig
; 
5341             // Find the next tab position. 
5342             // Even if we're at the end of the tab array, we must still draw the chunk. 
5344             if (nextTabPos 
> tabPos 
|| (i 
== (tabCount 
- 1))) 
5346                 if (nextTabPos 
<= tabPos
) 
5348                     int defaultTabWidth 
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
); 
5349                     nextTabPos 
= tabPos 
+ defaultTabWidth
; 
5356                     wxRect 
selRect(x
, rect
.y
, w
, rect
.GetHeight()); 
5357                     dc
.DrawRectangle(selRect
); 
5359                 dc
.DrawText(stringChunk
, x
, y
); 
5361                 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
)) 
5363                     wxPen oldPen 
= dc
.GetPen(); 
5364                     wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1)); 
5365                     dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5)); 
5366                     wxCheckSetPen(dc
, oldPen
); 
5372         hasTabs 
= (str
.Find(wxT('\t')) != wxNOT_FOUND
); 
5377         dc
.GetTextExtent(str
, & w
, & h
); 
5380             wxRect 
selRect(x
, rect
.y
, w
, rect
.GetHeight()); 
5381             dc
.DrawRectangle(selRect
); 
5383         dc
.DrawText(str
, x
, y
); 
5385         if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
)) 
5387             wxPen oldPen 
= dc
.GetPen(); 
5388             wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1)); 
5389             dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5)); 
5390             wxCheckSetPen(dc
, oldPen
); 
5399 /// Lay the item out 
5400 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
)) 
5402     // Only lay out if we haven't already cached the size 
5404         GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0)); 
5410 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
) 
5412     wxRichTextObject::Copy(obj
); 
5414     m_text 
= obj
.m_text
; 
5417 /// Get/set the object size for the given range. Returns false if the range 
5418 /// is invalid for this object. 
5419 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const 
5421     if (!range
.IsWithin(GetRange())) 
5424     wxRichTextParagraph
* para 
= wxDynamicCast(GetParent(), wxRichTextParagraph
); 
5425     wxASSERT (para 
!= NULL
); 
5427     wxTextAttr 
textAttr(para 
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes()); 
5429     // Always assume unformatted text, since at this level we have no knowledge 
5430     // of line breaks - and we don't need it, since we'll calculate size within 
5431     // formatted text by doing it in chunks according to the line ranges 
5433     bool bScript(false); 
5434     wxFont 
font(GetBuffer()->GetFontTable().FindFont(textAttr
)); 
5437         if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) 
5438             || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) ) 
5440             wxFont textFont 
= font
; 
5441             double size 
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
; 
5442             textFont
.SetPointSize( static_cast<int>(size
) ); 
5443             wxCheckSetFont(dc
, textFont
); 
5448             wxCheckSetFont(dc
, font
); 
5452     bool haveDescent 
= false; 
5453     int startPos 
= range
.GetStart() - GetRange().GetStart(); 
5454     long len 
= range
.GetLength(); 
5456     wxString 
str(m_text
); 
5457     wxString toReplace 
= wxRichTextLineBreakChar
; 
5458     str
.Replace(toReplace
, wxT(" ")); 
5460     wxString stringChunk 
= str
.Mid(startPos
, (size_t) len
); 
5462     if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
)) 
5463         stringChunk
.MakeUpper(); 
5467     if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
) 
5469         // the string has a tab 
5470         wxArrayInt tabArray
; 
5471         if (textAttr
.GetTabs().IsEmpty()) 
5472             tabArray 
= wxRichTextParagraph::GetDefaultTabs(); 
5474             tabArray 
= textAttr
.GetTabs(); 
5476         int tabCount 
= tabArray
.GetCount(); 
5478         for (int i 
= 0; i 
< tabCount
; ++i
) 
5480             int pos 
= tabArray
[i
]; 
5481             pos 
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
); 
5485         int nextTabPos 
= -1; 
5487         while (stringChunk
.Find(wxT('\t')) >= 0) 
5489             int absoluteWidth 
= 0; 
5491             // the string has a tab 
5492             // break up the string at the Tab 
5493             wxString stringFragment 
= stringChunk
.BeforeFirst(wxT('\t')); 
5494             stringChunk 
= stringChunk
.AfterFirst(wxT('\t')); 
5499                 if (partialExtents
->GetCount() > 0) 
5500                     oldWidth 
= (*partialExtents
)[partialExtents
->GetCount()-1]; 
5504                 // Add these partial extents 
5506                 dc
.GetPartialTextExtents(stringFragment
, p
); 
5508                 for (j 
= 0; j 
< p
.GetCount(); j
++) 
5509                     partialExtents
->Add(oldWidth 
+ p
[j
]); 
5511                 if (partialExtents
->GetCount() > 0) 
5512                     absoluteWidth 
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + position
.x
; 
5514                     absoluteWidth 
= position
.x
; 
5518                 dc
.GetTextExtent(stringFragment
, & w
, & h
); 
5520                 absoluteWidth 
= width 
+ position
.x
; 
5524             bool notFound 
= true; 
5525             for (int i 
= 0; i 
< tabCount 
&& notFound
; ++i
) 
5527                 nextTabPos 
= tabArray
.Item(i
); 
5529                 // Find the next tab position. 
5530                 // Even if we're at the end of the tab array, we must still process the chunk. 
5532                 if (nextTabPos 
> absoluteWidth 
|| (i 
== (tabCount 
- 1))) 
5534                     if (nextTabPos 
<= absoluteWidth
) 
5536                         int defaultTabWidth 
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
); 
5537                         nextTabPos 
= absoluteWidth 
+ defaultTabWidth
; 
5541                     width 
= nextTabPos 
- position
.x
; 
5544                         partialExtents
->Add(width
); 
5550     if (!stringChunk
.IsEmpty()) 
5555             if (partialExtents
->GetCount() > 0) 
5556                 oldWidth 
= (*partialExtents
)[partialExtents
->GetCount()-1]; 
5560             // Add these partial extents 
5562             dc
.GetPartialTextExtents(stringChunk
, p
); 
5564             for (j 
= 0; j 
< p
.GetCount(); j
++) 
5565                 partialExtents
->Add(oldWidth 
+ p
[j
]); 
5569             dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
); 
5577         int charHeight 
= dc
.GetCharHeight(); 
5578         if ((*partialExtents
).GetCount() > 0) 
5579             w 
= (*partialExtents
)[partialExtents
->GetCount()-1]; 
5582         size 
= wxSize(w
, charHeight
); 
5586         size 
= wxSize(width
, dc
.GetCharHeight()); 
5590         dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
); 
5598 /// Do a split, returning an object containing the second part, and setting 
5599 /// the first part in 'this'. 
5600 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
) 
5602     long index 
= pos 
- GetRange().GetStart(); 
5604     if (index 
< 0 || index 
>= (int) m_text
.length()) 
5607     wxString firstPart 
= m_text
.Mid(0, index
); 
5608     wxString secondPart 
= m_text
.Mid(index
); 
5612     wxRichTextPlainText
* newObject 
= new wxRichTextPlainText(secondPart
); 
5613     newObject
->SetAttributes(GetAttributes()); 
5615     newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd())); 
5616     GetRange().SetEnd(pos
-1); 
5622 void wxRichTextPlainText::CalculateRange(long start
, long& end
) 
5624     end 
= start 
+ m_text
.length() - 1; 
5625     m_range
.SetRange(start
, end
); 
5629 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
) 
5631     wxRichTextRange r 
= range
; 
5633     r
.LimitTo(GetRange()); 
5635     if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd()) 
5641     long startIndex 
= r
.GetStart() - GetRange().GetStart(); 
5642     long len 
= r
.GetLength(); 
5644     m_text 
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
); 
5648 /// Get text for the given range. 
5649 wxString 
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const 
5651     wxRichTextRange r 
= range
; 
5653     r
.LimitTo(GetRange()); 
5655     long startIndex 
= r
.GetStart() - GetRange().GetStart(); 
5656     long len 
= r
.GetLength(); 
5658     return m_text
.Mid(startIndex
, len
); 
5661 /// Returns true if this object can merge itself with the given one. 
5662 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const 
5664     return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) && 
5665         (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes())); 
5668 /// Returns true if this object merged itself with the given one. 
5669 /// The calling code will then delete the given object. 
5670 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
) 
5672     wxRichTextPlainText
* textObject 
= wxDynamicCast(object
, wxRichTextPlainText
); 
5673     wxASSERT( textObject 
!= NULL 
); 
5677         m_text 
+= textObject
->GetText(); 
5678         wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes()); 
5685 /// Dump to output stream for debugging 
5686 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
) 
5688     wxRichTextObject::Dump(stream
); 
5689     stream 
<< m_text 
<< wxT("\n"); 
5692 /// Get the first position from pos that has a line break character. 
5693 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
) 
5696     int len 
= m_text
.length(); 
5697     int startPos 
= pos 
- m_range
.GetStart(); 
5698     for (i 
= startPos
; i 
< len
; i
++) 
5700         wxChar ch 
= m_text
[i
]; 
5701         if (ch 
== wxRichTextLineBreakChar
) 
5703             return i 
+ m_range
.GetStart(); 
5711  * This is a kind of box, used to represent the whole buffer 
5714 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
) 
5716 wxList                  
wxRichTextBuffer::sm_handlers
; 
5717 wxRichTextRenderer
*     wxRichTextBuffer::sm_renderer 
= NULL
; 
5718 int                     wxRichTextBuffer::sm_bulletRightMargin 
= 20; 
5719 float                   wxRichTextBuffer::sm_bulletProportion 
= (float) 0.3; 
5722 void wxRichTextBuffer::Init() 
5724     m_commandProcessor 
= new wxCommandProcessor
; 
5725     m_styleSheet 
= NULL
; 
5727     m_batchedCommandDepth 
= 0; 
5728     m_batchedCommand 
= NULL
; 
5735 wxRichTextBuffer::~wxRichTextBuffer() 
5737     delete m_commandProcessor
; 
5738     delete m_batchedCommand
; 
5741     ClearEventHandlers(); 
5744 void wxRichTextBuffer::ResetAndClearCommands() 
5748     GetCommandProcessor()->ClearCommands(); 
5751     Invalidate(wxRICHTEXT_ALL
); 
5754 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
) 
5756     wxRichTextParagraphLayoutBox::Copy(obj
); 
5758     m_styleSheet 
= obj
.m_styleSheet
; 
5759     m_modified 
= obj
.m_modified
; 
5760     m_batchedCommandDepth 
= obj
.m_batchedCommandDepth
; 
5761     m_batchedCommand 
= obj
.m_batchedCommand
; 
5762     m_suppressUndo 
= obj
.m_suppressUndo
; 
5765 /// Push style sheet to top of stack 
5766 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
) 
5769         styleSheet
->InsertSheet(m_styleSheet
); 
5771     SetStyleSheet(styleSheet
); 
5776 /// Pop style sheet from top of stack 
5777 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet() 
5781         wxRichTextStyleSheet
* oldSheet 
= m_styleSheet
; 
5782         m_styleSheet 
= oldSheet
->GetNextSheet(); 
5791 /// Submit command to insert paragraphs 
5792 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
) 
5794     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
5796     wxTextAttr 
attr(GetDefaultStyle()); 
5798     wxTextAttr
* p 
= NULL
; 
5799     wxTextAttr paraAttr
; 
5800     if (flags 
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
) 
5802         paraAttr 
= GetStyleForNewParagraph(pos
); 
5803         if (!paraAttr
.IsDefault()) 
5809     action
->GetNewParagraphs() = paragraphs
; 
5811     if (p 
&& !p
->IsDefault()) 
5813         for (wxRichTextObjectList::compatibility_iterator node 
= action
->GetNewParagraphs().GetChildren().GetFirst(); node
; node 
= node
->GetNext()) 
5815             wxRichTextObject
* child 
= node
->GetData(); 
5816             child
->SetAttributes(*p
); 
5820     action
->SetPosition(pos
); 
5822     wxRichTextRange range 
= wxRichTextRange(pos
, pos 
+ paragraphs
.GetRange().GetEnd() - 1); 
5823     if (!paragraphs
.GetPartialParagraph()) 
5824         range
.SetEnd(range
.GetEnd()+1); 
5826     // Set the range we'll need to delete in Undo 
5827     action
->SetRange(range
); 
5829     SubmitAction(action
); 
5834 /// Submit command to insert the given text 
5835 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
) 
5837     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
5839     wxTextAttr
* p 
= NULL
; 
5840     wxTextAttr paraAttr
; 
5841     if (flags 
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
) 
5843         // Get appropriate paragraph style 
5844         paraAttr 
= GetStyleForNewParagraph(pos
, false, false); 
5845         if (!paraAttr
.IsDefault()) 
5849     action
->GetNewParagraphs().AddParagraphs(text
, p
); 
5851     int length 
= action
->GetNewParagraphs().GetRange().GetLength(); 
5853     if (text
.length() > 0 && text
.Last() != wxT('\n')) 
5855         // Don't count the newline when undoing 
5857         action
->GetNewParagraphs().SetPartialParagraph(true); 
5859     else if (text
.length() > 0 && text
.Last() == wxT('\n')) 
5862     action
->SetPosition(pos
); 
5864     // Set the range we'll need to delete in Undo 
5865     action
->SetRange(wxRichTextRange(pos
, pos 
+ length 
- 1)); 
5867     SubmitAction(action
); 
5872 /// Submit command to insert the given text 
5873 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
) 
5875     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
5877     wxTextAttr
* p 
= NULL
; 
5878     wxTextAttr paraAttr
; 
5879     if (flags 
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
) 
5881         paraAttr 
= GetStyleForNewParagraph(pos
, false, true /* look for next paragraph style */); 
5882         if (!paraAttr
.IsDefault()) 
5886     wxTextAttr 
attr(GetDefaultStyle()); 
5888     wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(wxEmptyString
, this, & attr
); 
5889     action
->GetNewParagraphs().AppendChild(newPara
); 
5890     action
->GetNewParagraphs().UpdateRanges(); 
5891     action
->GetNewParagraphs().SetPartialParagraph(false); 
5892     wxRichTextParagraph
* para 
= GetParagraphAtPosition(pos
, false); 
5896         newPara
->SetAttributes(*p
); 
5898     if (flags 
& wxRICHTEXT_INSERT_INTERACTIVE
) 
5900         if (para 
&& para
->GetRange().GetEnd() == pos
) 
5903         // Now see if we need to number the paragraph. 
5904         if (newPara
->GetAttributes().HasBulletNumber()) 
5906             wxRichTextAttr numberingAttr
; 
5907             if (FindNextParagraphNumber(para
, numberingAttr
)) 
5908                 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
); 
5912     action
->SetPosition(pos
); 
5914     // Use the default character style 
5915     // Use the default character style 
5916     if (!GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst()) 
5918         // Check whether the default style merely reflects the paragraph/basic style, 
5919         // in which case don't apply it. 
5920         wxTextAttrEx 
defaultStyle(GetDefaultStyle()); 
5921         wxTextAttrEx toApply
; 
5924             wxRichTextAttr combinedAttr 
= para
->GetCombinedAttributes(); 
5925             wxTextAttrEx newAttr
; 
5926             // This filters out attributes that are accounted for by the current 
5927             // paragraph/basic style 
5928             wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
); 
5931             toApply 
= defaultStyle
; 
5933         if (!toApply
.IsDefault()) 
5934             newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
); 
5937     // Set the range we'll need to delete in Undo 
5938     action
->SetRange(wxRichTextRange(pos1
, pos1
)); 
5940     SubmitAction(action
); 
5945 /// Submit command to insert the given image 
5946 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
, const wxRichTextAnchoredObjectAttr
& floatAttr
) 
5948     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
5950     wxTextAttr
* p 
= NULL
; 
5951     wxTextAttr paraAttr
; 
5952     if (flags 
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
) 
5954         paraAttr 
= GetStyleForNewParagraph(pos
); 
5955         if (!paraAttr
.IsDefault()) 
5959     wxTextAttr 
attr(GetDefaultStyle()); 
5961     wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(this, & attr
); 
5963         newPara
->SetAttributes(*p
); 
5965     wxRichTextImage
* imageObject 
= new wxRichTextImage(imageBlock
, newPara
); 
5966     newPara
->AppendChild(imageObject
); 
5967     imageObject
->SetAnchoredAttr(floatAttr
); 
5968     action
->GetNewParagraphs().AppendChild(newPara
); 
5969     action
->GetNewParagraphs().UpdateRanges(); 
5971     action
->GetNewParagraphs().SetPartialParagraph(true); 
5973     action
->SetPosition(pos
); 
5975     // Set the range we'll need to delete in Undo 
5976     action
->SetRange(wxRichTextRange(pos
, pos
)); 
5978     SubmitAction(action
); 
5983 // Insert an object with no change of it 
5984 bool wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject 
*object
, wxRichTextCtrl
* ctrl
, int flags
) 
5986     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert object"), wxRICHTEXT_INSERT
, this, ctrl
, false); 
5988     wxTextAttr
* p 
= NULL
; 
5989     wxTextAttr paraAttr
; 
5990     if (flags 
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
) 
5992         paraAttr 
= GetStyleForNewParagraph(pos
); 
5993         if (!paraAttr
.IsDefault()) 
5997     wxTextAttr 
attr(GetDefaultStyle()); 
5999     wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(this, & attr
); 
6001         newPara
->SetAttributes(*p
); 
6003     newPara
->AppendChild(object
); 
6004     action
->GetNewParagraphs().AppendChild(newPara
); 
6005     action
->GetNewParagraphs().UpdateRanges(); 
6007     action
->GetNewParagraphs().SetPartialParagraph(true); 
6009     action
->SetPosition(pos
); 
6011     // Set the range we'll need to delete in Undo 
6012     action
->SetRange(wxRichTextRange(pos
, pos
)); 
6014     SubmitAction(action
); 
6018 /// Get the style that is appropriate for a new paragraph at this position. 
6019 /// If the previous paragraph has a paragraph style name, look up the next-paragraph 
6021 wxTextAttr 
wxRichTextBuffer::GetStyleForNewParagraph(long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const 
6023     wxRichTextParagraph
* para 
= GetParagraphAtPosition(pos
, caretPosition
); 
6027         bool foundAttributes 
= false; 
6029         // Look for a matching paragraph style 
6030         if (lookUpNewParaStyle 
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && GetStyleSheet()) 
6032             wxRichTextParagraphStyleDefinition
* paraDef 
= GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName()); 
6035                 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style. 
6036                 if (para
->GetRange().GetEnd() == pos 
&& !paraDef
->GetNextStyle().IsEmpty()) 
6038                     wxRichTextParagraphStyleDefinition
* nextParaDef 
= GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle()); 
6041                         foundAttributes 
= true; 
6042                         attr 
= nextParaDef
->GetStyleMergedWithBase(GetStyleSheet()); 
6046                 // If we didn't find the 'next style', use this style instead. 
6047                 if (!foundAttributes
) 
6049                     foundAttributes 
= true; 
6050                     attr 
= paraDef
->GetStyleMergedWithBase(GetStyleSheet()); 
6055         // Also apply list style if present 
6056         if (lookUpNewParaStyle 
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && GetStyleSheet()) 
6058             wxRichTextListStyleDefinition
* listDef 
= GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName()); 
6061                 int thisIndent 
= para
->GetAttributes().GetLeftIndent(); 
6062                 int thisLevel 
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
); 
6064                 // Apply the overall list style, and item style for this level 
6065                 wxRichTextAttr 
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, GetStyleSheet())); 
6066                 wxRichTextApplyStyle(attr
, listStyle
); 
6067                 attr
.SetOutlineLevel(thisLevel
); 
6068                 if (para
->GetAttributes().HasBulletNumber()) 
6069                     attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber()); 
6073         if (!foundAttributes
) 
6075             attr 
= para
->GetAttributes(); 
6076             int flags 
= attr
.GetFlags(); 
6078             // Eliminate character styles 
6079             flags 
&= ( (~ wxTEXT_ATTR_FONT
) | 
6080                     (~ wxTEXT_ATTR_TEXT_COLOUR
) | 
6081                     (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) ); 
6082             attr
.SetFlags(flags
); 
6088         return wxTextAttr(); 
6091 /// Submit command to delete this range 
6092 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
) 
6094     wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, this, ctrl
); 
6096     action
->SetPosition(ctrl
->GetCaretPosition()); 
6098     // Set the range to delete 
6099     action
->SetRange(range
); 
6101     // Copy the fragment that we'll need to restore in Undo 
6102     CopyFragment(range
, action
->GetOldParagraphs()); 
6104     // See if we're deleting a paragraph marker, in which case we need to 
6105     // make a note not to copy the attributes from the 2nd paragraph to the 1st. 
6106     if (range
.GetStart() == range
.GetEnd()) 
6108         wxRichTextParagraph
* para 
= GetParagraphAtPosition(range
.GetStart()); 
6109         if (para 
&& para
->GetRange().GetEnd() == range
.GetEnd()) 
6111             wxRichTextParagraph
* nextPara 
= GetParagraphAtPosition(range
.GetStart()+1); 
6112             if (nextPara 
&& nextPara 
!= para
) 
6114                 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes()); 
6115                 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
); 
6120     SubmitAction(action
); 
6125 /// Collapse undo/redo commands 
6126 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
) 
6128     if (m_batchedCommandDepth 
== 0) 
6130         wxASSERT(m_batchedCommand 
== NULL
); 
6131         if (m_batchedCommand
) 
6133             GetCommandProcessor()->Store(m_batchedCommand
); 
6135         m_batchedCommand 
= new wxRichTextCommand(cmdName
); 
6138     m_batchedCommandDepth 
++; 
6143 /// Collapse undo/redo commands 
6144 bool wxRichTextBuffer::EndBatchUndo() 
6146     m_batchedCommandDepth 
--; 
6148     wxASSERT(m_batchedCommandDepth 
>= 0); 
6149     wxASSERT(m_batchedCommand 
!= NULL
); 
6151     if (m_batchedCommandDepth 
== 0) 
6153         GetCommandProcessor()->Store(m_batchedCommand
); 
6154         m_batchedCommand 
= NULL
; 
6160 /// Submit immediately, or delay according to whether collapsing is on 
6161 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
) 
6163     if (BatchingUndo() && m_batchedCommand 
&& !SuppressingUndo()) 
6165         wxRichTextCommand
* cmd 
= new wxRichTextCommand(action
->GetName()); 
6166         cmd
->AddAction(action
); 
6168         cmd
->GetActions().Clear(); 
6171         m_batchedCommand
->AddAction(action
); 
6175         wxRichTextCommand
* cmd 
= new wxRichTextCommand(action
->GetName()); 
6176         cmd
->AddAction(action
); 
6178         // Only store it if we're not suppressing undo. 
6179         return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo()); 
6185 /// Begin suppressing undo/redo commands. 
6186 bool wxRichTextBuffer::BeginSuppressUndo() 
6193 /// End suppressing undo/redo commands. 
6194 bool wxRichTextBuffer::EndSuppressUndo() 
6201 /// Begin using a style 
6202 bool wxRichTextBuffer::BeginStyle(const wxTextAttr
& style
) 
6204     wxTextAttr 
newStyle(GetDefaultStyle()); 
6206     // Save the old default style 
6207     m_attributeStack
.Append((wxObject
*) new wxTextAttr(GetDefaultStyle())); 
6209     wxRichTextApplyStyle(newStyle
, style
); 
6210     newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags()); 
6212     SetDefaultStyle(newStyle
); 
6218 bool wxRichTextBuffer::EndStyle() 
6220     if (!m_attributeStack
.GetFirst()) 
6222         wxLogDebug(_("Too many EndStyle calls!")); 
6226     wxList::compatibility_iterator node 
= m_attributeStack
.GetLast(); 
6227     wxTextAttr
* attr 
= (wxTextAttr
*)node
->GetData(); 
6228     m_attributeStack
.Erase(node
); 
6230     SetDefaultStyle(*attr
); 
6237 bool wxRichTextBuffer::EndAllStyles() 
6239     while (m_attributeStack
.GetCount() != 0) 
6244 /// Clear the style stack 
6245 void wxRichTextBuffer::ClearStyleStack() 
6247     for (wxList::compatibility_iterator node 
= m_attributeStack
.GetFirst(); node
; node 
= node
->GetNext()) 
6248         delete (wxTextAttr
*) node
->GetData(); 
6249     m_attributeStack
.Clear(); 
6252 /// Begin using bold 
6253 bool wxRichTextBuffer::BeginBold() 
6256     attr
.SetFontWeight(wxFONTWEIGHT_BOLD
); 
6258     return BeginStyle(attr
); 
6261 /// Begin using italic 
6262 bool wxRichTextBuffer::BeginItalic() 
6265     attr
.SetFontStyle(wxFONTSTYLE_ITALIC
); 
6267     return BeginStyle(attr
); 
6270 /// Begin using underline 
6271 bool wxRichTextBuffer::BeginUnderline() 
6274     attr
.SetFontUnderlined(true); 
6276     return BeginStyle(attr
); 
6279 /// Begin using point size 
6280 bool wxRichTextBuffer::BeginFontSize(int pointSize
) 
6283     attr
.SetFontSize(pointSize
); 
6285     return BeginStyle(attr
); 
6288 /// Begin using this font 
6289 bool wxRichTextBuffer::BeginFont(const wxFont
& font
) 
6294     return BeginStyle(attr
); 
6297 /// Begin using this colour 
6298 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
) 
6301     attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
); 
6302     attr
.SetTextColour(colour
); 
6304     return BeginStyle(attr
); 
6307 /// Begin using alignment 
6308 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
) 
6311     attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
); 
6312     attr
.SetAlignment(alignment
); 
6314     return BeginStyle(attr
); 
6317 /// Begin left indent 
6318 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
) 
6321     attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
); 
6322     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
6324     return BeginStyle(attr
); 
6327 /// Begin right indent 
6328 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
) 
6331     attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
); 
6332     attr
.SetRightIndent(rightIndent
); 
6334     return BeginStyle(attr
); 
6337 /// Begin paragraph spacing 
6338 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
) 
6342         flags 
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
; 
6344         flags 
|= wxTEXT_ATTR_PARA_SPACING_AFTER
; 
6347     attr
.SetFlags(flags
); 
6348     attr
.SetParagraphSpacingBefore(before
); 
6349     attr
.SetParagraphSpacingAfter(after
); 
6351     return BeginStyle(attr
); 
6354 /// Begin line spacing 
6355 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
) 
6358     attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
); 
6359     attr
.SetLineSpacing(lineSpacing
); 
6361     return BeginStyle(attr
); 
6364 /// Begin numbered bullet 
6365 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
) 
6368     attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
); 
6369     attr
.SetBulletStyle(bulletStyle
); 
6370     attr
.SetBulletNumber(bulletNumber
); 
6371     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
6373     return BeginStyle(attr
); 
6376 /// Begin symbol bullet 
6377 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
) 
6380     attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
); 
6381     attr
.SetBulletStyle(bulletStyle
); 
6382     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
6383     attr
.SetBulletText(symbol
); 
6385     return BeginStyle(attr
); 
6388 /// Begin standard bullet 
6389 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
) 
6392     attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
); 
6393     attr
.SetBulletStyle(bulletStyle
); 
6394     attr
.SetLeftIndent(leftIndent
, leftSubIndent
); 
6395     attr
.SetBulletName(bulletName
); 
6397     return BeginStyle(attr
); 
6400 /// Begin named character style 
6401 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
) 
6403     if (GetStyleSheet()) 
6405         wxRichTextCharacterStyleDefinition
* def 
= GetStyleSheet()->FindCharacterStyle(characterStyle
); 
6408             wxTextAttr attr 
= def
->GetStyleMergedWithBase(GetStyleSheet()); 
6409             return BeginStyle(attr
); 
6415 /// Begin named paragraph style 
6416 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
) 
6418     if (GetStyleSheet()) 
6420         wxRichTextParagraphStyleDefinition
* def 
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
); 
6423             wxTextAttr attr 
= def
->GetStyleMergedWithBase(GetStyleSheet()); 
6424             return BeginStyle(attr
); 
6430 /// Begin named list style 
6431 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
) 
6433     if (GetStyleSheet()) 
6435         wxRichTextListStyleDefinition
* def 
= GetStyleSheet()->FindListStyle(listStyle
); 
6438             wxTextAttr 
attr(def
->GetCombinedStyleForLevel(level
)); 
6440             attr
.SetBulletNumber(number
); 
6442             return BeginStyle(attr
); 
6449 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
) 
6453     if (!characterStyle
.IsEmpty() && GetStyleSheet()) 
6455         wxRichTextCharacterStyleDefinition
* def 
= GetStyleSheet()->FindCharacterStyle(characterStyle
); 
6458             attr 
= def
->GetStyleMergedWithBase(GetStyleSheet()); 
6463     return BeginStyle(attr
); 
6466 /// Adds a handler to the end 
6467 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler 
*handler
) 
6469     sm_handlers
.Append(handler
); 
6472 /// Inserts a handler at the front 
6473 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler 
*handler
) 
6475     sm_handlers
.Insert( handler 
); 
6478 /// Removes a handler 
6479 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
) 
6481     wxRichTextFileHandler 
*handler 
= FindHandler(name
); 
6484         sm_handlers
.DeleteObject(handler
); 
6492 /// Finds a handler by filename or, if supplied, type 
6493 wxRichTextFileHandler 
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
, 
6494                                                                    wxRichTextFileType imageType
) 
6496     if (imageType 
!= wxRICHTEXT_TYPE_ANY
) 
6497         return FindHandler(imageType
); 
6498     else if (!filename
.IsEmpty()) 
6500         wxString path
, file
, ext
; 
6501         wxFileName::SplitPath(filename
, & path
, & file
, & ext
); 
6502         return FindHandler(ext
, imageType
); 
6509 /// Finds a handler by name 
6510 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
) 
6512     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
6515         wxRichTextFileHandler 
*handler 
= (wxRichTextFileHandler
*)node
->GetData(); 
6516         if (handler
->GetName().Lower() == name
.Lower()) return handler
; 
6518         node 
= node
->GetNext(); 
6523 /// Finds a handler by extension and type 
6524 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
) 
6526     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
6529         wxRichTextFileHandler 
*handler 
= (wxRichTextFileHandler
*)node
->GetData(); 
6530         if ( handler
->GetExtension().Lower() == extension
.Lower() && 
6531             (type 
== wxRICHTEXT_TYPE_ANY 
|| handler
->GetType() == type
) ) 
6533         node 
= node
->GetNext(); 
6538 /// Finds a handler by type 
6539 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
) 
6541     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
6544         wxRichTextFileHandler 
*handler 
= (wxRichTextFileHandler 
*)node
->GetData(); 
6545         if (handler
->GetType() == type
) return handler
; 
6546         node 
= node
->GetNext(); 
6551 void wxRichTextBuffer::InitStandardHandlers() 
6553     if (!FindHandler(wxRICHTEXT_TYPE_TEXT
)) 
6554         AddHandler(new wxRichTextPlainTextHandler
); 
6557 void wxRichTextBuffer::CleanUpHandlers() 
6559     wxList::compatibility_iterator node 
= sm_handlers
.GetFirst(); 
6562         wxRichTextFileHandler
* handler 
= (wxRichTextFileHandler
*)node
->GetData(); 
6563         wxList::compatibility_iterator next 
= node
->GetNext(); 
6568     sm_handlers
.Clear(); 
6571 wxString 
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
) 
6578     wxList::compatibility_iterator node 
= GetHandlers().GetFirst(); 
6582         wxRichTextFileHandler
* handler 
= (wxRichTextFileHandler
*) node
->GetData(); 
6583         if (handler
->IsVisible() && ((save 
&& handler
->CanSave()) || (!save 
&& handler
->CanLoad()))) 
6588                     wildcard 
+= wxT(";"); 
6589                 wildcard 
+= wxT("*.") + handler
->GetExtension(); 
6594                     wildcard 
+= wxT("|"); 
6595                 wildcard 
+= handler
->GetName(); 
6596                 wildcard 
+= wxT(" "); 
6597                 wildcard 
+= _("files"); 
6598                 wildcard 
+= wxT(" (*."); 
6599                 wildcard 
+= handler
->GetExtension(); 
6600                 wildcard 
+= wxT(")|*."); 
6601                 wildcard 
+= handler
->GetExtension(); 
6603                     types
->Add(handler
->GetType()); 
6608         node 
= node
->GetNext(); 
6612         wildcard 
= wxT("(") + wildcard 
+ wxT(")|") + wildcard
; 
6617 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
) 
6619     wxRichTextFileHandler
* handler 
= FindHandlerFilenameOrType(filename
, type
); 
6622         SetDefaultStyle(wxTextAttr()); 
6623         handler
->SetFlags(GetHandlerFlags()); 
6624         bool success 
= handler
->LoadFile(this, filename
); 
6625         Invalidate(wxRICHTEXT_ALL
); 
6633 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
) 
6635     wxRichTextFileHandler
* handler 
= FindHandlerFilenameOrType(filename
, type
); 
6638         handler
->SetFlags(GetHandlerFlags()); 
6639         return handler
->SaveFile(this, filename
); 
6645 /// Load from a stream 
6646 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
) 
6648     wxRichTextFileHandler
* handler 
= FindHandler(type
); 
6651         SetDefaultStyle(wxTextAttr()); 
6652         handler
->SetFlags(GetHandlerFlags()); 
6653         bool success 
= handler
->LoadFile(this, stream
); 
6654         Invalidate(wxRICHTEXT_ALL
); 
6661 /// Save to a stream 
6662 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
) 
6664     wxRichTextFileHandler
* handler 
= FindHandler(type
); 
6667         handler
->SetFlags(GetHandlerFlags()); 
6668         return handler
->SaveFile(this, stream
); 
6674 /// Copy the range to the clipboard 
6675 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
) 
6677     bool success 
= false; 
6678 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ 
6680     if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open()) 
6682         wxTheClipboard
->Clear(); 
6684         // Add composite object 
6686         wxDataObjectComposite
* compositeObject 
= new wxDataObjectComposite(); 
6689             wxString text 
= GetTextForRange(range
); 
6692             text 
= wxTextFile::Translate(text
, wxTextFileType_Dos
); 
6695             compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */); 
6698         // Add rich text buffer data object. This needs the XML handler to be present. 
6700         if (FindHandler(wxRICHTEXT_TYPE_XML
)) 
6702             wxRichTextBuffer
* richTextBuf 
= new wxRichTextBuffer
; 
6703             CopyFragment(range
, *richTextBuf
); 
6705             compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */); 
6708         if (wxTheClipboard
->SetData(compositeObject
)) 
6711         wxTheClipboard
->Close(); 
6720 /// Paste the clipboard content to the buffer 
6721 bool wxRichTextBuffer::PasteFromClipboard(long position
) 
6723     bool success 
= false; 
6724 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ 
6725     if (CanPasteFromClipboard()) 
6727         if (wxTheClipboard
->Open()) 
6729             if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId()))) 
6731                 wxRichTextBufferDataObject data
; 
6732                 wxTheClipboard
->GetData(data
); 
6733                 wxRichTextBuffer
* richTextBuffer 
= data
.GetRichTextBuffer(); 
6736                     InsertParagraphsWithUndo(position
+1, *richTextBuffer
, GetRichTextCtrl(), 0); 
6737                     if (GetRichTextCtrl()) 
6738                         GetRichTextCtrl()->ShowPosition(position 
+ richTextBuffer
->GetRange().GetEnd()); 
6739                     delete richTextBuffer
; 
6742             else if (wxTheClipboard
->IsSupported(wxDF_TEXT
) || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)) 
6744                 wxTextDataObject data
; 
6745                 wxTheClipboard
->GetData(data
); 
6746                 wxString 
text(data
.GetText()); 
6749                 text2
.Alloc(text
.Length()+1); 
6751                 for (i 
= 0; i 
< text
.Length(); i
++) 
6753                     wxChar ch 
= text
[i
]; 
6754                     if (ch 
!= wxT('\r')) 
6758                 wxString text2 
= text
; 
6760                 InsertTextWithUndo(position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
); 
6762                 if (GetRichTextCtrl()) 
6763                     GetRichTextCtrl()->ShowPosition(position 
+ text2
.Length()); 
6767             else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
)) 
6769                 wxBitmapDataObject data
; 
6770                 wxTheClipboard
->GetData(data
); 
6771                 wxBitmap 
bitmap(data
.GetBitmap()); 
6772                 wxImage 
image(bitmap
.ConvertToImage()); 
6774                 wxRichTextAction
* action 
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, GetRichTextCtrl(), false); 
6776                 action
->GetNewParagraphs().AddImage(image
); 
6778                 if (action
->GetNewParagraphs().GetChildCount() == 1) 
6779                     action
->GetNewParagraphs().SetPartialParagraph(true); 
6781                 action
->SetPosition(position
+1); 
6783                 // Set the range we'll need to delete in Undo 
6784                 action
->SetRange(wxRichTextRange(position
+1, position
+1)); 
6786                 SubmitAction(action
); 
6790             wxTheClipboard
->Close(); 
6794     wxUnusedVar(position
); 
6799 /// Can we paste from the clipboard? 
6800 bool wxRichTextBuffer::CanPasteFromClipboard() const 
6802     bool canPaste 
= false; 
6803 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ 
6804     if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open()) 
6806         if (wxTheClipboard
->IsSupported(wxDF_TEXT
) || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
) || 
6807             wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) || 
6808             wxTheClipboard
->IsSupported(wxDF_BITMAP
)) 
6812         wxTheClipboard
->Close(); 
6818 /// Dumps contents of buffer for debugging purposes 
6819 void wxRichTextBuffer::Dump() 
6823         wxStringOutputStream 
stream(& text
); 
6824         wxTextOutputStream 
textStream(stream
); 
6831 /// Add an event handler 
6832 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
) 
6834     m_eventHandlers
.Append(handler
); 
6838 /// Remove an event handler 
6839 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
) 
6841     wxList::compatibility_iterator node 
= m_eventHandlers
.Find(handler
); 
6844         m_eventHandlers
.Erase(node
); 
6854 /// Clear event handlers 
6855 void wxRichTextBuffer::ClearEventHandlers() 
6857     m_eventHandlers
.Clear(); 
6860 /// Send event to event handlers. If sendToAll is true, will send to all event handlers, 
6861 /// otherwise will stop at the first successful one. 
6862 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
) 
6864     bool success 
= false; 
6865     for (wxList::compatibility_iterator node 
= m_eventHandlers
.GetFirst(); node
; node 
= node
->GetNext()) 
6867         wxEvtHandler
* handler 
= (wxEvtHandler
*) node
->GetData(); 
6868         if (handler
->ProcessEvent(event
)) 
6878 /// Set style sheet and notify of the change 
6879 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
) 
6881     wxRichTextStyleSheet
* oldSheet 
= GetStyleSheet(); 
6883     wxWindowID id 
= wxID_ANY
; 
6884     if (GetRichTextCtrl()) 
6885         id 
= GetRichTextCtrl()->GetId(); 
6887     wxRichTextEvent 
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, id
); 
6888     event
.SetEventObject(GetRichTextCtrl()); 
6889     event
.SetOldStyleSheet(oldSheet
); 
6890     event
.SetNewStyleSheet(sheet
); 
6893     if (SendEvent(event
) && !event
.IsAllowed()) 
6895         if (sheet 
!= oldSheet
) 
6901     if (oldSheet 
&& oldSheet 
!= sheet
) 
6904     SetStyleSheet(sheet
); 
6906     event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
); 
6907     event
.SetOldStyleSheet(NULL
); 
6910     return SendEvent(event
); 
6913 /// Set renderer, deleting old one 
6914 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
) 
6918     sm_renderer 
= renderer
; 
6921 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxTextAttr
& bulletAttr
, const wxRect
& rect
) 
6923     if (bulletAttr
.GetTextColour().Ok()) 
6925         wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour())); 
6926         wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour())); 
6930         wxCheckSetPen(dc
, *wxBLACK_PEN
); 
6931         wxCheckSetBrush(dc
, *wxBLACK_BRUSH
); 
6935     if (bulletAttr
.HasFont()) 
6937         font 
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
); 
6940         font 
= (*wxNORMAL_FONT
); 
6942     wxCheckSetFont(dc
, font
); 
6944     int charHeight 
= dc
.GetCharHeight(); 
6946     int bulletWidth 
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion()); 
6947     int bulletHeight 
= bulletWidth
; 
6951     // Calculate the top position of the character (as opposed to the whole line height) 
6952     int y 
= rect
.y 
+ (rect
.height 
- charHeight
); 
6954     // Calculate where the bullet should be positioned 
6955     y 
= y 
+ (charHeight
+1)/2 - (bulletHeight
+1)/2; 
6957     // The margin between a bullet and text. 
6958     int margin 
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin()); 
6960     if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
) 
6961         x 
= rect
.x 
+ rect
.width 
- bulletWidth 
- margin
; 
6962     else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
) 
6963         x 
= x 
+ (rect
.width
)/2 - bulletWidth
/2; 
6965     if (bulletAttr
.GetBulletName() == wxT("standard/square")) 
6967         dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
); 
6969     else if (bulletAttr
.GetBulletName() == wxT("standard/diamond")) 
6972         pts
[0].x 
= x
;                   pts
[0].y 
= y 
+ bulletHeight
/2; 
6973         pts
[1].x 
= x 
+ bulletWidth
/2;   pts
[1].y 
= y
; 
6974         pts
[2].x 
= x 
+ bulletWidth
;     pts
[2].y 
= y 
+ bulletHeight
/2; 
6975         pts
[3].x 
= x 
+ bulletWidth
/2;   pts
[3].y 
= y 
+ bulletHeight
; 
6977         dc
.DrawPolygon(4, pts
); 
6979     else if (bulletAttr
.GetBulletName() == wxT("standard/triangle")) 
6982         pts
[0].x 
= x
;                   pts
[0].y 
= y
; 
6983         pts
[1].x 
= x 
+ bulletWidth
;     pts
[1].y 
= y 
+ bulletHeight
/2; 
6984         pts
[2].x 
= x
;                   pts
[2].y 
= y 
+ bulletHeight
; 
6986         dc
.DrawPolygon(3, pts
); 
6988     else // "standard/circle", and catch-all 
6990         dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
); 
6996 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
) 
7001         if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont()) 
7003             wxTextAttr fontAttr
; 
7004             fontAttr
.SetFontSize(attr
.GetFontSize()); 
7005             fontAttr
.SetFontStyle(attr
.GetFontStyle()); 
7006             fontAttr
.SetFontWeight(attr
.GetFontWeight()); 
7007             fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined()); 
7008             fontAttr
.SetFontFaceName(attr
.GetBulletFont()); 
7009             font 
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
); 
7011         else if (attr
.HasFont()) 
7012             font 
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
); 
7014             font 
= (*wxNORMAL_FONT
); 
7016         wxCheckSetFont(dc
, font
); 
7018         if (attr
.GetTextColour().Ok()) 
7019             dc
.SetTextForeground(attr
.GetTextColour()); 
7021         dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
); 
7023         int charHeight 
= dc
.GetCharHeight(); 
7025         dc
.GetTextExtent(text
, & tw
, & th
); 
7029         // Calculate the top position of the character (as opposed to the whole line height) 
7030         int y 
= rect
.y 
+ (rect
.height 
- charHeight
); 
7032         // The margin between a bullet and text. 
7033         int margin 
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin()); 
7035         if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
) 
7036             x 
= (rect
.x 
+ rect
.width
) - tw 
- margin
; 
7037         else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
) 
7038             x 
= x 
+ (rect
.width
)/2 - tw
/2; 
7040         dc
.DrawText(text
, x
, y
); 
7048 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
)) 
7050     // Currently unimplemented. The intention is to store bitmaps by name in a media store associated 
7051     // with the buffer. The store will allow retrieval from memory, disk or other means. 
7055 /// Enumerate the standard bullet names currently supported 
7056 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
) 
7058     bulletNames
.Add(wxTRANSLATE("standard/circle")); 
7059     bulletNames
.Add(wxTRANSLATE("standard/square")); 
7060     bulletNames
.Add(wxTRANSLATE("standard/diamond")); 
7061     bulletNames
.Add(wxTRANSLATE("standard/triangle")); 
7067  * Module to initialise and clean up handlers 
7070 class wxRichTextModule
: public wxModule
 
7072 DECLARE_DYNAMIC_CLASS(wxRichTextModule
) 
7074     wxRichTextModule() {} 
7077         wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
); 
7078         wxRichTextBuffer::InitStandardHandlers(); 
7079         wxRichTextParagraph::InitDefaultTabs(); 
7084         wxRichTextBuffer::CleanUpHandlers(); 
7085         wxRichTextDecimalToRoman(-1); 
7086         wxRichTextParagraph::ClearDefaultTabs(); 
7087         wxRichTextCtrl::ClearAvailableFontNames(); 
7088         wxRichTextBuffer::SetRenderer(NULL
); 
7092 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
) 
7095 // If the richtext lib is dynamically loaded after the app has already started 
7096 // (such as from wxPython) then the built-in module system will not init this 
7097 // module.  Provide this function to do it manually. 
7098 void wxRichTextModuleInit() 
7100     wxModule
* module = new wxRichTextModule
; 
7102     wxModule::RegisterModule(module); 
7107  * Commands for undo/redo 
7111 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
, 
7112                                      wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
) 
7114     /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, ctrl
, ignoreFirstTime
); 
7117 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
) 
7121 wxRichTextCommand::~wxRichTextCommand() 
7126 void wxRichTextCommand::AddAction(wxRichTextAction
* action
) 
7128     if (!m_actions
.Member(action
)) 
7129         m_actions
.Append(action
); 
7132 bool wxRichTextCommand::Do() 
7134     for (wxList::compatibility_iterator node 
= m_actions
.GetFirst(); node
; node 
= node
->GetNext()) 
7136         wxRichTextAction
* action 
= (wxRichTextAction
*) node
->GetData(); 
7143 bool wxRichTextCommand::Undo() 
7145     for (wxList::compatibility_iterator node 
= m_actions
.GetLast(); node
; node 
= node
->GetPrevious()) 
7147         wxRichTextAction
* action 
= (wxRichTextAction
*) node
->GetData(); 
7154 void wxRichTextCommand::ClearActions() 
7156     WX_CLEAR_LIST(wxList
, m_actions
); 
7164 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
, 
7165                                      wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
) 
7168     m_ignoreThis 
= ignoreFirstTime
; 
7173     m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle()); 
7174     m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle()); 
7176         cmd
->AddAction(this); 
7179 wxRichTextAction::~wxRichTextAction() 
7183 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
) 
7185     // Store a list of line start character and y positions so we can figure out which area 
7186     // we need to refresh 
7188 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING 
7189     // NOTE: we're assuming that the buffer is laid out correctly at this point. 
7190     // If we had several actions, which only invalidate and leave layout until the 
7191     // paint handler is called, then this might not be true. So we may need to switch 
7192     // optimisation on only when we're simply adding text and not simultaneously 
7193     // deleting a selection, for example. Or, we make sure the buffer is laid out correctly 
7194     // first, but of course this means we'll be doing it twice. 
7195     if (!m_buffer
->GetDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly 
7197         wxSize clientSize 
= m_ctrl
->GetClientSize(); 
7198         wxPoint firstVisiblePt 
= m_ctrl
->GetFirstVisiblePoint(); 
7199         int lastY 
= firstVisiblePt
.y 
+ clientSize
.y
; 
7201         wxRichTextParagraph
* para 
= m_buffer
->GetParagraphAtPosition(GetRange().GetStart()); 
7202         wxRichTextObjectList::compatibility_iterator node 
= m_buffer
->GetChildren().Find(para
); 
7205             wxRichTextParagraph
* child 
= (wxRichTextParagraph
*) node
->GetData(); 
7206             wxRichTextLineList::compatibility_iterator node2 
= child
->GetLines().GetFirst(); 
7209                 wxRichTextLine
* line 
= node2
->GetData(); 
7210                 wxPoint pt 
= line
->GetAbsolutePosition(); 
7211                 wxRichTextRange range 
= line
->GetAbsoluteRange(); 
7215                     node2 
= wxRichTextLineList::compatibility_iterator(); 
7216                     node 
= wxRichTextObjectList::compatibility_iterator(); 
7218                 else if (range
.GetStart() > GetPosition() && pt
.y 
>= firstVisiblePt
.y
) 
7220                     optimizationLineCharPositions
.Add(range
.GetStart()); 
7221                     optimizationLineYPositions
.Add(pt
.y
); 
7225                     node2 
= node2
->GetNext(); 
7229                 node 
= node
->GetNext(); 
7235 bool wxRichTextAction::Do() 
7237     m_buffer
->Modify(true); 
7241     case wxRICHTEXT_INSERT
: 
7243             // Store a list of line start character and y positions so we can figure out which area 
7244             // we need to refresh 
7245             wxArrayInt optimizationLineCharPositions
; 
7246             wxArrayInt optimizationLineYPositions
; 
7248 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING 
7249             CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
); 
7252             m_buffer
->InsertFragment(GetRange().GetStart(), m_newParagraphs
); 
7253             m_buffer
->UpdateRanges(); 
7254             m_buffer
->Invalidate(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd())); 
7256             long newCaretPosition 
= GetPosition() + m_newParagraphs
.GetRange().GetLength(); 
7258             // Character position to caret position 
7259             newCaretPosition 
--; 
7261             // Don't take into account the last newline 
7262             if (m_newParagraphs
.GetPartialParagraph()) 
7263                 newCaretPosition 
--; 
7265                 if (m_newParagraphs
.GetChildren().GetCount() > 1) 
7267                     wxRichTextObject
* p 
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData(); 
7268                     if (p
->GetRange().GetLength() == 1) 
7269                         newCaretPosition 
--; 
7272             newCaretPosition 
= wxMin(newCaretPosition
, (m_buffer
->GetRange().GetEnd()-1)); 
7274             UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */); 
7276             wxRichTextEvent 
cmdEvent( 
7277                 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
, 
7278                 m_ctrl 
? m_ctrl
->GetId() : -1); 
7279             cmdEvent
.SetEventObject(m_ctrl 
? (wxObject
*) m_ctrl 
: (wxObject
*) m_buffer
); 
7280             cmdEvent
.SetRange(GetRange()); 
7281             cmdEvent
.SetPosition(GetRange().GetStart()); 
7283             m_buffer
->SendEvent(cmdEvent
); 
7287     case wxRICHTEXT_DELETE
: 
7289             wxArrayInt optimizationLineCharPositions
; 
7290             wxArrayInt optimizationLineYPositions
; 
7292 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING 
7293             CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
); 
7296             m_buffer
->DeleteRange(GetRange()); 
7297             m_buffer
->UpdateRanges(); 
7298             m_buffer
->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); 
7300             long caretPos 
= GetRange().GetStart()-1; 
7301             if (caretPos 
>= m_buffer
->GetRange().GetEnd()) 
7304             UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */); 
7306             wxRichTextEvent 
cmdEvent( 
7307                 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
, 
7308                 m_ctrl 
? m_ctrl
->GetId() : -1); 
7309             cmdEvent
.SetEventObject(m_ctrl 
? (wxObject
*) m_ctrl 
: (wxObject
*) m_buffer
); 
7310             cmdEvent
.SetRange(GetRange()); 
7311             cmdEvent
.SetPosition(GetRange().GetStart()); 
7313             m_buffer
->SendEvent(cmdEvent
); 
7317     case wxRICHTEXT_CHANGE_STYLE
: 
7319             ApplyParagraphs(GetNewParagraphs()); 
7320             m_buffer
->Invalidate(GetRange()); 
7322             UpdateAppearance(GetPosition()); 
7324             wxRichTextEvent 
cmdEvent( 
7325                 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
, 
7326                 m_ctrl 
? m_ctrl
->GetId() : -1); 
7327             cmdEvent
.SetEventObject(m_ctrl 
? (wxObject
*) m_ctrl 
: (wxObject
*) m_buffer
); 
7328             cmdEvent
.SetRange(GetRange()); 
7329             cmdEvent
.SetPosition(GetRange().GetStart()); 
7331             m_buffer
->SendEvent(cmdEvent
); 
7342 bool wxRichTextAction::Undo() 
7344     m_buffer
->Modify(true); 
7348     case wxRICHTEXT_INSERT
: 
7350             wxArrayInt optimizationLineCharPositions
; 
7351             wxArrayInt optimizationLineYPositions
; 
7353 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING 
7354             CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
); 
7357             m_buffer
->DeleteRange(GetRange()); 
7358             m_buffer
->UpdateRanges(); 
7359             m_buffer
->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart())); 
7361             long newCaretPosition 
= GetPosition() - 1; 
7363             UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */); 
7365             wxRichTextEvent 
cmdEvent( 
7366                 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
, 
7367                 m_ctrl 
? m_ctrl
->GetId() : -1); 
7368             cmdEvent
.SetEventObject(m_ctrl 
? (wxObject
*) m_ctrl 
: (wxObject
*) m_buffer
); 
7369             cmdEvent
.SetRange(GetRange()); 
7370             cmdEvent
.SetPosition(GetRange().GetStart()); 
7372             m_buffer
->SendEvent(cmdEvent
); 
7376     case wxRICHTEXT_DELETE
: 
7378             wxArrayInt optimizationLineCharPositions
; 
7379             wxArrayInt optimizationLineYPositions
; 
7381 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING 
7382             CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
); 
7385             m_buffer
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
); 
7386             m_buffer
->UpdateRanges(); 
7387             m_buffer
->Invalidate(GetRange()); 
7389             UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */); 
7391             wxRichTextEvent 
cmdEvent( 
7392                 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
, 
7393                 m_ctrl 
? m_ctrl
->GetId() : -1); 
7394             cmdEvent
.SetEventObject(m_ctrl 
? (wxObject
*) m_ctrl 
: (wxObject
*) m_buffer
); 
7395             cmdEvent
.SetRange(GetRange()); 
7396             cmdEvent
.SetPosition(GetRange().GetStart()); 
7398             m_buffer
->SendEvent(cmdEvent
); 
7402     case wxRICHTEXT_CHANGE_STYLE
: 
7404             ApplyParagraphs(GetOldParagraphs()); 
7405             m_buffer
->Invalidate(GetRange()); 
7407             UpdateAppearance(GetPosition()); 
7409             wxRichTextEvent 
cmdEvent( 
7410                 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
, 
7411                 m_ctrl 
? m_ctrl
->GetId() : -1); 
7412             cmdEvent
.SetEventObject(m_ctrl 
? (wxObject
*) m_ctrl 
: (wxObject
*) m_buffer
); 
7413             cmdEvent
.SetRange(GetRange()); 
7414             cmdEvent
.SetPosition(GetRange().GetStart()); 
7416             m_buffer
->SendEvent(cmdEvent
); 
7427 /// Update the control appearance 
7428 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* WXUNUSED(optimizationLineCharPositions
), wxArrayInt
* WXUNUSED(optimizationLineYPositions
), bool WXUNUSED(isDoCmd
)) 
7432         m_ctrl
->SetCaretPosition(caretPosition
); 
7433         if (!m_ctrl
->IsFrozen()) 
7435             m_ctrl
->LayoutContent(); 
7436             // TODO Refresh the whole client area now 
7437             m_ctrl
->Refresh(false); 
7439 #if wxRICHTEXT_USE_OWN_CARET 
7440             m_ctrl
->PositionCaret(); 
7442             if (sendUpdateEvent
) 
7443                 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
); 
7448 /// Replace the buffer paragraphs with the new ones. 
7449 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
) 
7451     wxRichTextObjectList::compatibility_iterator node 
= fragment
.GetChildren().GetFirst(); 
7454         wxRichTextParagraph
* para 
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
); 
7455         wxASSERT (para 
!= NULL
); 
7457         // We'll replace the existing paragraph by finding the paragraph at this position, 
7458         // delete its node data, and setting a copy as the new node data. 
7459         // TODO: make more efficient by simply swapping old and new paragraph objects. 
7461         wxRichTextParagraph
* existingPara 
= m_buffer
->GetParagraphAtPosition(para
->GetRange().GetStart()); 
7464             wxRichTextObjectList::compatibility_iterator bufferParaNode 
= m_buffer
->GetChildren().Find(existingPara
); 
7467                 wxRichTextParagraph
* newPara 
= new wxRichTextParagraph(*para
); 
7468                 newPara
->SetParent(m_buffer
); 
7470                 bufferParaNode
->SetData(newPara
); 
7472                 delete existingPara
; 
7476         node 
= node
->GetNext(); 
7483  * This stores beginning and end positions for a range of data. 
7486 /// Limit this range to be within 'range' 
7487 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
) 
7489     if (m_start 
< range
.m_start
) 
7490         m_start 
= range
.m_start
; 
7492     if (m_end 
> range
.m_end
) 
7493         m_end 
= range
.m_end
; 
7500  * wxRichTextPlaceHoldingObject implementation 
7503 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlaceHoldingObject
, wxRichTextObject
) 
7505 wxRichTextPlaceHoldingObject::wxRichTextPlaceHoldingObject(wxRichTextObject 
*parent
, wxRichTextAnchoredObject 
*real
) 
7506                             : wxRichTextObject(parent
), m_real(real
) 
7510 wxRichTextPlaceHoldingObject::~wxRichTextPlaceHoldingObject() 
7514 bool wxRichTextPlaceHoldingObject::Draw(wxDC
& WXUNUSED(dc
), const wxRichTextRange
& WXUNUSED(range
), const wxRichTextRange
& WXUNUSED(selectionrange
), const wxRect
& WXUNUSED(rect
), int WXUNUSED(descent
), int WXUNUSED(style
)) 
7519 bool wxRichTextPlaceHoldingObject::Layout(wxDC
& WXUNUSED(dc
), const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
)) 
7521     SetCachedSize(wxSize(0, 0)); 
7525 bool wxRichTextPlaceHoldingObject::GetRangeSize(const wxRichTextRange
& WXUNUSED(range
), wxSize
& size
, int& WXUNUSED(descent
), wxDC
& WXUNUSED(dc
), int WXUNUSED(flags
), wxPoint 
WXUNUSED(position
), wxArrayInt
* partialExtents
) const 
7527     size
.x 
= size
.y 
= 0; 
7529         partialExtents
->Add(0); 
7533 void wxRichTextPlaceHoldingObject::Copy(const wxRichTextPlaceHoldingObject
& obj
) 
7535     wxRichTextObject::Copy(obj
); 
7536     wxASSERT (obj
.m_real
); 
7537     wxRichTextObject
* o 
= obj
.m_real
->Clone(); 
7538     wxASSERT (o
->IsFloatable()); 
7539     wxRichTextAnchoredObject
* anchor 
= wxDynamicCast(o
, wxRichTextAnchoredObject
); 
7541     anchor
->SetPlaceHoldingObject(this); 
7545 void wxRichTextPlaceHoldingObject::SetParent(wxRichTextObject
* parent
) 
7547     wxRichTextObject::SetParent(parent
); 
7550         m_real
->wxRichTextObject::SetParent(parent
); 
7558  * wxRichTextAnchoredObject implementation 
7560 IMPLEMENT_CLASS(wxRichTextAnchoredObject
, wxRichTextObject
) 
7562 wxRichTextAnchoredObject::wxRichTextAnchoredObject(wxRichTextObject
* parent
, const wxRichTextAnchoredObjectAttr
& attr
): 
7563     wxRichTextObject(parent
), m_anchoredAttr(attr
) 
7567 wxRichTextAnchoredObject::~wxRichTextAnchoredObject() 
7571 void wxRichTextAnchoredObject::SetAnchoredAttr(const wxRichTextAnchoredObjectAttr
& attr
) 
7573     m_anchoredAttr 
= attr
; 
7576 void wxRichTextAnchoredObject::Copy(const wxRichTextAnchoredObject
& obj
) 
7578     wxRichTextObject::Copy(obj
); 
7579     m_anchoredAttr 
= obj
.m_anchoredAttr
; 
7582 void wxRichTextAnchoredObject::SetParent(wxRichTextObject
* parent
) 
7584     wxRichTextObject::SetParent(parent
); 
7588  * wxRichTextImage implementation 
7589  * This object represents an image. 
7592 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextAnchoredObject
) 
7594 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxTextAttr
* charStyle
): 
7595     wxRichTextAnchoredObject(parent
) 
7597     m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
); 
7599         SetAttributes(*charStyle
); 
7602 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxTextAttr
* charStyle
): 
7603     wxRichTextAnchoredObject(parent
) 
7605     m_imageBlock 
= imageBlock
; 
7607         SetAttributes(*charStyle
); 
7610 /// Create a cached image at the required size 
7611 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
) 
7613     if (resetCache 
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */) 
7615         if (!m_imageBlock
.IsOk()) 
7619         m_imageBlock
.Load(image
); 
7623         int width 
= image
.GetWidth(); 
7624         int height 
= image
.GetHeight(); 
7626         if (m_anchoredAttr
.m_width 
!= -1) 
7628             // Calculate the user specified length 
7629             if (m_anchoredAttr
.m_unitsW 
== wxRICHTEXT_MM
) 
7631                 width 
= ConvertTenthsMMToPixels(dc
, m_anchoredAttr
.m_width
); 
7635                 width 
= m_anchoredAttr
.m_width
; 
7639         if (m_anchoredAttr
.m_height 
!= -1) 
7641             if (m_anchoredAttr
.m_unitsH 
== wxRICHTEXT_MM
) 
7643                 height 
= ConvertTenthsMMToPixels(dc
, m_anchoredAttr
.m_height
); 
7647                 height 
= m_anchoredAttr
.m_height
; 
7651         if (image
.GetWidth() == width 
&& image
.GetHeight() == height
) 
7652             m_imageCache 
= wxBitmap(image
); 
7655             // If the original width and height is small, e.g. 400 or below, 
7656             // scale up and then down to improve image quality. This can make 
7657             // a big difference, with not much performance hit. 
7658             int upscaleThreshold 
= 400; 
7660             if (image
.GetWidth() <= upscaleThreshold 
|| image
.GetHeight() <= upscaleThreshold
) 
7662                 img 
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2); 
7663                 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
); 
7666                 img 
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
); 
7667             m_imageCache 
= wxBitmap(img
); 
7671     return m_imageCache
.IsOk(); 
7675 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextRange
& selectionRange
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
)) 
7677     // Don't need cached size AFAIK 
7678     // wxSize size = GetCachedSize(); 
7679     if (!LoadImageCache(dc
)) 
7682     int y 
= rect
.y 
+ (rect
.height 
- m_imageCache
.GetHeight()); 
7684     dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true); 
7686     if (selectionRange
.Contains(range
.GetStart())) 
7688         wxCheckSetBrush(dc
, *wxBLACK_BRUSH
); 
7689         wxCheckSetPen(dc
, *wxBLACK_PEN
); 
7690         dc
.SetLogicalFunction(wxINVERT
); 
7691         dc
.DrawRectangle(rect
); 
7692         dc
.SetLogicalFunction(wxCOPY
); 
7698 /// Lay the item out 
7699 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(style
)) 
7701     if (!LoadImageCache(dc
)) 
7704     SetCachedSize(wxSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight())); 
7705     SetPosition(rect
.GetPosition()); 
7710 /// Get/set the object size for the given range. Returns false if the range 
7711 /// is invalid for this object. 
7712 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint 
WXUNUSED(position
), wxArrayInt
* partialExtents
) const 
7714     if (!range
.IsWithin(GetRange())) 
7717     if (!((wxRichTextImage
*)this)->LoadImageCache(dc
)) 
7719         size
.x 
= 0; size
.y 
= 0; 
7721             partialExtents
->Add(0); 
7725     int width 
= m_imageCache
.GetWidth(); 
7726     int height 
= m_imageCache
.GetHeight(); 
7729         partialExtents
->Add(width
); 
7738 void wxRichTextImage::Copy(const wxRichTextImage
& obj
) 
7740     wxRichTextAnchoredObject::Copy(obj
); 
7742     m_imageBlock 
= obj
.m_imageBlock
; 
7745 /// Edit properties via a GUI 
7746 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
) 
7748     wxRichTextImageDialog 
imageDlg(wxGetTopLevelParent(parent
)); 
7749     imageDlg
.SetImageObject(this, buffer
); 
7751     if (imageDlg
.ShowModal() == wxID_OK
) 
7753         imageDlg
.ApplyImageAttr(); 
7765 /// Compare two attribute objects 
7766 bool wxTextAttrEq(const wxTextAttr
& attr1
, const wxTextAttr
& attr2
) 
7768     return (attr1 
== attr2
); 
7771 // Partial equality test taking flags into account 
7772 bool wxTextAttrEqPartial(const wxTextAttr
& attr1
, const wxTextAttr
& attr2
, int flags
) 
7774     return attr1
.EqPartial(attr2
, flags
); 
7778 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
) 
7780     if (tabs1
.GetCount() != tabs2
.GetCount()) 
7784     for (i 
= 0; i 
< tabs1
.GetCount(); i
++) 
7786         if (tabs1
[i
] != tabs2
[i
]) 
7792 bool wxRichTextApplyStyle(wxTextAttr
& destStyle
, const wxTextAttr
& style
, wxTextAttr
* compareWith
) 
7794     return destStyle
.Apply(style
, compareWith
); 
7797 // Remove attributes 
7798 bool wxRichTextRemoveStyle(wxTextAttr
& destStyle
, const wxTextAttr
& style
) 
7800     return wxTextAttr::RemoveStyle(destStyle
, style
); 
7803 /// Combine two bitlists, specifying the bits of interest with separate flags. 
7804 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
) 
7806     return wxTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
); 
7809 /// Compare two bitlists 
7810 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
) 
7812     return wxTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
); 
7815 /// Split into paragraph and character styles 
7816 bool wxRichTextSplitParaCharStyles(const wxTextAttr
& style
, wxTextAttr
& parStyle
, wxTextAttr
& charStyle
) 
7818     return wxTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
); 
7821 /// Convert a decimal to Roman numerals 
7822 wxString 
wxRichTextDecimalToRoman(long n
) 
7824     static wxArrayInt decimalNumbers
; 
7825     static wxArrayString romanNumbers
; 
7830         decimalNumbers
.Clear(); 
7831         romanNumbers
.Clear(); 
7832         return wxEmptyString
; 
7835     if (decimalNumbers
.GetCount() == 0) 
7837         #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r); 
7839         wxRichTextAddDecRom(1000, wxT("M")); 
7840         wxRichTextAddDecRom(900, wxT("CM")); 
7841         wxRichTextAddDecRom(500, wxT("D")); 
7842         wxRichTextAddDecRom(400, wxT("CD")); 
7843         wxRichTextAddDecRom(100, wxT("C")); 
7844         wxRichTextAddDecRom(90, wxT("XC")); 
7845         wxRichTextAddDecRom(50, wxT("L")); 
7846         wxRichTextAddDecRom(40, wxT("XL")); 
7847         wxRichTextAddDecRom(10, wxT("X")); 
7848         wxRichTextAddDecRom(9, wxT("IX")); 
7849         wxRichTextAddDecRom(5, wxT("V")); 
7850         wxRichTextAddDecRom(4, wxT("IV")); 
7851         wxRichTextAddDecRom(1, wxT("I")); 
7857     while (n 
> 0 && i 
< 13) 
7859         if (n 
>= decimalNumbers
[i
]) 
7861             n 
-= decimalNumbers
[i
]; 
7862             roman 
+= romanNumbers
[i
]; 
7869     if (roman
.IsEmpty()) 
7875  * wxRichTextFileHandler 
7876  * Base class for file handlers 
7879 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
) 
7881 #if wxUSE_FFILE && wxUSE_STREAMS 
7882 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer 
*buffer
, const wxString
& filename
) 
7884     wxFFileInputStream 
stream(filename
); 
7886         return LoadFile(buffer
, stream
); 
7891 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer 
*buffer
, const wxString
& filename
) 
7893     wxFFileOutputStream 
stream(filename
); 
7895         return SaveFile(buffer
, stream
); 
7899 #endif // wxUSE_FFILE && wxUSE_STREAMS 
7901 /// Can we handle this filename (if using files)? By default, checks the extension. 
7902 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const 
7904     wxString path
, file
, ext
; 
7905     wxFileName::SplitPath(filename
, & path
, & file
, & ext
); 
7907     return (ext
.Lower() == GetExtension()); 
7911  * wxRichTextTextHandler 
7912  * Plain text handler 
7915 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
) 
7918 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer 
*buffer
, wxInputStream
& stream
) 
7926     while (!stream
.Eof()) 
7928         int ch 
= stream
.GetC(); 
7932             if (ch 
== 10 && lastCh 
!= 13) 
7935             if (ch 
> 0 && ch 
!= 10) 
7942     buffer
->ResetAndClearCommands(); 
7944     buffer
->AddParagraphs(str
); 
7945     buffer
->UpdateRanges(); 
7950 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer 
*buffer
, wxOutputStream
& stream
) 
7955     wxString text 
= buffer
->GetText(); 
7957     wxString newLine 
= wxRichTextLineBreakChar
; 
7958     text
.Replace(newLine
, wxT("\n")); 
7960     wxCharBuffer buf 
= text
.ToAscii(); 
7962     stream
.Write((const char*) buf
, text
.length()); 
7965 #endif // wxUSE_STREAMS 
7968  * Stores information about an image, in binary in-memory form 
7971 wxRichTextImageBlock::wxRichTextImageBlock() 
7976 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject() 
7982 wxRichTextImageBlock::~wxRichTextImageBlock() 
7987 void wxRichTextImageBlock::Init() 
7991     m_imageType 
= wxBITMAP_TYPE_INVALID
; 
7994 void wxRichTextImageBlock::Clear() 
7998     m_imageType 
= wxBITMAP_TYPE_INVALID
; 
8002 // Load the original image into a memory block. 
8003 // If the image is not a JPEG, we must convert it into a JPEG 
8004 // to conserve space. 
8005 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to 
8006 // load the image a 2nd time. 
8008 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
, 
8009                                           wxImage
& image
, bool convertToJPEG
) 
8011     m_imageType 
= imageType
; 
8013     wxString 
filenameToRead(filename
); 
8014     bool removeFile 
= false; 
8016     if (imageType 
== wxBITMAP_TYPE_INVALID
) 
8017         return false; // Could not determine image type 
8019     if ((imageType 
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
) 
8022             wxFileName::CreateTempFileName(_("image")); 
8024         wxASSERT(!tempFile
.IsEmpty()); 
8026         image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
); 
8027         filenameToRead 
= tempFile
; 
8030         m_imageType 
= wxBITMAP_TYPE_JPEG
; 
8033     if (!file
.Open(filenameToRead
)) 
8036     m_dataSize 
= (size_t) file
.Length(); 
8041     m_data 
= ReadBlock(filenameToRead
, m_dataSize
); 
8044         wxRemoveFile(filenameToRead
); 
8046     return (m_data 
!= NULL
); 
8049 // Make an image block from the wxImage in the given 
8051 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
) 
8053     image
.SetOption(wxT("quality"), quality
); 
8055     if (imageType 
== wxBITMAP_TYPE_INVALID
) 
8056         return false; // Could not determine image type 
8058     return DoMakeImageBlock(image
, imageType
); 
8061 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG) 
8062 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
) 
8064     if (imageType 
== wxBITMAP_TYPE_INVALID
) 
8065         return false; // Could not determine image type 
8067     return DoMakeImageBlock(image
, imageType
); 
8070 // Makes the image block 
8071 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
) 
8073     wxMemoryOutputStream memStream
; 
8074     if (!image
.SaveFile(memStream
, imageType
)) 
8079     unsigned char* block 
= new unsigned char[memStream
.GetSize()]; 
8087     m_imageType 
= imageType
;     
8088     m_dataSize 
= memStream
.GetSize(); 
8090     memStream
.CopyTo(m_data
, m_dataSize
); 
8092     return (m_data 
!= NULL
); 
8096 bool wxRichTextImageBlock::Write(const wxString
& filename
) 
8098     return WriteBlock(filename
, m_data
, m_dataSize
); 
8101 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
) 
8103     m_imageType 
= block
.m_imageType
; 
8105     m_dataSize 
= block
.m_dataSize
; 
8106     if (m_dataSize 
== 0) 
8109     m_data 
= new unsigned char[m_dataSize
]; 
8111     for (i 
= 0; i 
< m_dataSize
; i
++) 
8112         m_data
[i
] = block
.m_data
[i
]; 
8116 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
) 
8121 // Load a wxImage from the block 
8122 bool wxRichTextImageBlock::Load(wxImage
& image
) 
8127     // Read in the image. 
8129     wxMemoryInputStream 
mstream(m_data
, m_dataSize
); 
8130     bool success 
= image
.LoadFile(mstream
, GetImageType()); 
8132     wxString tempFile 
= wxFileName::CreateTempFileName(_("image")); 
8133     wxASSERT(!tempFile
.IsEmpty()); 
8135     if (!WriteBlock(tempFile
, m_data
, m_dataSize
)) 
8139     success 
= image
.LoadFile(tempFile
, GetImageType()); 
8140     wxRemoveFile(tempFile
); 
8146 // Write data in hex to a stream 
8147 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
) 
8149     const int bufSize 
= 512; 
8150     char buf
[bufSize
+1]; 
8152     int left 
= m_dataSize
; 
8157         if (left
*2 > bufSize
) 
8159             n 
= bufSize
; left 
-= (bufSize
/2); 
8163             n 
= left
*2; left 
= 0; 
8167         for (i 
= 0; i 
< (n
/2); i
++) 
8169             wxDecToHex(m_data
[j
], b
, b
+1); 
8174         stream
.Write((const char*) buf
, n
); 
8179 // Read data in hex from a stream 
8180 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
) 
8182     int dataSize 
= length
/2; 
8187     // create a null terminated temporary string: 
8191     m_data 
= new unsigned char[dataSize
]; 
8193     for (i 
= 0; i 
< dataSize
; i 
++) 
8195         str
[0] = (char)stream
.GetC(); 
8196         str
[1] = (char)stream
.GetC(); 
8198         m_data
[i
] = (unsigned char)wxHexToDec(str
); 
8201     m_dataSize 
= dataSize
; 
8202     m_imageType 
= imageType
; 
8207 // Allocate and read from stream as a block of memory 
8208 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
) 
8210     unsigned char* block 
= new unsigned char[size
]; 
8214     stream
.Read(block
, size
); 
8219 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
) 
8221     wxFileInputStream 
stream(filename
); 
8225     return ReadBlock(stream
, size
); 
8228 // Write memory block to stream 
8229 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
) 
8231     stream
.Write((void*) block
, size
); 
8232     return stream
.IsOk(); 
8236 // Write memory block to file 
8237 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
) 
8239     wxFileOutputStream 
outStream(filename
); 
8240     if (!outStream
.Ok()) 
8243     return WriteBlock(outStream
, block
, size
); 
8246 // Gets the extension for the block's type 
8247 wxString 
wxRichTextImageBlock::GetExtension() const 
8249     wxImageHandler
* handler 
= wxImage::FindHandler(GetImageType()); 
8251         return handler
->GetExtension(); 
8253         return wxEmptyString
; 
8259  * The data object for a wxRichTextBuffer 
8262 const wxChar 
*wxRichTextBufferDataObject::ms_richTextBufferFormatId 
= wxT("wxShape"); 
8264 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
) 
8266     m_richTextBuffer 
= richTextBuffer
; 
8268     // this string should uniquely identify our format, but is otherwise 
8270     m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId()); 
8272     SetFormat(m_formatRichTextBuffer
); 
8275 wxRichTextBufferDataObject::~wxRichTextBufferDataObject() 
8277     delete m_richTextBuffer
; 
8280 // after a call to this function, the richTextBuffer is owned by the caller and it 
8281 // is responsible for deleting it! 
8282 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer() 
8284     wxRichTextBuffer
* richTextBuffer 
= m_richTextBuffer
; 
8285     m_richTextBuffer 
= NULL
; 
8287     return richTextBuffer
; 
8290 wxDataFormat 
wxRichTextBufferDataObject::GetPreferredFormat(Direction 
WXUNUSED(dir
)) const 
8292     return m_formatRichTextBuffer
; 
8295 size_t wxRichTextBufferDataObject::GetDataSize() const 
8297     if (!m_richTextBuffer
) 
8303         wxStringOutputStream 
stream(& bufXML
); 
8304         if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
)) 
8306             wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler.")); 
8312     wxCharBuffer buffer 
= bufXML
.mb_str(wxConvUTF8
); 
8313     return strlen(buffer
) + 1; 
8315     return bufXML
.Length()+1; 
8319 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const 
8321     if (!pBuf 
|| !m_richTextBuffer
) 
8327         wxStringOutputStream 
stream(& bufXML
); 
8328         if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
)) 
8330             wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler.")); 
8336     wxCharBuffer buffer 
= bufXML
.mb_str(wxConvUTF8
); 
8337     size_t len 
= strlen(buffer
); 
8338     memcpy((char*) pBuf
, (const char*) buffer
, len
); 
8339     ((char*) pBuf
)[len
] = 0; 
8341     size_t len 
= bufXML
.Length(); 
8342     memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
); 
8343     ((char*) pBuf
)[len
] = 0; 
8349 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
) 
8351     wxDELETE(m_richTextBuffer
); 
8353     wxString 
bufXML((const char*) buf
, wxConvUTF8
); 
8355     m_richTextBuffer 
= new wxRichTextBuffer
; 
8357     wxStringInputStream 
stream(bufXML
); 
8358     if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
)) 
8360         wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler.")); 
8362         wxDELETE(m_richTextBuffer
); 
8374  * wxRichTextFontTable 
8375  * Manages quick access to a pool of fonts for rendering rich text 
8378 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
); 
8380 class wxRichTextFontTableData
: public wxObjectRefData
 
8383     wxRichTextFontTableData() {} 
8385     wxFont 
FindFont(const wxTextAttr
& fontSpec
); 
8387     wxRichTextFontTableHashMap  m_hashMap
; 
8390 wxFont 
wxRichTextFontTableData::FindFont(const wxTextAttr
& fontSpec
) 
8392     wxString 
facename(fontSpec
.GetFontFaceName()); 
8393     wxString 
spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec
.GetFontSize(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), facename
.c_str(), (int) fontSpec
.GetFontEncoding())); 
8394     wxRichTextFontTableHashMap::iterator entry 
= m_hashMap
.find(spec
); 
8396     if ( entry 
== m_hashMap
.end() ) 
8398         wxFont 
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str()); 
8399         m_hashMap
[spec
] = font
; 
8404         return entry
->second
; 
8408 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
) 
8410 wxRichTextFontTable::wxRichTextFontTable() 
8412     m_refData 
= new wxRichTextFontTableData
; 
8415 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
) 
8421 wxRichTextFontTable::~wxRichTextFontTable() 
8426 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const 
8428     return (m_refData 
== table
.m_refData
); 
8431 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
) 
8436 wxFont 
wxRichTextFontTable::FindFont(const wxTextAttr
& fontSpec
) 
8438     wxRichTextFontTableData
* data 
= (wxRichTextFontTableData
*) m_refData
; 
8440         return data
->FindFont(fontSpec
); 
8445 void wxRichTextFontTable::Clear() 
8447     wxRichTextFontTableData
* data 
= (wxRichTextFontTableData
*) m_refData
; 
8449         data
->m_hashMap
.clear();