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"
44 #include "wx/richtext/richtextsizepage.h"
46 #include "wx/listimpl.cpp"
47 #include "wx/arrimpl.cpp"
49 WX_DEFINE_LIST(wxRichTextObjectList
)
50 WX_DEFINE_LIST(wxRichTextLineList
)
52 // Switch off if the platform doesn't like it for some reason
53 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55 // Use GetPartialTextExtents for platforms that support it natively
56 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
60 // Helper classes for floating layout
61 struct wxRichTextFloatRectMap
63 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
73 wxRichTextObject
* anchor
;
76 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
78 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
80 return r1
->startY
- r2
->startY
;
83 class wxRichTextFloatCollector
86 wxRichTextFloatCollector(const wxRect
& availableRect
);
87 ~wxRichTextFloatCollector();
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph
* para
);
91 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
93 // Return the last paragraph we collected
94 wxRichTextParagraph
* LastParagraph();
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect
GetAvailableRect(int startY
, int endY
);
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction
, int start
, int height
) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
104 // Find the last y position
105 int GetLastRectBottom();
107 // Draw the floats inside a rect
108 void Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
120 bool DeleteFloat(wxRichTextObject
* obj
);
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject
* obj
);
125 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
133 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
138 wxRichTextFloatRectMapArray m_left
;
139 wxRichTextFloatRectMapArray m_right
;
141 wxRect m_availableRect
;
142 wxRichTextParagraph
* m_para
;
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
149 for (i
= 0; i
< m_left
.GetCount(); i
++)
151 if (m_left
[i
]->anchor
== obj
)
157 for (i
= 0; i
< m_right
.GetCount(); i
++)
159 if (m_right
[i
]->anchor
== obj
)
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
172 for (i
= 0; i
< m_left
.GetCount(); i
++)
174 if (m_left
[i
]->anchor
== obj
)
179 for (i
= 0; i
< m_right
.GetCount(); i
++)
181 if (m_right
[i
]->anchor
== obj
)
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
193 for (i
= 0; i
< m_left
.GetCount(); i
++)
194 objects
.Append(m_left
[i
]->anchor
);
195 for (i
= 0; i
< m_right
.GetCount(); i
++)
196 objects
.Append(m_right
[i
]->anchor
);
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
209 int end
= array
.GetCount() - 1;
222 int mid
= (start
+ end
) / 2;
223 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
225 else if (array
[mid
]->startY
> point
)
230 else if (array
[mid
]->endY
< point
)
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
243 int len
= array
.GetCount();
245 wxASSERT(index
>= 0 && index
< len
);
247 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
248 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
249 while (index
< len
&& array
[index
]->startY
<= endY
)
251 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
260 m_availableRect
= rect
;
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
266 int len
= array
.GetCount();
267 for (int i
= 0; i
< len
; i
++)
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 FreeFloatRectMapArray(m_left
);
274 FreeFloatRectMapArray(m_right
);
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
279 if (array
.GetCount() == 0)
282 int i
= SearchAdjacentRect(array
, start
);
284 while (i
< (int) array
.GetCount())
286 if (array
[i
]->startY
- last
>= height
)
288 last
= array
[i
]->endY
;
295 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
297 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
298 return GetFitPosition(m_left
, start
, height
);
299 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
300 return GetFitPosition(m_right
, start
, height
);
303 wxASSERT("Never should be here");
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
312 int direction
= floating
->GetFloatDirection();
314 wxPoint pos
= floating
->GetPosition();
315 wxSize size
= floating
->GetCachedSize();
316 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
319 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
328 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
333 wxASSERT("Unrecognised float attribute.");
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
341 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
344 wxRichTextObject
* floating
= node
->GetData();
346 if (floating
->IsFloating())
348 CollectFloat(para
, floating
);
351 node
= node
->GetNext();
357 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
362 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
364 int widthLeft
= 0, widthRight
= 0;
365 if (m_left
.GetCount() != 0)
367 int i
= SearchAdjacentRect(m_left
, startY
);
368 if (i
< (int) m_left
.GetCount())
369 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
371 if (m_right
.GetCount() != 0)
373 int j
= SearchAdjacentRect(m_right
, startY
);
374 if (j
< (int) m_right
.GetCount())
375 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
384 int wxRichTextFloatCollector::GetLastRectBottom()
387 int len
= m_left
.GetCount();
389 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
391 len
= m_right
.GetCount();
393 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
402 int end
= rect
.y
+ rect
.height
;
404 i
= SearchAdjacentRect(array
, start
);
405 if (i
< 0 || i
>= (int) array
.GetCount())
407 j
= SearchAdjacentRect(array
, end
);
408 if (j
< 0 || j
>= (int) array
.GetCount())
409 j
= array
.GetCount() - 1;
412 wxRichTextObject
* obj
= array
[i
]->anchor
;
413 wxRichTextRange r
= obj
->GetRange();
414 obj
->Draw(dc
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
430 if (array
.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE
;
432 i
= SearchAdjacentRect(array
, pt
.y
);
433 if (i
< 0 || i
>= (int) array
.GetCount())
434 return wxRICHTEXT_HITTEST_NONE
;
435 if (!array
[i
]->anchor
->IsShown())
436 return wxRICHTEXT_HITTEST_NONE
;
438 wxPoint point
= array
[i
]->anchor
->GetPosition();
439 wxSize size
= array
[i
]->anchor
->GetCachedSize();
440 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
441 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
443 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
444 * obj
= array
[i
]->anchor
;
445 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE
;
448 return wxRICHTEXT_HITTEST_AFTER
;
451 return wxRICHTEXT_HITTEST_NONE
;
454 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, pt
, textPosition
, obj
, flags
);
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
467 // JACS: did I do this some time ago when testing? Should we re-enable it?
469 const wxFont
& font1
= dc
.GetFont();
470 if (font1
.IsOk() && font
.IsOk())
472 if (font1
.GetPointSize() == font
.GetPointSize() &&
473 font1
.GetFamily() == font
.GetFamily() &&
474 font1
.GetStyle() == font
.GetStyle() &&
475 font1
.GetWeight() == font
.GetWeight() &&
476 font1
.GetUnderlined() == font
.GetUnderlined() &&
477 font1
.GetFamily() == font
.GetFamily() &&
478 font1
.GetFaceName() == font
.GetFaceName())
485 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
487 const wxPen
& pen1
= dc
.GetPen();
488 if (pen1
.IsOk() && pen
.IsOk())
490 if (pen1
.GetWidth() == pen
.GetWidth() &&
491 pen1
.GetStyle() == pen
.GetStyle() &&
492 pen1
.GetColour() == pen
.GetColour())
498 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
500 const wxBrush
& brush1
= dc
.GetBrush();
501 if (brush1
.IsOk() && brush
.IsOk())
503 if (brush1
.GetStyle() == brush
.GetStyle() &&
504 brush1
.GetColour() == brush
.GetColour())
512 * This is the base for drawable objects.
515 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
517 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
525 wxRichTextObject::~wxRichTextObject()
529 void wxRichTextObject::Dereference()
537 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
540 m_maxSize
= obj
.m_maxSize
;
541 m_minSize
= obj
.m_minSize
;
543 m_range
= obj
.m_range
;
544 m_ownRange
= obj
.m_ownRange
;
545 m_attributes
= obj
.m_attributes
;
546 m_properties
= obj
.m_properties
;
547 m_descent
= obj
.m_descent
;
551 // Get/set the top-level container of this object.
552 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
554 const wxRichTextObject
* p
= this;
559 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
566 void wxRichTextObject::SetMargins(int margin
)
568 SetMargins(margin
, margin
, margin
, margin
);
571 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
573 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
574 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
579 int wxRichTextObject::GetLeftMargin() const
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
584 int wxRichTextObject::GetRightMargin() const
586 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
589 int wxRichTextObject::GetTopMargin() const
591 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
594 int wxRichTextObject::GetBottomMargin() const
596 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
599 // Calculate the available content space in the given rectangle, given the
600 // margins, border and padding specified in the object's attributes.
601 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, const wxRect
& outerRect
) const
603 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
604 marginRect
= outerRect
;
605 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
609 // Invalidate the buffer. With no argument, invalidates whole buffer.
610 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
612 if (invalidRange
!= wxRICHTEXT_NONE
)
614 SetCachedSize(wxDefaultSize
);
615 SetMaxSize(wxDefaultSize
);
616 SetMinSize(wxDefaultSize
);
620 // Convert units in tenths of a millimetre to device units
621 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
626 scale
= GetBuffer()->GetScale();
627 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
632 // Convert units in tenths of a millimetre to device units
633 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
635 // There are ppi pixels in 254.1 "1/10 mm"
637 double pixels
= ((double) units
* (double)ppi
) / 254.1;
641 // If the result is very small, make it at least one pixel in size.
642 if (pixels
== 0 && units
> 0)
648 // Convert units in pixels to tenths of a millimetre
649 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
654 scale
= GetBuffer()->GetScale();
656 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
659 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
661 // There are ppi pixels in 254.1 "1/10 mm"
663 double p
= double(pixels
);
668 int units
= int( p
* 254.1 / (double) ppi
);
672 // Draw the borders and background for the given rectangle and attributes.
673 // Width and height are taken to be the outer margin size, not the content.
674 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
676 // Assume boxRect is the area around the content
677 wxRect marginRect
= boxRect
;
678 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
680 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
682 // Margin is transparent. Draw background from margin.
683 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
686 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
688 // TODO: get selection colour from control?
689 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
692 colour
= attr
.GetBackgroundColour();
695 wxBrush
brush(colour
);
699 dc
.DrawRectangle(marginRect
);
702 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
704 wxRichTextAttr editBorderAttr
= attr
;
705 // TODO: make guideline colour configurable
706 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
707 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
708 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
710 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
713 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
714 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
716 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
717 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
723 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
725 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
726 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
728 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
730 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
731 wxColour
col(attr
.GetLeft().GetColour());
733 // If pen width is > 1, resorts to a solid rectangle.
736 int penStyle
= wxSOLID
;
737 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
739 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
740 penStyle
= wxLONG_DASH
;
741 wxPen
pen(col
, 1, penStyle
);
743 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
746 else if (borderLeft
> 1)
752 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
756 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
758 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
760 wxColour
col(attr
.GetRight().GetColour());
762 // If pen width is > 1, resorts to a solid rectangle.
763 if (borderRight
== 1)
765 int penStyle
= wxSOLID
;
766 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
768 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
769 penStyle
= wxLONG_DASH
;
770 wxPen
pen(col
, 1, penStyle
);
772 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
775 else if (borderRight
> 1)
781 dc
.DrawRectangle(rect
.x
- borderRight
, rect
.y
, borderRight
, rect
.height
);
785 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
787 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
789 wxColour
col(attr
.GetTop().GetColour());
791 // If pen width is > 1, resorts to a solid rectangle.
794 int penStyle
= wxSOLID
;
795 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
797 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
798 penStyle
= wxLONG_DASH
;
799 wxPen
pen(col
, 1, penStyle
);
801 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
804 else if (borderTop
> 1)
810 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
814 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
816 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
817 wxColour
col(attr
.GetTop().GetColour());
819 // If pen width is > 1, resorts to a solid rectangle.
820 if (borderBottom
== 1)
822 int penStyle
= wxSOLID
;
823 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
825 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
826 penStyle
= wxLONG_DASH
;
827 wxPen
pen(col
, 1, penStyle
);
829 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
832 else if (borderBottom
> 1)
838 dc
.DrawRectangle(rect
.x
, rect
.y
- rect
.height
- borderBottom
, rect
.width
, borderBottom
);
845 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
846 // or marginRect (outer), and the other must be the default rectangle (no width or height).
847 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
850 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
852 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
854 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
855 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
856 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
857 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
859 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
861 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
862 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
863 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
864 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
865 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
866 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
867 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
868 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
870 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
871 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
872 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
873 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
874 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
875 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
876 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
877 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
879 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
880 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
881 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
882 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
883 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
884 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
885 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
886 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
888 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
889 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
890 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
891 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
892 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
893 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
894 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
895 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
897 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
898 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
899 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
900 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
902 if (marginRect
!= wxRect())
904 contentRect
.x
= marginRect
.x
+ leftTotal
;
905 contentRect
.y
= marginRect
.y
+ topTotal
;
906 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
907 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
911 marginRect
.x
= contentRect
.x
- leftTotal
;
912 marginRect
.y
= contentRect
.y
- topTotal
;
913 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
914 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
917 borderRect
.x
= marginRect
.x
+ marginLeft
;
918 borderRect
.y
= marginRect
.y
+ marginTop
;
919 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
920 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
922 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
923 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
924 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
925 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
927 // The outline is outside the margin and doesn't influence the overall box position or content size.
928 outlineRect
.x
= marginRect
.x
- outlineLeft
;
929 outlineRect
.y
= marginRect
.y
- outlineTop
;
930 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
931 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
936 // Get the total margin for the object in pixels, taking into account margin, padding and border size
937 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
938 int& topMargin
, int& bottomMargin
)
940 // Assume boxRect is the area around the content
941 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
942 marginRect
= wxRect(0, 0, 1000, 1000);
944 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
946 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
947 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
948 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
949 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
954 // Returns the rectangle which the child has available to it given restrictions specified in the
955 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
956 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
)
958 wxRect rect
= availableParentSpace
;
961 scale
= buffer
->GetScale();
963 wxTextAttrDimensionConverter
converter(dc
, scale
, availableParentSpace
.GetSize());
965 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
966 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
968 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
969 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
971 // Can specify either left or right for the position (we're assuming we can't
972 // set the left and right edges to effectively set the size. Would we want to do that?)
973 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
975 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
977 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
979 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
981 rect
.x
= availableParentSpace
.x
+ availableParentSpace
.width
- rect
.width
;
986 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
988 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
990 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
992 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
993 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
994 rect
.y
= availableParentSpace
.y
+ availableParentSpace
.height
- rect
.height
;
1002 // Dump to output stream for debugging
1003 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1005 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1006 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");
1007 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");
1010 // Gets the containing buffer
1011 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1013 const wxRichTextObject
* obj
= this;
1014 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1015 obj
= obj
->GetParent();
1016 return wxDynamicCast(obj
, wxRichTextBuffer
);
1019 // Get the absolute object position, by traversing up the child/parent hierarchy
1020 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1022 wxPoint pt
= GetPosition();
1024 wxRichTextObject
* p
= GetParent();
1027 pt
= pt
+ p
->GetPosition();
1034 // Hit-testing: returns a flag indicating hit test details, plus
1035 // information about position
1036 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1039 return wxRICHTEXT_HITTEST_NONE
;
1041 wxRect rect
= GetRect();
1042 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1043 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1046 *contextObj
= GetParentContainer();
1047 textPosition
= GetRange().GetStart();
1048 return wxRICHTEXT_HITTEST_ON
;
1051 return wxRICHTEXT_HITTEST_NONE
;
1054 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1055 // lays out the object again using the maximum ('best') size
1056 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextBuffer
* buffer
,
1057 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
, const wxRect
& availableParentSpace
,
1060 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
);
1061 wxRect originalAvailableRect
= availableChildRect
;
1062 Layout(dc
, availableChildRect
, style
);
1064 wxSize maxSize
= GetMaxSize();
1066 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1068 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
/* && maxSize.x > 0 */)
1070 // Redo the layout with a fixed, minimum size this time.
1071 Invalidate(wxRICHTEXT_ALL
);
1072 wxRichTextAttr
newAttr(attr
);
1073 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1074 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1076 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
);
1078 // If a paragraph, align the whole paragraph.
1079 // Problem with this: if we're limited by a floating object, a line may be centered
1080 // w.r.t. the smaller resulting box rather than the actual available width.
1081 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1083 // centering, right-justification
1084 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1086 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1088 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1090 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1094 Layout(dc
, availableChildRect
, style
);
1108 // Move the object recursively, by adding the offset from old to new
1109 void wxRichTextObject::Move(const wxPoint
& pt
)
1116 * wxRichTextCompositeObject
1117 * This is the base for drawable objects.
1120 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1122 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1123 wxRichTextObject(parent
)
1127 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1132 /// Get the nth child
1133 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1135 wxASSERT ( n
< m_children
.GetCount() );
1137 return m_children
.Item(n
)->GetData();
1140 /// Append a child, returning the position
1141 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1143 m_children
.Append(child
);
1144 child
->SetParent(this);
1145 return m_children
.GetCount() - 1;
1148 /// Insert the child in front of the given object, or at the beginning
1149 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1153 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1154 m_children
.Insert(node
, child
);
1157 m_children
.Insert(child
);
1158 child
->SetParent(this);
1163 /// Delete the child
1164 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1166 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1169 wxRichTextObject
* obj
= node
->GetData();
1170 m_children
.Erase(node
);
1179 /// Delete all children
1180 bool wxRichTextCompositeObject::DeleteChildren()
1182 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1185 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1187 wxRichTextObject
* child
= node
->GetData();
1188 child
->Dereference(); // Only delete if reference count is zero
1190 node
= node
->GetNext();
1191 m_children
.Erase(oldNode
);
1197 /// Get the child count
1198 size_t wxRichTextCompositeObject::GetChildCount() const
1200 return m_children
.GetCount();
1204 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1206 wxRichTextObject::Copy(obj
);
1210 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1213 wxRichTextObject
* child
= node
->GetData();
1214 wxRichTextObject
* newChild
= child
->Clone();
1215 newChild
->SetParent(this);
1216 m_children
.Append(newChild
);
1218 node
= node
->GetNext();
1222 /// Hit-testing: returns a flag indicating hit test details, plus
1223 /// information about position
1224 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1227 return wxRICHTEXT_HITTEST_NONE
;
1229 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1232 wxRichTextObject
* child
= node
->GetData();
1234 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1236 // Just check if we hit the overall object
1237 int ret
= child
->wxRichTextObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1238 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1241 else if (child
->IsShown())
1243 int ret
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1244 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1248 node
= node
->GetNext();
1251 return wxRICHTEXT_HITTEST_NONE
;
1254 /// Finds the absolute position and row height for the given character position
1255 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1257 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1260 wxRichTextObject
* child
= node
->GetData();
1262 // Don't recurse if the child is a top-level object,
1263 // such as a text box, because the character position will no longer
1264 // apply. By definition, a top-level object has its own range of
1265 // character positions.
1266 if (!child
->IsTopLevel() && child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
))
1269 node
= node
->GetNext();
1276 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1278 long current
= start
;
1279 long lastEnd
= current
;
1287 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1290 wxRichTextObject
* child
= node
->GetData();
1293 child
->CalculateRange(current
, childEnd
);
1296 current
= childEnd
+ 1;
1298 node
= node
->GetNext();
1303 // A top-level object always has a range of size 1,
1304 // because its children don't count at this level.
1306 m_range
.SetRange(start
, start
);
1308 // An object with no children has zero length
1309 if (m_children
.GetCount() == 0)
1311 m_ownRange
.SetRange(0, lastEnd
);
1317 // An object with no children has zero length
1318 if (m_children
.GetCount() == 0)
1321 m_range
.SetRange(start
, end
);
1325 /// Delete range from layout.
1326 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1328 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1332 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1333 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1335 // Delete the range in each paragraph
1337 // When a chunk has been deleted, internally the content does not
1338 // now match the ranges.
1339 // However, so long as deletion is not done on the same object twice this is OK.
1340 // If you may delete content from the same object twice, recalculate
1341 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1342 // adjust the range you're deleting accordingly.
1344 if (!obj
->GetRange().IsOutside(range
))
1346 // No need to delete within a top-level object; just removing this object will do fine
1347 if (!obj
->IsTopLevel())
1348 obj
->DeleteRange(range
);
1350 // Delete an empty object, or paragraph within this range.
1351 if (obj
->IsEmpty() ||
1352 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1354 // An empty paragraph has length 1, so won't be deleted unless the
1355 // whole range is deleted.
1356 RemoveChild(obj
, true);
1366 /// Get any text in this object for the given range
1367 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1370 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1373 wxRichTextObject
* child
= node
->GetData();
1374 wxRichTextRange childRange
= range
;
1375 if (!child
->GetRange().IsOutside(range
))
1377 childRange
.LimitTo(child
->GetRange());
1379 wxString childText
= child
->GetTextForRange(childRange
);
1383 node
= node
->GetNext();
1389 /// Get the child object at the given character position
1390 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1392 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1395 wxRichTextObject
* child
= node
->GetData();
1396 if (child
->GetRange().GetStart() == pos
)
1398 node
= node
->GetNext();
1403 /// Recursively merge all pieces that can be merged.
1404 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1406 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1409 wxRichTextObject
* child
= node
->GetData();
1410 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1412 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1414 composite
->Defragment();
1416 if (node
->GetNext())
1418 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1419 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1421 nextChild
->Dereference();
1422 m_children
.Erase(node
->GetNext());
1424 // Don't set node -- we'll see if we can merge again with the next
1428 node
= node
->GetNext();
1431 node
= node
->GetNext();
1434 node
= node
->GetNext();
1437 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1438 if (GetChildCount() > 1)
1440 node
= m_children
.GetFirst();
1443 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1444 wxRichTextObject
* child
= node
->GetData();
1445 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1447 if (child
->IsEmpty())
1449 child
->Dereference();
1450 m_children
.Erase(node
);
1455 node
= node
->GetNext();
1462 /// Dump to output stream for debugging
1463 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1465 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1468 wxRichTextObject
* child
= node
->GetData();
1469 child
->Dump(stream
);
1470 node
= node
->GetNext();
1474 /// Get/set the object size for the given range. Returns false if the range
1475 /// is invalid for this object.
1476 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1478 if (!range
.IsWithin(GetRange()))
1483 wxArrayInt childExtents
;
1490 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1493 wxRichTextObject
* child
= node
->GetData();
1494 if (!child
->GetRange().IsOutside(range
))
1496 // Floating objects have a zero size within the paragraph.
1497 if (child
->IsFloating())
1502 if (partialExtents
->GetCount() > 0)
1503 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1507 partialExtents
->Add(0 /* zero size */ + lastSize
);
1514 wxRichTextRange rangeToUse
= range
;
1515 rangeToUse
.LimitTo(child
->GetRange());
1516 if (child
->IsTopLevel())
1517 rangeToUse
= child
->GetOwnRange();
1519 int childDescent
= 0;
1521 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1522 // but it's only going to be used after caching has taken place.
1523 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1525 childDescent
= child
->GetDescent();
1526 childSize
= child
->GetCachedSize();
1528 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1529 sz
.x
+= childSize
.x
;
1530 descent
= wxMax(descent
, childDescent
);
1532 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1534 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1535 sz
.x
+= childSize
.x
;
1536 descent
= wxMax(descent
, childDescent
);
1538 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1540 child
->SetCachedSize(childSize
);
1541 child
->SetDescent(childDescent
);
1547 if (partialExtents
->GetCount() > 0)
1548 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1553 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1555 partialExtents
->Add(childExtents
[i
] + lastSize
);
1565 node
= node
->GetNext();
1571 // Invalidate the buffer. With no argument, invalidates whole buffer.
1572 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1574 wxRichTextObject::Invalidate(invalidRange
);
1576 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1579 wxRichTextObject
* child
= node
->GetData();
1580 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1584 else if (child
->IsTopLevel())
1586 if (invalidRange
== wxRICHTEXT_NONE
)
1587 child
->Invalidate(wxRICHTEXT_NONE
);
1589 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1592 child
->Invalidate(invalidRange
);
1593 node
= node
->GetNext();
1597 // Move the object recursively, by adding the offset from old to new
1598 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1600 wxPoint oldPos
= GetPosition();
1602 wxPoint offset
= pt
- oldPos
;
1604 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1607 wxRichTextObject
* child
= node
->GetData();
1608 wxPoint childPos
= child
->GetPosition() + offset
;
1609 child
->Move(childPos
);
1610 node
= node
->GetNext();
1616 * wxRichTextParagraphLayoutBox
1617 * This box knows how to lay out paragraphs.
1620 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1622 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1623 wxRichTextCompositeObject(parent
)
1628 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1630 if (m_floatCollector
)
1632 delete m_floatCollector
;
1633 m_floatCollector
= NULL
;
1637 /// Initialize the object.
1638 void wxRichTextParagraphLayoutBox::Init()
1642 // For now, assume is the only box and has no initial size.
1643 m_range
= wxRichTextRange(0, -1);
1644 m_ownRange
= wxRichTextRange(0, -1);
1646 m_invalidRange
= wxRICHTEXT_ALL
;
1649 m_partialParagraph
= false;
1650 m_floatCollector
= NULL
;
1653 void wxRichTextParagraphLayoutBox::Clear()
1657 if (m_floatCollector
)
1658 delete m_floatCollector
;
1659 m_floatCollector
= NULL
;
1660 m_partialParagraph
= false;
1664 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1668 wxRichTextCompositeObject::Copy(obj
);
1670 m_partialParagraph
= obj
.m_partialParagraph
;
1671 m_defaultAttributes
= obj
.m_defaultAttributes
;
1674 // Gather information about floating objects; only gather floats for those paragraphs that
1675 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1677 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1679 if (m_floatCollector
!= NULL
)
1680 delete m_floatCollector
;
1681 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1682 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1683 // Only gather floats up to the point we'll start formatting paragraphs.
1684 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1686 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1687 wxASSERT (child
!= NULL
);
1689 m_floatCollector
->CollectFloat(child
);
1690 node
= node
->GetNext();
1696 // Returns the style sheet associated with the overall buffer.
1697 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1699 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1702 // Get the number of floating objects at this level
1703 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1705 if (m_floatCollector
)
1706 return m_floatCollector
->GetFloatingObjectCount();
1711 // Get a list of floating objects
1712 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1714 if (m_floatCollector
)
1716 return m_floatCollector
->GetFloatingObjects(objects
);
1723 void wxRichTextParagraphLayoutBox::UpdateRanges()
1727 start
= GetRange().GetStart();
1729 CalculateRange(start
, end
);
1733 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1736 return wxRICHTEXT_HITTEST_NONE
;
1738 int ret
= wxRICHTEXT_HITTEST_NONE
;
1739 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1740 ret
= m_floatCollector
->HitTest(dc
, pt
, textPosition
, obj
, flags
);
1742 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1743 return wxRichTextCompositeObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1751 /// Draw the floating objects
1752 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1754 if (m_floatCollector
)
1755 m_floatCollector
->Draw(dc
, range
, selection
, rect
, descent
, style
);
1758 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1763 from
->RemoveChild(obj
);
1764 to
->AppendChild(obj
);
1768 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1773 wxRect
thisRect(GetPosition(), GetCachedSize());
1776 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1779 // Don't draw guidelines if at top level
1780 int theseFlags
= flags
;
1782 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1783 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), thisRect
, theseFlags
);
1785 DrawFloats(dc
, range
, selection
, rect
, descent
, style
);
1786 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1789 wxRichTextObject
* child
= node
->GetData();
1791 if (child
&& !child
->GetRange().IsOutside(range
))
1793 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1794 wxRichTextRange childRange
= range
;
1795 if (child
->IsTopLevel())
1797 childRange
= child
->GetOwnRange();
1800 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1805 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1810 child
->Draw(dc
, childRange
, selection
, rect
, descent
, style
);
1813 node
= node
->GetNext();
1818 /// Lay the item out
1819 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
1821 SetPosition(rect
.GetPosition());
1826 wxRect availableSpace
;
1827 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1829 // If only laying out a specific area, the passed rect has a different meaning:
1830 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1831 // so that during a size, only the visible part will be relaid out, or
1832 // it would take too long causing flicker. As an approximation, we assume that
1833 // everything up to the start of the visible area is laid out correctly.
1836 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1837 availableSpace
= GetAvailableContentArea(dc
, rect2
);
1839 // Invalidate the part of the buffer from the first visible line
1840 // to the end. If other parts of the buffer are currently invalid,
1841 // then they too will be taken into account if they are above
1842 // the visible point.
1844 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1846 startPos
= line
->GetAbsoluteRange().GetStart();
1848 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1852 availableSpace
= GetAvailableContentArea(dc
, rect
);
1855 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1856 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), GetAttributes(), leftMargin
, rightMargin
,
1857 topMargin
, bottomMargin
);
1862 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1863 int maxMaxWidth
= 0;
1865 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1866 int maxMinWidth
= 0;
1868 // If we have vertical alignment, we must recalculate everything.
1869 bool hasVerticalAlignment
= (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1870 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1872 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1874 bool layoutAll
= true;
1876 // Get invalid range, rounding to paragraph start/end.
1877 wxRichTextRange invalidRange
= GetInvalidRange(true);
1879 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1882 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1884 else // If we know what range is affected, start laying out from that point on.
1885 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1887 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1890 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1891 wxRichTextObjectList::compatibility_iterator previousNode
;
1893 previousNode
= firstNode
->GetPrevious();
1898 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1899 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1902 // Now we're going to start iterating from the first affected paragraph.
1910 // Gather information about only those floating objects that will not be formatted,
1911 // after which floats will be gathered per-paragraph during layout.
1912 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1914 // A way to force speedy rest-of-buffer layout (the 'else' below)
1915 bool forceQuickLayout
= false;
1919 // Assume this box only contains paragraphs
1921 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1922 wxCHECK_MSG( child
, false, wxT("Unknown object in layout") );
1924 if (child
&& child
->IsShown())
1926 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1927 if ( !forceQuickLayout
&&
1929 child
->GetLines().IsEmpty() ||
1930 !child
->GetRange().IsOutside(invalidRange
)) )
1932 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1933 // lays out the object again using the minimum size
1934 child
->LayoutToBestSize(dc
, GetBuffer(),
1935 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1937 // Layout must set the cached size
1938 availableSpace
.y
+= child
->GetCachedSize().y
;
1939 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1940 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1941 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1943 // If we're just formatting the visible part of the buffer,
1944 // and we're now past the bottom of the window, and we don't have any
1945 // floating objects (since they may cause wrapping to change for the rest of the
1946 // the buffer), start quick layout.
1947 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1948 forceQuickLayout
= true;
1952 // We're outside the immediately affected range, so now let's just
1953 // move everything up or down. This assumes that all the children have previously
1954 // been laid out and have wrapped line lists associated with them.
1955 // TODO: check all paragraphs before the affected range.
1957 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1961 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1964 if (child
->GetLines().GetCount() == 0)
1966 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1967 // lays out the object again using the minimum size
1968 child
->LayoutToBestSize(dc
, GetBuffer(),
1969 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1971 //child->Layout(dc, availableChildRect, style);
1974 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1976 availableSpace
.y
+= child
->GetCachedSize().y
;
1977 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1978 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1979 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1982 node
= node
->GetNext();
1988 node
= node
->GetNext();
1991 node
= m_children
.GetLast();
1992 if (node
&& node
->GetData()->IsShown())
1994 wxRichTextObject
* child
= node
->GetData();
1995 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
1996 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
1999 maxHeight
= 0; // topMargin + bottomMargin;
2001 // TODO: (also in para layout) should set the
2002 // object's size to an absolute one if specified,
2003 // but if not specified, calculate it from content.
2005 // We need to add back the margins etc.
2007 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2008 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2009 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2010 SetCachedSize(marginRect
.GetSize());
2013 // The maximum size is the greatest of all maximum widths for all paragraphs.
2015 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2016 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2017 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2018 SetMaxSize(marginRect
.GetSize());
2021 // The minimum size is the greatest of all minimum widths for all paragraphs.
2023 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2024 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2025 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2026 SetMinSize(marginRect
.GetSize());
2029 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2030 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2033 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2034 if (leftOverSpace
> 0)
2036 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2038 yOffset
= (leftOverSpace
/2);
2040 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2042 yOffset
= leftOverSpace
;
2046 // Move all the children to vertically align the content
2047 // This doesn't take into account floating objects, unfortunately.
2050 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2053 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2055 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2057 node
= node
->GetNext();
2062 m_invalidRange
= wxRICHTEXT_NONE
;
2067 /// Get/set the size for the given range.
2068 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2072 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2073 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2075 // First find the first paragraph whose starting position is within the range.
2076 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2079 // child is a paragraph
2080 wxRichTextObject
* child
= node
->GetData();
2081 const wxRichTextRange
& r
= child
->GetRange();
2083 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2089 node
= node
->GetNext();
2092 // Next find the last paragraph containing part of the range
2093 node
= m_children
.GetFirst();
2096 // child is a paragraph
2097 wxRichTextObject
* child
= node
->GetData();
2098 const wxRichTextRange
& r
= child
->GetRange();
2100 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2106 node
= node
->GetNext();
2109 if (!startPara
|| !endPara
)
2112 // Now we can add up the sizes
2113 for (node
= startPara
; node
; node
= node
->GetNext())
2115 // child is a paragraph
2116 wxRichTextObject
* child
= node
->GetData();
2117 const wxRichTextRange
& childRange
= child
->GetRange();
2118 wxRichTextRange rangeToFind
= range
;
2119 rangeToFind
.LimitTo(childRange
);
2121 if (child
->IsTopLevel())
2122 rangeToFind
= child
->GetOwnRange();
2126 int childDescent
= 0;
2127 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2129 descent
= wxMax(childDescent
, descent
);
2131 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2132 sz
.y
+= childSize
.y
;
2134 if (node
== endPara
)
2143 /// Get the paragraph at the given position
2144 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2149 // First find the first paragraph whose starting position is within the range.
2150 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2153 // child is a paragraph
2154 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2155 // wxASSERT (child != NULL);
2159 // Return first child in buffer if position is -1
2163 if (child
->GetRange().Contains(pos
))
2167 node
= node
->GetNext();
2172 /// Get the line at the given position
2173 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2178 // First find the first paragraph whose starting position is within the range.
2179 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2182 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2183 if (obj
->GetRange().Contains(pos
))
2185 // child is a paragraph
2186 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2187 // wxASSERT (child != NULL);
2191 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2194 wxRichTextLine
* line
= node2
->GetData();
2196 wxRichTextRange range
= line
->GetAbsoluteRange();
2198 if (range
.Contains(pos
) ||
2200 // If the position is end-of-paragraph, then return the last line of
2201 // of the paragraph.
2202 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2205 node2
= node2
->GetNext();
2210 node
= node
->GetNext();
2213 int lineCount
= GetLineCount();
2215 return GetLineForVisibleLineNumber(lineCount
-1);
2220 /// Get the line at the given y pixel position, or the last line.
2221 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2223 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2226 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2227 // wxASSERT (child != NULL);
2231 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2234 wxRichTextLine
* line
= node2
->GetData();
2236 wxRect
rect(line
->GetRect());
2238 if (y
<= rect
.GetBottom())
2241 node2
= node2
->GetNext();
2245 node
= node
->GetNext();
2249 int lineCount
= GetLineCount();
2251 return GetLineForVisibleLineNumber(lineCount
-1);
2256 /// Get the number of visible lines
2257 int wxRichTextParagraphLayoutBox::GetLineCount() const
2261 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2264 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2265 // wxASSERT (child != NULL);
2268 count
+= child
->GetLines().GetCount();
2270 node
= node
->GetNext();
2276 /// Get the paragraph for a given line
2277 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2279 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2282 /// Get the line size at the given position
2283 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2285 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2288 return line
->GetSize();
2291 return wxSize(0, 0);
2295 /// Convenience function to add a paragraph of text
2296 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2298 // Don't use the base style, just the default style, and the base style will
2299 // be combined at display time.
2300 // Divide into paragraph and character styles.
2302 wxRichTextAttr defaultCharStyle
;
2303 wxRichTextAttr defaultParaStyle
;
2305 // If the default style is a named paragraph style, don't apply any character formatting
2306 // to the initial text string.
2307 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2309 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2311 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2314 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2316 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2317 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2319 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2325 return para
->GetRange();
2328 /// Adds multiple paragraphs, based on newlines.
2329 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2331 // Don't use the base style, just the default style, and the base style will
2332 // be combined at display time.
2333 // Divide into paragraph and character styles.
2335 wxRichTextAttr defaultCharStyle
;
2336 wxRichTextAttr defaultParaStyle
;
2338 // If the default style is a named paragraph style, don't apply any character formatting
2339 // to the initial text string.
2340 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2342 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2344 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2347 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2349 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2350 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2352 wxRichTextParagraph
* firstPara
= NULL
;
2353 wxRichTextParagraph
* lastPara
= NULL
;
2355 wxRichTextRange
range(-1, -1);
2358 size_t len
= text
.length();
2360 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2369 wxChar ch
= text
[i
];
2370 if (ch
== wxT('\n') || ch
== wxT('\r'))
2374 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2375 plainText
->SetText(line
);
2377 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2382 line
= wxEmptyString
;
2393 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2394 plainText
->SetText(line
);
2399 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2402 /// Convenience function to add an image
2403 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2405 // Don't use the base style, just the default style, and the base style will
2406 // be combined at display time.
2407 // Divide into paragraph and character styles.
2409 wxRichTextAttr defaultCharStyle
;
2410 wxRichTextAttr defaultParaStyle
;
2412 // If the default style is a named paragraph style, don't apply any character formatting
2413 // to the initial text string.
2414 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2416 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2418 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2421 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2423 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2424 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2426 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2428 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2432 return para
->GetRange();
2436 /// Insert fragment into this box at the given position. If partialParagraph is true,
2437 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2440 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2442 // First, find the first paragraph whose starting position is within the range.
2443 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2446 wxRichTextAttr originalAttr
= para
->GetAttributes();
2448 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2450 // Now split at this position, returning the object to insert the new
2451 // ones in front of.
2452 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2454 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2455 // text, for example, so let's optimize.
2457 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2459 // Add the first para to this para...
2460 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2464 // Iterate through the fragment paragraph inserting the content into this paragraph.
2465 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2466 wxASSERT (firstPara
!= NULL
);
2468 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2471 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2476 para
->AppendChild(newObj
);
2480 // Insert before nextObject
2481 para
->InsertChild(newObj
, nextObject
);
2484 objectNode
= objectNode
->GetNext();
2491 // Procedure for inserting a fragment consisting of a number of
2494 // 1. Remove and save the content that's after the insertion point, for adding
2495 // back once we've added the fragment.
2496 // 2. Add the content from the first fragment paragraph to the current
2498 // 3. Add remaining fragment paragraphs after the current paragraph.
2499 // 4. Add back the saved content from the first paragraph. If partialParagraph
2500 // is true, add it to the last paragraph added and not a new one.
2502 // 1. Remove and save objects after split point.
2503 wxList savedObjects
;
2505 para
->MoveToList(nextObject
, savedObjects
);
2507 // 2. Add the content from the 1st fragment paragraph.
2508 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2512 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2513 wxASSERT(firstPara
!= NULL
);
2515 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2516 para
->SetAttributes(firstPara
->GetAttributes());
2518 // Save empty paragraph attributes for appending later
2519 // These are character attributes deliberately set for a new paragraph. Without this,
2520 // we couldn't pass default attributes when appending a new paragraph.
2521 wxRichTextAttr emptyParagraphAttributes
;
2523 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2525 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2526 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2530 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2533 para
->AppendChild(newObj
);
2535 objectNode
= objectNode
->GetNext();
2538 // 3. Add remaining fragment paragraphs after the current paragraph.
2539 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2540 wxRichTextObject
* nextParagraph
= NULL
;
2541 if (nextParagraphNode
)
2542 nextParagraph
= nextParagraphNode
->GetData();
2544 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2545 wxRichTextParagraph
* finalPara
= para
;
2547 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2549 // If there was only one paragraph, we need to insert a new one.
2552 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2553 wxASSERT( para
!= NULL
);
2555 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2558 InsertChild(finalPara
, nextParagraph
);
2560 AppendChild(finalPara
);
2565 // If there was only one paragraph, or we have full paragraphs in our fragment,
2566 // we need to insert a new one.
2569 finalPara
= new wxRichTextParagraph
;
2572 InsertChild(finalPara
, nextParagraph
);
2574 AppendChild(finalPara
);
2577 // 4. Add back the remaining content.
2581 finalPara
->MoveFromList(savedObjects
);
2583 // Ensure there's at least one object
2584 if (finalPara
->GetChildCount() == 0)
2586 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2587 text
->SetAttributes(emptyParagraphAttributes
);
2589 finalPara
->AppendChild(text
);
2593 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2594 finalPara
->SetAttributes(firstPara
->GetAttributes());
2595 else if (finalPara
&& finalPara
!= para
)
2596 finalPara
->SetAttributes(originalAttr
);
2604 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2607 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2608 wxASSERT( para
!= NULL
);
2610 AppendChild(para
->Clone());
2619 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2620 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2621 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2623 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2626 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2627 wxASSERT( para
!= NULL
);
2629 if (!para
->GetRange().IsOutside(range
))
2631 fragment
.AppendChild(para
->Clone());
2636 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2637 if (!fragment
.IsEmpty())
2639 wxRichTextRange
topTailRange(range
);
2641 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2642 wxASSERT( firstPara
!= NULL
);
2644 // Chop off the start of the paragraph
2645 if (topTailRange
.GetStart() > firstPara
->GetRange().GetStart())
2647 wxRichTextRange
r(firstPara
->GetRange().GetStart(), topTailRange
.GetStart()-1);
2648 firstPara
->DeleteRange(r
);
2650 // Make sure the numbering is correct
2652 fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
);
2654 // Now, we've deleted some positions, so adjust the range
2656 topTailRange
.SetEnd(topTailRange
.GetEnd() - r
.GetLength());
2659 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2660 wxASSERT( lastPara
!= NULL
);
2662 if (topTailRange
.GetEnd() < (lastPara
->GetRange().GetEnd()-1))
2664 wxRichTextRange
r(topTailRange
.GetEnd()+1, lastPara
->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
2665 lastPara
->DeleteRange(r
);
2667 // Make sure the numbering is correct
2669 fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
);
2671 // We only have part of a paragraph at the end
2672 fragment
.SetPartialParagraph(true);
2676 if (topTailRange
.GetEnd() == (lastPara
->GetRange().GetEnd() - 1))
2677 // We have a partial paragraph (don't save last new paragraph marker)
2678 fragment
.SetPartialParagraph(true);
2680 // We have a complete paragraph
2681 fragment
.SetPartialParagraph(false);
2688 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2689 /// starting from zero at the start of the buffer.
2690 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2697 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2700 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2701 // wxASSERT( child != NULL );
2705 if (child
->GetRange().Contains(pos
))
2707 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2710 wxRichTextLine
* line
= node2
->GetData();
2711 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2713 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2715 // If the caret is displayed at the end of the previous wrapped line,
2716 // we want to return the line it's _displayed_ at (not the actual line
2717 // containing the position).
2718 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2719 return lineCount
- 1;
2726 node2
= node2
->GetNext();
2728 // If we didn't find it in the lines, it must be
2729 // the last position of the paragraph. So return the last line.
2733 lineCount
+= child
->GetLines().GetCount();
2736 node
= node
->GetNext();
2743 /// Given a line number, get the corresponding wxRichTextLine object.
2744 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2748 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2751 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2752 // wxASSERT(child != NULL);
2756 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2758 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2761 wxRichTextLine
* line
= node2
->GetData();
2763 if (lineCount
== lineNumber
)
2768 node2
= node2
->GetNext();
2772 lineCount
+= child
->GetLines().GetCount();
2775 node
= node
->GetNext();
2782 /// Delete range from layout.
2783 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2785 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2787 wxRichTextParagraph
* firstPara
= NULL
;
2790 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2791 // wxASSERT (obj != NULL);
2793 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2797 // Delete the range in each paragraph
2799 if (!obj
->GetRange().IsOutside(range
))
2801 // Deletes the content of this object within the given range
2802 obj
->DeleteRange(range
);
2804 wxRichTextRange thisRange
= obj
->GetRange();
2805 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2807 // If the whole paragraph is within the range to delete,
2808 // delete the whole thing.
2809 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2811 // Delete the whole object
2812 RemoveChild(obj
, true);
2815 else if (!firstPara
)
2818 // If the range includes the paragraph end, we need to join this
2819 // and the next paragraph.
2820 if (range
.GetEnd() <= thisRange
.GetEnd())
2822 // We need to move the objects from the next paragraph
2823 // to this paragraph
2825 wxRichTextParagraph
* nextParagraph
= NULL
;
2826 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2827 nextParagraph
= obj
;
2830 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2832 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2835 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2837 wxRichTextAttr nextParaAttr
;
2838 if (applyFinalParagraphStyle
)
2840 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2841 // not the next one.
2842 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2843 nextParaAttr
= thisAttr
;
2845 nextParaAttr
= nextParagraph
->GetAttributes();
2848 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2850 // Move the objects to the previous para
2851 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2855 wxRichTextObject
* obj1
= node1
->GetData();
2857 firstPara
->AppendChild(obj1
);
2859 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2860 nextParagraph
->GetChildren().Erase(node1
);
2865 // Delete the paragraph
2866 RemoveChild(nextParagraph
, true);
2869 // Avoid empty paragraphs
2870 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2872 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2873 firstPara
->AppendChild(text
);
2876 if (applyFinalParagraphStyle
)
2877 firstPara
->SetAttributes(nextParaAttr
);
2890 /// Get any text in this object for the given range
2891 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2895 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2898 wxRichTextObject
* child
= node
->GetData();
2899 if (!child
->GetRange().IsOutside(range
))
2901 wxRichTextRange childRange
= range
;
2902 childRange
.LimitTo(child
->GetRange());
2904 wxString childText
= child
->GetTextForRange(childRange
);
2908 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2913 node
= node
->GetNext();
2919 /// Get all the text
2920 wxString
wxRichTextParagraphLayoutBox::GetText() const
2922 return GetTextForRange(GetOwnRange());
2925 /// Get the paragraph by number
2926 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2928 if ((size_t) paragraphNumber
>= GetChildCount())
2931 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2934 /// Get the length of the paragraph
2935 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2937 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2939 return para
->GetRange().GetLength() - 1; // don't include newline
2944 /// Get the text of the paragraph
2945 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2947 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2949 return para
->GetTextForRange(para
->GetRange());
2951 return wxEmptyString
;
2954 /// Convert zero-based line column and paragraph number to a position.
2955 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
2957 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
2960 return para
->GetRange().GetStart() + x
;
2966 /// Convert zero-based position to line column and paragraph number
2967 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
2969 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
2973 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2976 wxRichTextObject
* child
= node
->GetData();
2980 node
= node
->GetNext();
2984 *x
= pos
- para
->GetRange().GetStart();
2992 /// Get the leaf object in a paragraph at this position.
2993 /// Given a line number, get the corresponding wxRichTextLine object.
2994 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
2996 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2999 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3003 wxRichTextObject
* child
= node
->GetData();
3004 if (child
->GetRange().Contains(position
))
3007 node
= node
->GetNext();
3009 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3010 return para
->GetChildren().GetLast()->GetData();
3015 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3016 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3018 bool characterStyle
= false;
3019 bool paragraphStyle
= false;
3021 if (style
.IsCharacterStyle())
3022 characterStyle
= true;
3023 if (style
.IsParagraphStyle())
3024 paragraphStyle
= true;
3026 wxRichTextBuffer
* buffer
= GetBuffer();
3028 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3029 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3030 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3031 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3032 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3033 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3035 // Apply paragraph style first, if any
3036 wxRichTextAttr
wholeStyle(style
);
3038 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3040 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3042 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3045 // Limit the attributes to be set to the content to only character attributes.
3046 wxRichTextAttr
characterAttributes(wholeStyle
);
3047 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3049 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3051 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3053 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3056 // If we are associated with a control, make undoable; otherwise, apply immediately
3059 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3061 wxRichTextAction
* action
= NULL
;
3063 if (haveControl
&& withUndo
)
3065 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3066 action
->SetRange(range
);
3067 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3070 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3073 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3074 // wxASSERT (para != NULL);
3076 if (para
&& para
->GetChildCount() > 0)
3078 // Stop searching if we're beyond the range of interest
3079 if (para
->GetRange().GetStart() > range
.GetEnd())
3082 if (!para
->GetRange().IsOutside(range
))
3084 // We'll be using a copy of the paragraph to make style changes,
3085 // not updating the buffer directly.
3086 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3088 if (haveControl
&& withUndo
)
3090 newPara
= new wxRichTextParagraph(*para
);
3091 action
->GetNewParagraphs().AppendChild(newPara
);
3093 // Also store the old ones for Undo
3094 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3099 // If we're specifying paragraphs only, then we really mean character formatting
3100 // to be included in the paragraph style
3101 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3105 // Removes the given style from the paragraph
3106 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3108 else if (resetExistingStyle
)
3109 newPara
->GetAttributes() = wholeStyle
;
3114 // Only apply attributes that will make a difference to the combined
3115 // style as seen on the display
3116 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3117 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3120 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3124 // When applying paragraph styles dynamically, don't change the text objects' attributes
3125 // since they will computed as needed. Only apply the character styling if it's _only_
3126 // character styling. This policy is subject to change and might be put under user control.
3128 // Hm. we might well be applying a mix of paragraph and character styles, in which
3129 // case we _do_ want to apply character styles regardless of what para styles are set.
3130 // But if we're applying a paragraph style, which has some character attributes, but
3131 // we only want the paragraphs to hold this character style, then we _don't_ want to
3132 // apply the character style. So we need to be able to choose.
3134 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3136 wxRichTextRange
childRange(range
);
3137 childRange
.LimitTo(newPara
->GetRange());
3139 // Find the starting position and if necessary split it so
3140 // we can start applying a different style.
3141 // TODO: check that the style actually changes or is different
3142 // from style outside of range
3143 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3144 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3146 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3147 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3149 firstObject
= newPara
->SplitAt(range
.GetStart());
3151 // Increment by 1 because we're apply the style one _after_ the split point
3152 long splitPoint
= childRange
.GetEnd();
3153 if (splitPoint
!= newPara
->GetRange().GetEnd())
3157 if (splitPoint
== newPara
->GetRange().GetEnd())
3158 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3160 // lastObject is set as a side-effect of splitting. It's
3161 // returned as the object before the new object.
3162 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3164 wxASSERT(firstObject
!= NULL
);
3165 wxASSERT(lastObject
!= NULL
);
3167 if (!firstObject
|| !lastObject
)
3170 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3171 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3173 wxASSERT(firstNode
);
3176 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3180 wxRichTextObject
* child
= node2
->GetData();
3184 // Removes the given style from the paragraph
3185 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3187 else if (resetExistingStyle
)
3188 child
->GetAttributes() = characterAttributes
;
3193 // Only apply attributes that will make a difference to the combined
3194 // style as seen on the display
3195 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3196 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3199 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3202 if (node2
== lastNode
)
3205 node2
= node2
->GetNext();
3211 node
= node
->GetNext();
3214 // Do action, or delay it until end of batch.
3215 if (haveControl
&& withUndo
)
3216 buffer
->SubmitAction(action
);
3221 // Just change the attributes for this single object.
3222 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3224 wxRichTextBuffer
* buffer
= GetBuffer();
3225 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3226 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3227 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3229 wxRichTextAction
*action
= NULL
;
3230 wxRichTextAttr newAttr
= obj
->GetAttributes();
3231 if (resetExistingStyle
)
3234 newAttr
.Apply(textAttr
);
3236 if (haveControl
&& withUndo
)
3238 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3239 action
->SetRange(obj
->GetRange().FromInternal());
3240 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3241 action
->MakeObject(obj
);
3243 action
->GetAttributes() = newAttr
;
3246 obj
->GetAttributes() = newAttr
;
3248 if (haveControl
&& withUndo
)
3249 buffer
->SubmitAction(action
);
3252 /// Get the text attributes for this position.
3253 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3255 return DoGetStyle(position
, style
, true);
3258 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3260 return DoGetStyle(position
, style
, false);
3263 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3264 /// context attributes.
3265 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3267 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3269 if (style
.IsParagraphStyle())
3271 obj
= GetParagraphAtPosition(position
);
3276 // Start with the base style
3277 style
= GetAttributes();
3279 // Apply the paragraph style
3280 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3283 style
= obj
->GetAttributes();
3290 obj
= GetLeafObjectAtPosition(position
);
3295 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3296 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3299 style
= obj
->GetAttributes();
3307 static bool wxHasStyle(long flags
, long style
)
3309 return (flags
& style
) != 0;
3312 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3314 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3316 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3321 /// Get the combined style for a range - if any attribute is different within the range,
3322 /// that attribute is not present within the flags.
3323 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3325 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3327 style
= wxRichTextAttr();
3329 wxRichTextAttr clashingAttr
;
3330 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3332 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3335 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3336 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3338 if (para
->GetChildren().GetCount() == 0)
3340 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3342 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3346 wxRichTextRange
paraRange(para
->GetRange());
3347 paraRange
.LimitTo(range
);
3349 // First collect paragraph attributes only
3350 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3351 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3352 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3354 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3358 wxRichTextObject
* child
= childNode
->GetData();
3359 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3361 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3363 // Now collect character attributes only
3364 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3366 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3369 childNode
= childNode
->GetNext();
3373 node
= node
->GetNext();
3378 /// Set default style
3379 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3381 m_defaultAttributes
= style
;
3385 /// Test if this whole range has character attributes of the specified kind. If any
3386 /// of the attributes are different within the range, the test fails. You
3387 /// can use this to implement, for example, bold button updating. style must have
3388 /// flags indicating which attributes are of interest.
3389 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3392 int matchingCount
= 0;
3394 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3397 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3398 // wxASSERT (para != NULL);
3402 // Stop searching if we're beyond the range of interest
3403 if (para
->GetRange().GetStart() > range
.GetEnd())
3404 return foundCount
== matchingCount
&& foundCount
!= 0;
3406 if (!para
->GetRange().IsOutside(range
))
3408 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3412 wxRichTextObject
* child
= node2
->GetData();
3413 // Allow for empty string if no buffer
3414 wxRichTextRange childRange
= child
->GetRange();
3415 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3416 childRange
.SetEnd(childRange
.GetEnd()+1);
3418 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3421 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3423 if (wxTextAttrEqPartial(textAttr
, style
))
3427 node2
= node2
->GetNext();
3432 node
= node
->GetNext();
3435 return foundCount
== matchingCount
&& foundCount
!= 0;
3438 /// Test if this whole range has paragraph attributes of the specified kind. If any
3439 /// of the attributes are different within the range, the test fails. You
3440 /// can use this to implement, for example, centering button updating. style must have
3441 /// flags indicating which attributes are of interest.
3442 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3445 int matchingCount
= 0;
3447 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3450 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3451 // wxASSERT (para != NULL);
3455 // Stop searching if we're beyond the range of interest
3456 if (para
->GetRange().GetStart() > range
.GetEnd())
3457 return foundCount
== matchingCount
&& foundCount
!= 0;
3459 if (!para
->GetRange().IsOutside(range
))
3461 wxRichTextAttr textAttr
= GetAttributes();
3462 // Apply the paragraph style
3463 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3466 if (wxTextAttrEqPartial(textAttr
, style
))
3471 node
= node
->GetNext();
3473 return foundCount
== matchingCount
&& foundCount
!= 0;
3476 void wxRichTextParagraphLayoutBox::Reset()
3480 wxRichTextBuffer
* buffer
= GetBuffer();
3481 if (buffer
&& buffer
->GetRichTextCtrl())
3483 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3484 event
.SetEventObject(buffer
->GetRichTextCtrl());
3485 event
.SetContainer(this);
3487 buffer
->SendEvent(event
, true);
3490 AddParagraph(wxEmptyString
);
3492 InvalidateHierarchy(wxRICHTEXT_ALL
);
3495 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3496 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3498 wxRichTextCompositeObject::Invalidate(invalidRange
);
3500 DoInvalidate(invalidRange
);
3503 // Do the (in)validation for this object only
3504 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3506 if (invalidRange
== wxRICHTEXT_ALL
)
3508 m_invalidRange
= wxRICHTEXT_ALL
;
3510 // Already invalidating everything
3511 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3516 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3517 m_invalidRange
.SetStart(invalidRange
.GetStart());
3518 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3519 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3523 // Do the (in)validation both up and down the hierarchy
3524 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3526 Invalidate(invalidRange
);
3528 if (invalidRange
!= wxRICHTEXT_NONE
)
3530 // Now go up the hierarchy
3531 wxRichTextObject
* thisObj
= this;
3532 wxRichTextObject
* p
= GetParent();
3535 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3537 l
->DoInvalidate(thisObj
->GetRange());
3545 /// Get invalid range, rounding to entire paragraphs if argument is true.
3546 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3548 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3549 return m_invalidRange
;
3551 wxRichTextRange range
= m_invalidRange
;
3553 if (wholeParagraphs
)
3555 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3557 range
.SetStart(para1
->GetRange().GetStart());
3558 // floating layout make all child should be relayout
3559 range
.SetEnd(GetOwnRange().GetEnd());
3564 /// Apply the style sheet to the buffer, for example if the styles have changed.
3565 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3567 wxASSERT(styleSheet
!= NULL
);
3573 wxRichTextAttr
attr(GetBasicStyle());
3574 if (GetBasicStyle().HasParagraphStyleName())
3576 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3579 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3580 SetBasicStyle(attr
);
3585 if (GetBasicStyle().HasCharacterStyleName())
3587 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3590 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3591 SetBasicStyle(attr
);
3596 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3599 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3600 // wxASSERT (para != NULL);
3604 // Combine paragraph and list styles. If there is a list style in the original attributes,
3605 // the current indentation overrides anything else and is used to find the item indentation.
3606 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3607 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3608 // exception as above).
3609 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3610 // So when changing a list style interactively, could retrieve level based on current style, then
3611 // set appropriate indent and apply new style.
3615 if (para
->GetAttributes().HasOutlineLevel())
3616 outline
= para
->GetAttributes().GetOutlineLevel();
3617 if (para
->GetAttributes().HasBulletNumber())
3618 num
= para
->GetAttributes().GetBulletNumber();
3620 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3622 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3624 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3625 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3626 if (paraDef
&& !listDef
)
3628 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3631 else if (listDef
&& !paraDef
)
3633 // Set overall style defined for the list style definition
3634 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3636 // Apply the style for this level
3637 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3640 else if (listDef
&& paraDef
)
3642 // Combines overall list style, style for level, and paragraph style
3643 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3647 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3649 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3651 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3653 // Overall list definition style
3654 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3656 // Style for this level
3657 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3661 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3663 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3666 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3672 para
->GetAttributes().SetOutlineLevel(outline
);
3674 para
->GetAttributes().SetBulletNumber(num
);
3677 node
= node
->GetNext();
3679 return foundCount
!= 0;
3683 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3685 wxRichTextBuffer
* buffer
= GetBuffer();
3686 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3688 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3689 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3690 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3691 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3693 // Current number, if numbering
3696 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3698 // If we are associated with a control, make undoable; otherwise, apply immediately
3701 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3703 wxRichTextAction
* action
= NULL
;
3705 if (haveControl
&& withUndo
)
3707 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3708 action
->SetRange(range
);
3709 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3712 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3715 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3716 // wxASSERT (para != NULL);
3718 if (para
&& para
->GetChildCount() > 0)
3720 // Stop searching if we're beyond the range of interest
3721 if (para
->GetRange().GetStart() > range
.GetEnd())
3724 if (!para
->GetRange().IsOutside(range
))
3726 // We'll be using a copy of the paragraph to make style changes,
3727 // not updating the buffer directly.
3728 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3730 if (haveControl
&& withUndo
)
3732 newPara
= new wxRichTextParagraph(*para
);
3733 action
->GetNewParagraphs().AppendChild(newPara
);
3735 // Also store the old ones for Undo
3736 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3743 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3744 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3746 // How is numbering going to work?
3747 // If we are renumbering, or numbering for the first time, we need to keep
3748 // track of the number for each level. But we might be simply applying a different
3750 // In Word, applying a style to several paragraphs, even if at different levels,
3751 // reverts the level back to the same one. So we could do the same here.
3752 // Renumbering will need to be done when we promote/demote a paragraph.
3754 // Apply the overall list style, and item style for this level
3755 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3756 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3758 // Now we need to do numbering
3761 newPara
->GetAttributes().SetBulletNumber(n
);
3766 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3768 // if def is NULL, remove list style, applying any associated paragraph style
3769 // to restore the attributes
3771 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3772 newPara
->GetAttributes().SetLeftIndent(0, 0);
3773 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3775 // Eliminate the main list-related attributes
3776 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
);
3778 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3780 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3783 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3790 node
= node
->GetNext();
3793 // Do action, or delay it until end of batch.
3794 if (haveControl
&& withUndo
)
3795 buffer
->SubmitAction(action
);
3800 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3802 wxRichTextBuffer
* buffer
= GetBuffer();
3803 if (buffer
&& buffer
->GetStyleSheet())
3805 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3807 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
3812 /// Clear list for given range
3813 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3815 return SetListStyle(range
, NULL
, flags
);
3818 /// Number/renumber any list elements in the given range
3819 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3821 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
3824 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3825 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
3826 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3828 wxRichTextBuffer
* buffer
= GetBuffer();
3829 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3831 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3832 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3834 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3837 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3839 // Max number of levels
3840 const int maxLevels
= 10;
3842 // The level we're looking at now
3843 int currentLevel
= -1;
3845 // The item number for each level
3846 int levels
[maxLevels
];
3849 // Reset all numbering
3850 for (i
= 0; i
< maxLevels
; i
++)
3852 if (startFrom
!= -1)
3853 levels
[i
] = startFrom
-1;
3854 else if (renumber
) // start again
3857 levels
[i
] = -1; // start from the number we found, if any
3860 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3862 // If we are associated with a control, make undoable; otherwise, apply immediately
3865 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3867 wxRichTextAction
* action
= NULL
;
3869 if (haveControl
&& withUndo
)
3871 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3872 action
->SetRange(range
);
3873 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3876 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3879 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3880 // wxASSERT (para != NULL);
3882 if (para
&& para
->GetChildCount() > 0)
3884 // Stop searching if we're beyond the range of interest
3885 if (para
->GetRange().GetStart() > range
.GetEnd())
3888 if (!para
->GetRange().IsOutside(range
))
3890 // We'll be using a copy of the paragraph to make style changes,
3891 // not updating the buffer directly.
3892 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3894 if (haveControl
&& withUndo
)
3896 newPara
= new wxRichTextParagraph(*para
);
3897 action
->GetNewParagraphs().AppendChild(newPara
);
3899 // Also store the old ones for Undo
3900 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3905 wxRichTextListStyleDefinition
* defToUse
= def
;
3908 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
3909 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
3914 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3915 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
3917 // If we've specified a level to apply to all, change the level.
3918 if (specifiedLevel
!= -1)
3919 thisLevel
= specifiedLevel
;
3921 // Do promotion if specified
3922 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
3924 thisLevel
= thisLevel
- promoteBy
;
3931 // Apply the overall list style, and item style for this level
3932 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3933 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3935 // OK, we've (re)applied the style, now let's get the numbering right.
3937 if (currentLevel
== -1)
3938 currentLevel
= thisLevel
;
3940 // Same level as before, do nothing except increment level's number afterwards
3941 if (currentLevel
== thisLevel
)
3944 // A deeper level: start renumbering all levels after current level
3945 else if (thisLevel
> currentLevel
)
3947 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
3951 currentLevel
= thisLevel
;
3953 else if (thisLevel
< currentLevel
)
3955 currentLevel
= thisLevel
;
3958 // Use the current numbering if -1 and we have a bullet number already
3959 if (levels
[currentLevel
] == -1)
3961 if (newPara
->GetAttributes().HasBulletNumber())
3962 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
3964 levels
[currentLevel
] = 1;
3968 levels
[currentLevel
] ++;
3971 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
3973 // Create the bullet text if an outline list
3974 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
3977 for (i
= 0; i
<= currentLevel
; i
++)
3979 if (!text
.IsEmpty())
3981 text
+= wxString::Format(wxT("%d"), levels
[i
]);
3983 newPara
->GetAttributes().SetBulletText(text
);
3989 node
= node
->GetNext();
3992 // Do action, or delay it until end of batch.
3993 if (haveControl
&& withUndo
)
3994 buffer
->SubmitAction(action
);
3999 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4001 wxRichTextBuffer
* buffer
= GetBuffer();
4002 if (buffer
->GetStyleSheet())
4004 wxRichTextListStyleDefinition
* def
= NULL
;
4005 if (!defName
.IsEmpty())
4006 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4007 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4012 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4013 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4016 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4017 // to NumberList with a flag indicating promotion is required within one of the ranges.
4018 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4019 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4020 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4021 // list position will start from 1.
4022 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4023 // We can end the renumbering at this point.
4025 // For now, only renumber within the promotion range.
4027 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4030 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4032 wxRichTextBuffer
* buffer
= GetBuffer();
4033 if (buffer
->GetStyleSheet())
4035 wxRichTextListStyleDefinition
* def
= NULL
;
4036 if (!defName
.IsEmpty())
4037 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4038 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4043 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4044 /// position of the paragraph that it had to start looking from.
4045 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4047 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4050 wxRichTextBuffer
* buffer
= GetBuffer();
4051 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4052 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4054 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4057 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4058 // int thisLevel = def->FindLevelForIndent(thisIndent);
4060 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4062 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4063 if (previousParagraph
->GetAttributes().HasBulletName())
4064 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4065 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4066 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4068 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4069 attr
.SetBulletNumber(nextNumber
);
4073 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4074 if (!text
.IsEmpty())
4076 int pos
= text
.Find(wxT('.'), true);
4077 if (pos
!= wxNOT_FOUND
)
4079 text
= text
.Mid(0, text
.Length() - pos
- 1);
4082 text
= wxEmptyString
;
4083 if (!text
.IsEmpty())
4085 text
+= wxString::Format(wxT("%d"), nextNumber
);
4086 attr
.SetBulletText(text
);
4100 * wxRichTextParagraph
4101 * This object represents a single paragraph (or in a straight text editor, a line).
4104 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4106 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4108 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4109 wxRichTextCompositeObject(parent
)
4112 SetAttributes(*style
);
4115 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4116 wxRichTextCompositeObject(parent
)
4119 SetAttributes(*paraStyle
);
4121 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4124 wxRichTextParagraph::~wxRichTextParagraph()
4130 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4135 // Currently we don't merge these attributes with the parent, but we
4136 // should consider whether we should (e.g. if we set a border colour
4137 // for all paragraphs). But generally box attributes are likely to be
4138 // different for different objects.
4139 wxRect paraRect
= GetRect();
4140 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4142 wxRichTextAttr attr
= GetCombinedAttributes();
4144 // Draw the bullet, if any
4145 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4147 if (attr
.GetLeftSubIndent() != 0)
4149 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4150 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4152 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4154 // Combine with the font of the first piece of content, if one is specified
4155 if (GetChildren().GetCount() > 0)
4157 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4158 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4160 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4164 // Get line height from first line, if any
4165 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4168 int lineHeight
wxDUMMY_INITIALIZE(0);
4171 lineHeight
= line
->GetSize().y
;
4172 linePos
= line
->GetPosition() + GetPosition();
4177 if (bulletAttr
.HasFont() && GetBuffer())
4178 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4180 font
= (*wxNORMAL_FONT
);
4182 wxCheckSetFont(dc
, font
);
4184 lineHeight
= dc
.GetCharHeight();
4185 linePos
= GetPosition();
4186 linePos
.y
+= spaceBeforePara
;
4189 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4191 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4193 if (wxRichTextBuffer::GetRenderer())
4194 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4196 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4198 if (wxRichTextBuffer::GetRenderer())
4199 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4203 wxString bulletText
= GetBulletText();
4205 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4206 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4211 // Draw the range for each line, one object at a time.
4213 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4216 wxRichTextLine
* line
= node
->GetData();
4217 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4219 // Lines are specified relative to the paragraph
4221 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4223 // Don't draw if off the screen
4224 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4226 wxPoint objectPosition
= linePosition
;
4227 int maxDescent
= line
->GetDescent();
4229 // Loop through objects until we get to the one within range
4230 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4235 wxRichTextObject
* child
= node2
->GetData();
4237 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4239 // Draw this part of the line at the correct position
4240 wxRichTextRange
objectRange(child
->GetRange());
4241 objectRange
.LimitTo(lineRange
);
4244 if (child
->IsTopLevel())
4246 objectSize
= child
->GetCachedSize();
4247 objectRange
= child
->GetOwnRange();
4251 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4252 if (i
< (int) line
->GetObjectSizes().GetCount())
4254 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4260 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4264 // Use the child object's width, but the whole line's height
4265 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4266 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4268 objectPosition
.x
+= objectSize
.x
;
4271 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4272 // Can break out of inner loop now since we've passed this line's range
4275 node2
= node2
->GetNext();
4279 node
= node
->GetNext();
4285 // Get the range width using partial extents calculated for the whole paragraph.
4286 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4288 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4290 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4293 int leftMostPos
= 0;
4294 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4295 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4297 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4299 int w
= rightMostPos
- leftMostPos
;
4304 /// Lay the item out
4305 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
4307 // Deal with floating objects firstly before the normal layout
4308 wxRichTextBuffer
* buffer
= GetBuffer();
4310 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4311 wxASSERT(collector
);
4312 LayoutFloat(dc
, rect
, style
, collector
);
4314 wxRichTextAttr attr
= GetCombinedAttributes();
4318 // Increase the size of the paragraph due to spacing
4319 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4320 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4321 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4322 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4323 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4325 int lineSpacing
= 0;
4327 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4328 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().Ok())
4330 wxCheckSetFont(dc
, attr
.GetFont());
4331 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4334 // Start position for each line relative to the paragraph
4335 int startPositionFirstLine
= leftIndent
;
4336 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4338 // If we have a bullet in this paragraph, the start position for the first line's text
4339 // is actually leftIndent + leftSubIndent.
4340 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4341 startPositionFirstLine
= startPositionSubsequentLines
;
4343 long lastEndPos
= GetRange().GetStart()-1;
4344 long lastCompletedEndPos
= lastEndPos
;
4346 int currentWidth
= 0;
4347 SetPosition(rect
.GetPosition());
4349 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4352 int maxHeight
= currentPosition
.y
;
4357 int lineDescent
= 0;
4359 wxRichTextObjectList::compatibility_iterator node
;
4361 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4363 wxArrayInt partialExtents
;
4366 int paraDescent
= 0;
4368 // This calculates the partial text extents
4369 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4371 node
= m_children
.GetFirst();
4374 wxRichTextObject
* child
= node
->GetData();
4376 //child->SetCachedSize(wxDefaultSize);
4377 child
->Layout(dc
, rect
, style
);
4379 node
= node
->GetNext();
4386 // We may need to go back to a previous child, in which case create the new line,
4387 // find the child corresponding to the start position of the string, and
4390 wxRect availableRect
;
4392 node
= m_children
.GetFirst();
4395 wxRichTextObject
* child
= node
->GetData();
4397 // If floating, ignore. We already laid out floats.
4398 // Also ignore if empty object, except if we haven't got any
4400 if (child
->IsFloating() || !child
->IsShown() ||
4401 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4404 node
= node
->GetNext();
4408 // If this is e.g. a composite text box, it will need to be laid out itself.
4409 // But if just a text fragment or image, for example, this will
4410 // do nothing. NB: won't we need to set the position after layout?
4411 // since for example if position is dependent on vertical line size, we
4412 // can't tell the position until the size is determined. So possibly introduce
4413 // another layout phase.
4415 // We may only be looking at part of a child, if we searched back for wrapping
4416 // and found a suitable point some way into the child. So get the size for the fragment
4419 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4420 long lastPosToUse
= child
->GetRange().GetEnd();
4421 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4423 if (lineBreakInThisObject
)
4424 lastPosToUse
= nextBreakPos
;
4427 int childDescent
= 0;
4429 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4430 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4431 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4433 if (child
->IsTopLevel())
4435 wxSize oldSize
= child
->GetCachedSize();
4437 child
->Invalidate(wxRICHTEXT_ALL
);
4438 child
->SetPosition(wxPoint(0, 0));
4440 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4441 // lays out the object again using the minimum size
4442 // The position will be determined by its location in its line,
4443 // and not by the child's actual position.
4444 child
->LayoutToBestSize(dc
, buffer
,
4445 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4447 if (oldSize
!= child
->GetCachedSize())
4449 partialExtents
.Clear();
4451 // Recalculate the partial text extents since the child object changed size
4452 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4456 // Problem: we need to layout composites here for which we need the available width,
4457 // but we can't get the available width without using the float collector which
4458 // needs to know the object height.
4460 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4462 childSize
= child
->GetCachedSize();
4463 childDescent
= child
->GetDescent();
4467 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4468 // Get height only, then the width using the partial extents
4469 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4470 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4472 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4477 int loopIterations
= 0;
4479 // If there are nested objects that need to lay themselves out, we have to do this in a
4480 // loop because the height of the object may well depend on the available width.
4481 // And because of floating object positioning, the available width depends on the
4482 // height of the object and whether it will clash with the floating objects.
4483 // So, we see whether the available width changes due to the presence of floating images.
4484 // If it does, then we'll use the new restricted width to find the object height again.
4485 // If this causes another restriction in the available width, we'll try again, until
4486 // either we lose patience or the available width settles down.
4491 wxRect oldAvailableRect
= availableRect
;
4493 // Available width depends on the floating objects and the line height.
4494 // Note: the floating objects may be placed vertically along the two side of
4495 // buffer, so we may have different available line widths with different
4496 // [startY, endY]. So, we can't determine how wide the available
4497 // space is until we know the exact line height.
4498 lineDescent
= wxMax(childDescent
, maxDescent
);
4499 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4500 lineHeight
= lineDescent
+ lineAscent
;
4501 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4503 // Adjust availableRect to the space that is available when taking floating objects into account.
4505 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4507 int newX
= floatAvailableRect
.x
+ startOffset
;
4508 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4509 availableRect
.x
= newX
;
4510 availableRect
.width
= newW
;
4513 if (floatAvailableRect
.width
< availableRect
.width
)
4514 availableRect
.width
= floatAvailableRect
.width
;
4516 currentPosition
.x
= availableRect
.x
- rect
.x
;
4518 if (child
->IsTopLevel() && loopIterations
<= 20)
4520 if (availableRect
!= oldAvailableRect
)
4522 wxSize oldSize
= child
->GetCachedSize();
4524 //child->SetCachedSize(wxDefaultSize);
4525 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4526 // lays out the object again using the minimum size
4527 child
->Invalidate(wxRICHTEXT_ALL
);
4528 child
->LayoutToBestSize(dc
, buffer
,
4529 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4530 childSize
= child
->GetCachedSize();
4531 childDescent
= child
->GetDescent();
4532 //child->SetPosition(availableRect.GetPosition());
4534 if (oldSize
!= child
->GetCachedSize())
4536 partialExtents
.Clear();
4538 // Recalculate the partial text extents since the child object changed size
4539 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4542 // Go around the loop finding the available rect for the given floating objects
4553 // 1) There was a line break BEFORE the natural break
4554 // 2) There was a line break AFTER the natural break
4555 // 3) It's the last line
4556 // 4) The child still fits (carry on) - 'else' clause
4558 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4560 (childSize
.x
+ currentWidth
> availableRect
.width
)
4562 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4566 if (child
->IsTopLevel())
4568 // We can move it to the correct position at this point
4569 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4572 long wrapPosition
= 0;
4573 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4574 wrapPosition
= child
->GetRange().GetEnd();
4577 // Find a place to wrap. This may walk back to previous children,
4578 // for example if a word spans several objects.
4579 // Note: one object must contains only one wxTextAtrr, so the line height will not
4580 // change inside one object. Thus, we can pass the remain line width to the
4581 // FindWrapPosition function.
4582 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4584 // If the function failed, just cut it off at the end of this child.
4585 wrapPosition
= child
->GetRange().GetEnd();
4588 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4589 if (wrapPosition
<= lastCompletedEndPos
)
4590 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4592 // Line end position shouldn't be the same as the end, or greater.
4593 if (wrapPosition
>= GetRange().GetEnd())
4594 wrapPosition
= GetRange().GetEnd()-1;
4596 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4598 // Let's find the actual size of the current line now
4600 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4602 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4603 /// for the fragment we're about to add.
4604 childDescent
= maxDescent
;
4606 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4607 if (!child
->IsEmpty())
4609 // Get height only, then the width using the partial extents
4610 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4611 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4615 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4617 currentWidth
= actualSize
.x
;
4618 maxDescent
= wxMax(childDescent
, maxDescent
);
4619 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4620 lineHeight
= maxDescent
+ maxAscent
;
4622 if (lineHeight
== 0 && buffer
)
4624 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4625 wxCheckSetFont(dc
, font
);
4626 lineHeight
= dc
.GetCharHeight();
4629 if (maxDescent
== 0)
4632 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4636 wxRichTextLine
* line
= AllocateLine(lineCount
);
4638 // Set relative range so we won't have to change line ranges when paragraphs are moved
4639 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4640 line
->SetPosition(currentPosition
);
4641 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4642 line
->SetDescent(maxDescent
);
4644 maxHeight
= currentPosition
.y
+ lineHeight
;
4646 // Now move down a line. TODO: add margins, spacing
4647 currentPosition
.y
+= lineHeight
;
4648 currentPosition
.y
+= lineSpacing
;
4651 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4656 // TODO: account for zero-length objects, such as fields
4657 // wxASSERT(wrapPosition > lastCompletedEndPos);
4659 lastEndPos
= wrapPosition
;
4660 lastCompletedEndPos
= lastEndPos
;
4664 if (wrapPosition
< GetRange().GetEnd()-1)
4666 // May need to set the node back to a previous one, due to searching back in wrapping
4667 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4668 if (childAfterWrapPosition
)
4669 node
= m_children
.Find(childAfterWrapPosition
);
4671 node
= node
->GetNext();
4674 node
= node
->GetNext();
4676 // Apply paragraph styles such as alignment to the wrapped line
4677 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4681 // We still fit, so don't add a line, and keep going
4682 currentWidth
+= childSize
.x
;
4683 maxDescent
= wxMax(childDescent
, maxDescent
);
4684 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4685 lineHeight
= maxDescent
+ maxAscent
;
4687 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4688 lastEndPos
= child
->GetRange().GetEnd();
4690 node
= node
->GetNext();
4694 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4696 // Remove remaining unused line objects, if any
4697 ClearUnusedLines(lineCount
);
4699 // We need to add back the margins etc.
4701 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4702 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4703 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4704 SetCachedSize(marginRect
.GetSize());
4707 // The maximum size is the length of the paragraph stretched out into a line.
4708 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4709 // this size. TODO: take into account line breaks.
4711 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4712 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
, currentPosition
.y
+ spaceAfterPara
));
4713 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4714 SetMaxSize(marginRect
.GetSize());
4717 // Find the greatest minimum size. Currently we only look at non-text objects,
4718 // which isn't ideal but it would be slow to find the maximum word width to
4719 // use as the minimum.
4722 node
= m_children
.GetFirst();
4725 wxRichTextObject
* child
= node
->GetData();
4727 // If floating, ignore. We already laid out floats.
4728 // Also ignore if empty object, except if we haven't got any
4730 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4732 if (child
->GetCachedSize().x
> minWidth
)
4733 minWidth
= child
->GetMinSize().x
;
4735 node
= node
->GetNext();
4738 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4739 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4740 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4741 SetMinSize(marginRect
.GetSize());
4745 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4746 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4747 // Use the text extents to calculate the size of each fragment in each line
4748 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4751 wxRichTextLine
* line
= lineNode
->GetData();
4752 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4754 // Loop through objects until we get to the one within range
4755 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4759 wxRichTextObject
* child
= node2
->GetData();
4761 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4763 wxRichTextRange rangeToUse
= lineRange
;
4764 rangeToUse
.LimitTo(child
->GetRange());
4766 // Find the size of the child from the text extents, and store in an array
4767 // for drawing later
4769 if (rangeToUse
.GetStart() > GetRange().GetStart())
4770 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4771 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4772 int sz
= right
- left
;
4773 line
->GetObjectSizes().Add(sz
);
4775 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4776 // Can break out of inner loop now since we've passed this line's range
4779 node2
= node2
->GetNext();
4782 lineNode
= lineNode
->GetNext();
4790 /// Apply paragraph styles, such as centering, to wrapped lines
4791 /// TODO: take into account box attributes, possibly
4792 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4794 if (!attr
.HasAlignment())
4797 wxPoint pos
= line
->GetPosition();
4798 wxSize size
= line
->GetSize();
4800 // centering, right-justification
4801 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4803 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4804 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
4805 line
->SetPosition(pos
);
4807 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4809 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4810 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
4811 line
->SetPosition(pos
);
4815 /// Insert text at the given position
4816 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
4818 wxRichTextObject
* childToUse
= NULL
;
4819 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
4821 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4824 wxRichTextObject
* child
= node
->GetData();
4825 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
4832 node
= node
->GetNext();
4837 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
4840 int posInString
= pos
- textObject
->GetRange().GetStart();
4842 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
4843 text
+ textObject
->GetText().Mid(posInString
);
4844 textObject
->SetText(newText
);
4846 int textLength
= text
.length();
4848 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
4849 textObject
->GetRange().GetEnd() + textLength
));
4851 // Increment the end range of subsequent fragments in this paragraph.
4852 // We'll set the paragraph range itself at a higher level.
4854 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
4857 wxRichTextObject
* child
= node
->GetData();
4858 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
4859 textObject
->GetRange().GetEnd() + textLength
));
4861 node
= node
->GetNext();
4868 // TODO: if not a text object, insert at closest position, e.g. in front of it
4874 // Don't pass parent initially to suppress auto-setting of parent range.
4875 // We'll do that at a higher level.
4876 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
4878 AppendChild(textObject
);
4885 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
4887 wxRichTextCompositeObject::Copy(obj
);
4890 /// Clear the cached lines
4891 void wxRichTextParagraph::ClearLines()
4893 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
4896 /// Get/set the object size for the given range. Returns false if the range
4897 /// is invalid for this object.
4898 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
4900 if (!range
.IsWithin(GetRange()))
4903 if (flags
& wxRICHTEXT_UNFORMATTED
)
4905 // Just use unformatted data, assume no line breaks
4906 // TODO: take into account line breaks
4910 wxArrayInt childExtents
;
4917 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4921 wxRichTextObject
* child
= node
->GetData();
4922 if (!child
->GetRange().IsOutside(range
))
4924 // Floating objects have a zero size within the paragraph.
4925 if (child
->IsFloating())
4930 if (partialExtents
->GetCount() > 0)
4931 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4935 partialExtents
->Add(0 /* zero size */ + lastSize
);
4942 wxRichTextRange rangeToUse
= range
;
4943 rangeToUse
.LimitTo(child
->GetRange());
4945 if (child
->IsTopLevel())
4946 rangeToUse
= child
->GetOwnRange();
4948 int childDescent
= 0;
4950 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4951 // but it's only going to be used after caching has taken place.
4952 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
4954 childDescent
= child
->GetDescent();
4955 childSize
= child
->GetCachedSize();
4957 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4958 sz
.x
+= childSize
.x
;
4959 descent
= wxMax(descent
, childDescent
);
4961 else if (child
->IsTopLevel())
4963 childDescent
= child
->GetDescent();
4964 childSize
= child
->GetCachedSize();
4966 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4967 sz
.x
+= childSize
.x
;
4968 descent
= wxMax(descent
, childDescent
);
4969 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
4971 child
->SetCachedSize(childSize
);
4972 child
->SetDescent(childDescent
);
4978 if (partialExtents
->GetCount() > 0)
4979 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4983 partialExtents
->Add(childSize
.x
+ lastSize
);
4986 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
4988 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4989 sz
.x
+= childSize
.x
;
4990 descent
= wxMax(descent
, childDescent
);
4992 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
4994 child
->SetCachedSize(childSize
);
4995 child
->SetDescent(childDescent
);
5001 if (partialExtents
->GetCount() > 0)
5002 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5007 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5009 partialExtents
->Add(childExtents
[i
] + lastSize
);
5019 node
= node
->GetNext();
5025 // Use formatted data, with line breaks
5028 // We're going to loop through each line, and then for each line,
5029 // call GetRangeSize for the fragment that comprises that line.
5030 // Only we have to do that multiple times within the line, because
5031 // the line may be broken into pieces. For now ignore line break commands
5032 // (so we can assume that getting the unformatted size for a fragment
5033 // within a line is the actual size)
5035 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5038 wxRichTextLine
* line
= node
->GetData();
5039 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5040 if (!lineRange
.IsOutside(range
))
5044 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5047 wxRichTextObject
* child
= node2
->GetData();
5049 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5051 wxRichTextRange rangeToUse
= lineRange
;
5052 rangeToUse
.LimitTo(child
->GetRange());
5053 if (child
->IsTopLevel())
5054 rangeToUse
= child
->GetOwnRange();
5057 int childDescent
= 0;
5058 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5060 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5061 lineSize
.x
+= childSize
.x
;
5063 descent
= wxMax(descent
, childDescent
);
5066 node2
= node2
->GetNext();
5069 // Increase size by a line (TODO: paragraph spacing)
5071 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5073 node
= node
->GetNext();
5080 /// Finds the absolute position and row height for the given character position
5081 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5085 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5087 *height
= line
->GetSize().y
;
5089 *height
= dc
.GetCharHeight();
5091 // -1 means 'the start of the buffer'.
5094 pt
= pt
+ line
->GetPosition();
5099 // The final position in a paragraph is taken to mean the position
5100 // at the start of the next paragraph.
5101 if (index
== GetRange().GetEnd())
5103 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5104 wxASSERT( parent
!= NULL
);
5106 // Find the height at the next paragraph, if any
5107 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5110 *height
= line
->GetSize().y
;
5111 pt
= line
->GetAbsolutePosition();
5115 *height
= dc
.GetCharHeight();
5116 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5117 pt
= wxPoint(indent
, GetCachedSize().y
);
5123 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5126 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5129 wxRichTextLine
* line
= node
->GetData();
5130 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5131 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5133 // If this is the last point in the line, and we're forcing the
5134 // returned value to be the start of the next line, do the required
5136 if (index
== lineRange
.GetEnd() && forceLineStart
)
5138 if (node
->GetNext())
5140 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5141 *height
= nextLine
->GetSize().y
;
5142 pt
= nextLine
->GetAbsolutePosition();
5147 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5149 wxRichTextRange
r(lineRange
.GetStart(), index
);
5153 // We find the size of the line up to this point,
5154 // then we can add this size to the line start position and
5155 // paragraph start position to find the actual position.
5157 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5159 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5160 *height
= line
->GetSize().y
;
5167 node
= node
->GetNext();
5173 /// Hit-testing: returns a flag indicating hit test details, plus
5174 /// information about position
5175 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5178 return wxRICHTEXT_HITTEST_NONE
;
5180 // If we're in the top-level container, then we can return
5181 // a suitable hit test code even if the point is outside the container area,
5182 // so that we can position the caret sensibly even if we don't
5183 // click on valid content. If we're not at the top-level, and the point
5184 // is not within this paragraph object, then we don't want to stop more
5185 // precise hit-testing from working prematurely, so return immediately.
5186 // NEW STRATEGY: use the parent boundary to test whether we're in the
5187 // right region, not the paragraph, since the paragraph may be positioned
5188 // some way in from where the user clicks.
5191 wxRichTextObject
* tempObj
, *tempContextObj
;
5192 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5193 return wxRICHTEXT_HITTEST_NONE
;
5196 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5199 wxRichTextObject
* child
= objNode
->GetData();
5200 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5203 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5204 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5209 objNode
= objNode
->GetNext();
5212 wxPoint paraPos
= GetPosition();
5214 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5217 wxRichTextLine
* line
= node
->GetData();
5218 wxPoint linePos
= paraPos
+ line
->GetPosition();
5219 wxSize lineSize
= line
->GetSize();
5220 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5222 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5224 if (pt
.x
< linePos
.x
)
5226 textPosition
= lineRange
.GetStart();
5227 *obj
= FindObjectAtPosition(textPosition
);
5228 *contextObj
= GetContainer();
5229 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5231 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5233 textPosition
= lineRange
.GetEnd();
5234 *obj
= FindObjectAtPosition(textPosition
);
5235 *contextObj
= GetContainer();
5236 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5240 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5241 wxArrayInt partialExtents
;
5246 // This calculates the partial text extents
5247 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), & partialExtents
);
5249 int lastX
= linePos
.x
;
5251 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5253 int nextX
= partialExtents
[i
] + linePos
.x
;
5255 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5257 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5259 *obj
= FindObjectAtPosition(textPosition
);
5260 *contextObj
= GetContainer();
5262 // So now we know it's between i-1 and i.
5263 // Let's see if we can be more precise about
5264 // which side of the position it's on.
5266 int midPoint
= (nextX
+ lastX
)/2;
5267 if (pt
.x
>= midPoint
)
5268 return wxRICHTEXT_HITTEST_AFTER
;
5270 return wxRICHTEXT_HITTEST_BEFORE
;
5277 int lastX
= linePos
.x
;
5278 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5283 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5285 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5287 int nextX
= childSize
.x
+ linePos
.x
;
5289 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5293 *obj
= FindObjectAtPosition(textPosition
);
5294 *contextObj
= GetContainer();
5296 // So now we know it's between i-1 and i.
5297 // Let's see if we can be more precise about
5298 // which side of the position it's on.
5300 int midPoint
= (nextX
+ lastX
)/2;
5301 if (pt
.x
>= midPoint
)
5302 return wxRICHTEXT_HITTEST_AFTER
;
5304 return wxRICHTEXT_HITTEST_BEFORE
;
5315 node
= node
->GetNext();
5318 return wxRICHTEXT_HITTEST_NONE
;
5321 /// Split an object at this position if necessary, and return
5322 /// the previous object, or NULL if inserting at beginning.
5323 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5325 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5328 wxRichTextObject
* child
= node
->GetData();
5330 if (pos
== child
->GetRange().GetStart())
5334 if (node
->GetPrevious())
5335 *previousObject
= node
->GetPrevious()->GetData();
5337 *previousObject
= NULL
;
5343 if (child
->GetRange().Contains(pos
))
5345 // This should create a new object, transferring part of
5346 // the content to the old object and the rest to the new object.
5347 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5349 // If we couldn't split this object, just insert in front of it.
5352 // Maybe this is an empty string, try the next one
5357 // Insert the new object after 'child'
5358 if (node
->GetNext())
5359 m_children
.Insert(node
->GetNext(), newObject
);
5361 m_children
.Append(newObject
);
5362 newObject
->SetParent(this);
5365 *previousObject
= child
;
5371 node
= node
->GetNext();
5374 *previousObject
= NULL
;
5378 /// Move content to a list from obj on
5379 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5381 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5384 wxRichTextObject
* child
= node
->GetData();
5387 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5389 node
= node
->GetNext();
5391 m_children
.DeleteNode(oldNode
);
5395 /// Add content back from list
5396 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5398 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5400 AppendChild((wxRichTextObject
*) node
->GetData());
5405 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5407 wxRichTextCompositeObject::CalculateRange(start
, end
);
5409 // Add one for end of paragraph
5412 m_range
.SetRange(start
, end
);
5415 /// Find the object at the given position
5416 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5418 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5421 wxRichTextObject
* obj
= node
->GetData();
5422 if (obj
->GetRange().Contains(position
) ||
5423 obj
->GetRange().GetStart() == position
||
5424 obj
->GetRange().GetEnd() == position
)
5427 node
= node
->GetNext();
5432 /// Get the plain text searching from the start or end of the range.
5433 /// The resulting string may be shorter than the range given.
5434 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5436 text
= wxEmptyString
;
5440 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5443 wxRichTextObject
* obj
= node
->GetData();
5444 if (!obj
->GetRange().IsOutside(range
))
5446 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5449 text
+= textObj
->GetTextForRange(range
);
5457 node
= node
->GetNext();
5462 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5465 wxRichTextObject
* obj
= node
->GetData();
5466 if (!obj
->GetRange().IsOutside(range
))
5468 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5471 text
= textObj
->GetTextForRange(range
) + text
;
5475 text
= wxT(" ") + text
;
5479 node
= node
->GetPrevious();
5486 /// Find a suitable wrap position.
5487 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5489 if (range
.GetLength() <= 0)
5492 // Find the first position where the line exceeds the available space.
5494 long breakPosition
= range
.GetEnd();
5496 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5497 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5501 if (range
.GetStart() > GetRange().GetStart())
5502 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5507 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5509 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5511 if (widthFromStartOfThisRange
> availableSpace
)
5513 breakPosition
= i
-1;
5521 // Binary chop for speed
5522 long minPos
= range
.GetStart();
5523 long maxPos
= range
.GetEnd();
5526 if (minPos
== maxPos
)
5529 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5531 if (sz
.x
> availableSpace
)
5532 breakPosition
= minPos
- 1;
5535 else if ((maxPos
- minPos
) == 1)
5538 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5540 if (sz
.x
> availableSpace
)
5541 breakPosition
= minPos
- 1;
5544 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5545 if (sz
.x
> availableSpace
)
5546 breakPosition
= maxPos
-1;
5552 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5555 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5557 if (sz
.x
> availableSpace
)
5569 // Now we know the last position on the line.
5570 // Let's try to find a word break.
5573 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5575 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5576 if (newLinePos
!= wxNOT_FOUND
)
5578 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5582 int spacePos
= plainText
.Find(wxT(' '), true);
5583 int tabPos
= plainText
.Find(wxT('\t'), true);
5584 int pos
= wxMax(spacePos
, tabPos
);
5585 if (pos
!= wxNOT_FOUND
)
5587 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5588 breakPosition
= breakPosition
- positionsFromEndOfString
;
5593 wrapPosition
= breakPosition
;
5598 /// Get the bullet text for this paragraph.
5599 wxString
wxRichTextParagraph::GetBulletText()
5601 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5602 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5603 return wxEmptyString
;
5605 int number
= GetAttributes().GetBulletNumber();
5608 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5610 text
.Printf(wxT("%d"), number
);
5612 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5614 // TODO: Unicode, and also check if number > 26
5615 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5617 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5619 // TODO: Unicode, and also check if number > 26
5620 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5622 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5624 text
= wxRichTextDecimalToRoman(number
);
5626 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5628 text
= wxRichTextDecimalToRoman(number
);
5631 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5633 text
= GetAttributes().GetBulletText();
5636 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5638 // The outline style relies on the text being computed statically,
5639 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5640 // should be stored in the attributes; if not, just use the number for this
5641 // level, as previously computed.
5642 if (!GetAttributes().GetBulletText().IsEmpty())
5643 text
= GetAttributes().GetBulletText();
5646 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5648 text
= wxT("(") + text
+ wxT(")");
5650 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5652 text
= text
+ wxT(")");
5655 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5663 /// Allocate or reuse a line object
5664 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5666 if (pos
< (int) m_cachedLines
.GetCount())
5668 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5674 wxRichTextLine
* line
= new wxRichTextLine(this);
5675 m_cachedLines
.Append(line
);
5680 /// Clear remaining unused line objects, if any
5681 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5683 int cachedLineCount
= m_cachedLines
.GetCount();
5684 if ((int) cachedLineCount
> lineCount
)
5686 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5688 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5689 wxRichTextLine
* line
= node
->GetData();
5690 m_cachedLines
.Erase(node
);
5697 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5698 /// retrieve the actual style.
5699 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5701 wxRichTextAttr attr
;
5702 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5705 attr
= buf
->GetBasicStyle();
5706 if (!includingBoxAttr
)
5708 attr
.GetTextBoxAttr().Reset();
5709 // The background colour will be painted by the container, and we don't
5710 // want to unnecessarily overwrite the background when we're drawing text
5711 // because this may erase the guideline (which appears just under the text
5712 // if there's no padding).
5713 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5715 wxRichTextApplyStyle(attr
, GetAttributes());
5718 attr
= GetAttributes();
5720 wxRichTextApplyStyle(attr
, contentStyle
);
5724 /// Get combined attributes of the base style and paragraph style.
5725 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5727 wxRichTextAttr attr
;
5728 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5731 attr
= buf
->GetBasicStyle();
5732 if (!includingBoxAttr
)
5733 attr
.GetTextBoxAttr().Reset();
5734 wxRichTextApplyStyle(attr
, GetAttributes());
5737 attr
= GetAttributes();
5742 // Create default tabstop array
5743 void wxRichTextParagraph::InitDefaultTabs()
5745 // create a default tab list at 10 mm each.
5746 for (int i
= 0; i
< 20; ++i
)
5748 sm_defaultTabs
.Add(i
*100);
5752 // Clear default tabstop array
5753 void wxRichTextParagraph::ClearDefaultTabs()
5755 sm_defaultTabs
.Clear();
5758 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5760 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5763 wxRichTextObject
* anchored
= node
->GetData();
5764 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5768 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5771 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5773 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5774 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5776 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5780 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5782 /* Update the offset */
5783 int newOffsetY
= pos
- rect
.y
;
5784 if (newOffsetY
!= offsetY
)
5786 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5787 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5788 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5791 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5793 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5794 x
= rect
.x
+ rect
.width
- size
.x
;
5796 anchored
->SetPosition(wxPoint(x
, pos
));
5797 anchored
->SetCachedSize(size
);
5798 floatCollector
->CollectFloat(this, anchored
);
5801 node
= node
->GetNext();
5805 // Get the first position from pos that has a line break character.
5806 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
5808 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5811 wxRichTextObject
* obj
= node
->GetData();
5812 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
5814 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5817 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
5822 node
= node
->GetNext();
5829 * This object represents a line in a paragraph, and stores
5830 * offsets from the start of the paragraph representing the
5831 * start and end positions of the line.
5834 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
5840 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
5843 m_range
.SetRange(-1, -1);
5844 m_pos
= wxPoint(0, 0);
5845 m_size
= wxSize(0, 0);
5847 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5848 m_objectSizes
.Clear();
5853 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
5855 m_range
= obj
.m_range
;
5856 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5857 m_objectSizes
= obj
.m_objectSizes
;
5861 /// Get the absolute object position
5862 wxPoint
wxRichTextLine::GetAbsolutePosition() const
5864 return m_parent
->GetPosition() + m_pos
;
5867 /// Get the absolute range
5868 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
5870 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
5871 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
5876 * wxRichTextPlainText
5877 * This object represents a single piece of text.
5880 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
5882 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
5883 wxRichTextObject(parent
)
5886 SetAttributes(*style
);
5891 #define USE_KERNING_FIX 1
5893 // If insufficient tabs are defined, this is the tab width used
5894 #define WIDTH_FOR_DEFAULT_TABS 50
5897 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
5899 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
5900 wxASSERT (para
!= NULL
);
5902 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5904 // Let's make the assumption for now that for content in a paragraph, including
5905 // text, we never have a discontinuous selection. So we only deal with a
5907 wxRichTextRange selectionRange
;
5908 if (selection
.IsValid())
5910 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
5911 if (selectionRanges
.GetCount() > 0)
5912 selectionRange
= selectionRanges
[0];
5914 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5917 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5919 int offset
= GetRange().GetStart();
5921 // Replace line break characters with spaces
5922 wxString str
= m_text
;
5923 wxString toRemove
= wxRichTextLineBreakChar
;
5924 str
.Replace(toRemove
, wxT(" "));
5925 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
5928 long len
= range
.GetLength();
5929 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
5931 // Test for the optimized situations where all is selected, or none
5934 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
5935 wxCheckSetFont(dc
, textFont
);
5936 int charHeight
= dc
.GetCharHeight();
5939 if ( textFont
.Ok() )
5941 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
5943 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5944 textFont
.SetPointSize( static_cast<int>(size
) );
5947 wxCheckSetFont(dc
, textFont
);
5949 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
5951 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5952 textFont
.SetPointSize( static_cast<int>(size
) );
5954 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
5955 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
5956 wxCheckSetFont(dc
, textFont
);
5961 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5967 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5970 // TODO: new selection code
5972 // (a) All selected.
5973 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
5975 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
5977 // (b) None selected.
5978 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
5980 // Draw all unselected
5981 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
5985 // (c) Part selected, part not
5986 // Let's draw unselected chunk, selected chunk, then unselected chunk.
5988 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
5990 // 1. Initial unselected chunk, if any, up until start of selection.
5991 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
5993 int r1
= range
.GetStart();
5994 int s1
= selectionRange
.GetStart()-1;
5995 int fragmentLen
= s1
- r1
+ 1;
5996 if (fragmentLen
< 0)
5998 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6000 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6002 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6005 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6007 // Compensate for kerning difference
6008 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6009 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6011 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6012 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6013 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6014 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6016 int kerningDiff
= (w1
+ w3
) - w2
;
6017 x
= x
- kerningDiff
;
6022 // 2. Selected chunk, if any.
6023 if (selectionRange
.GetEnd() >= range
.GetStart())
6025 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6026 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6028 int fragmentLen
= s2
- s1
+ 1;
6029 if (fragmentLen
< 0)
6031 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6033 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6035 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6038 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6040 // Compensate for kerning difference
6041 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6042 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6044 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6045 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6046 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6047 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6049 int kerningDiff
= (w1
+ w3
) - w2
;
6050 x
= x
- kerningDiff
;
6055 // 3. Remaining unselected chunk, if any
6056 if (selectionRange
.GetEnd() < range
.GetEnd())
6058 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6059 int r2
= range
.GetEnd();
6061 int fragmentLen
= r2
- s2
+ 1;
6062 if (fragmentLen
< 0)
6064 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6066 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6068 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6075 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6077 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6079 wxArrayInt tabArray
;
6083 if (attr
.GetTabs().IsEmpty())
6084 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6086 tabArray
= attr
.GetTabs();
6087 tabCount
= tabArray
.GetCount();
6089 for (int i
= 0; i
< tabCount
; ++i
)
6091 int pos
= tabArray
[i
];
6092 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6099 int nextTabPos
= -1;
6105 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6106 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6108 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6109 wxCheckSetPen(dc
, wxPen(highlightColour
));
6110 dc
.SetTextForeground(highlightTextColour
);
6111 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6115 dc
.SetTextForeground(attr
.GetTextColour());
6117 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6119 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6120 dc
.SetTextBackground(attr
.GetBackgroundColour());
6123 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6126 wxCoord x_orig
= GetParent()->GetPosition().x
;
6129 // the string has a tab
6130 // break up the string at the Tab
6131 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6132 str
= str
.AfterFirst(wxT('\t'));
6133 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6135 bool not_found
= true;
6136 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6138 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6140 // Find the next tab position.
6141 // Even if we're at the end of the tab array, we must still draw the chunk.
6143 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6145 if (nextTabPos
<= tabPos
)
6147 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6148 nextTabPos
= tabPos
+ defaultTabWidth
;
6155 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6156 dc
.DrawRectangle(selRect
);
6158 dc
.DrawText(stringChunk
, x
, y
);
6160 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6162 wxPen oldPen
= dc
.GetPen();
6163 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6164 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6165 wxCheckSetPen(dc
, oldPen
);
6171 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6176 dc
.GetTextExtent(str
, & w
, & h
);
6179 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6180 dc
.DrawRectangle(selRect
);
6182 dc
.DrawText(str
, x
, y
);
6184 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6186 wxPen oldPen
= dc
.GetPen();
6187 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6188 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6189 wxCheckSetPen(dc
, oldPen
);
6198 /// Lay the item out
6199 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
))
6201 // Only lay out if we haven't already cached the size
6203 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6205 // Eventually we want to have a reasonable estimate of minimum size.
6206 m_minSize
= wxSize(0, 0);
6211 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6213 wxRichTextObject::Copy(obj
);
6215 m_text
= obj
.m_text
;
6218 /// Get/set the object size for the given range. Returns false if the range
6219 /// is invalid for this object.
6220 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6222 if (!range
.IsWithin(GetRange()))
6225 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6226 wxASSERT (para
!= NULL
);
6228 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6230 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6232 // Always assume unformatted text, since at this level we have no knowledge
6233 // of line breaks - and we don't need it, since we'll calculate size within
6234 // formatted text by doing it in chunks according to the line ranges
6236 bool bScript(false);
6237 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6240 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6241 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6243 wxFont textFont
= font
;
6244 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6245 textFont
.SetPointSize( static_cast<int>(size
) );
6246 wxCheckSetFont(dc
, textFont
);
6251 wxCheckSetFont(dc
, font
);
6255 bool haveDescent
= false;
6256 int startPos
= range
.GetStart() - GetRange().GetStart();
6257 long len
= range
.GetLength();
6259 wxString
str(m_text
);
6260 wxString toReplace
= wxRichTextLineBreakChar
;
6261 str
.Replace(toReplace
, wxT(" "));
6263 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6265 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6266 stringChunk
.MakeUpper();
6270 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6272 // the string has a tab
6273 wxArrayInt tabArray
;
6274 if (textAttr
.GetTabs().IsEmpty())
6275 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6277 tabArray
= textAttr
.GetTabs();
6279 int tabCount
= tabArray
.GetCount();
6281 for (int i
= 0; i
< tabCount
; ++i
)
6283 int pos
= tabArray
[i
];
6284 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6288 int nextTabPos
= -1;
6290 while (stringChunk
.Find(wxT('\t')) >= 0)
6292 int absoluteWidth
= 0;
6294 // the string has a tab
6295 // break up the string at the Tab
6296 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6297 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6302 if (partialExtents
->GetCount() > 0)
6303 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6307 // Add these partial extents
6309 dc
.GetPartialTextExtents(stringFragment
, p
);
6311 for (j
= 0; j
< p
.GetCount(); j
++)
6312 partialExtents
->Add(oldWidth
+ p
[j
]);
6314 if (partialExtents
->GetCount() > 0)
6315 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6317 absoluteWidth
= relativeX
;
6321 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6323 absoluteWidth
= width
+ relativeX
;
6327 bool notFound
= true;
6328 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6330 nextTabPos
= tabArray
.Item(i
);
6332 // Find the next tab position.
6333 // Even if we're at the end of the tab array, we must still process the chunk.
6335 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6337 if (nextTabPos
<= absoluteWidth
)
6339 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6340 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6344 width
= nextTabPos
- relativeX
;
6347 partialExtents
->Add(width
);
6353 if (!stringChunk
.IsEmpty())
6358 if (partialExtents
->GetCount() > 0)
6359 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6363 // Add these partial extents
6365 dc
.GetPartialTextExtents(stringChunk
, p
);
6367 for (j
= 0; j
< p
.GetCount(); j
++)
6368 partialExtents
->Add(oldWidth
+ p
[j
]);
6372 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6380 int charHeight
= dc
.GetCharHeight();
6381 if ((*partialExtents
).GetCount() > 0)
6382 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6385 size
= wxSize(w
, charHeight
);
6389 size
= wxSize(width
, dc
.GetCharHeight());
6393 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6401 /// Do a split, returning an object containing the second part, and setting
6402 /// the first part in 'this'.
6403 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6405 long index
= pos
- GetRange().GetStart();
6407 if (index
< 0 || index
>= (int) m_text
.length())
6410 wxString firstPart
= m_text
.Mid(0, index
);
6411 wxString secondPart
= m_text
.Mid(index
);
6415 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6416 newObject
->SetAttributes(GetAttributes());
6418 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6419 GetRange().SetEnd(pos
-1);
6425 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6427 end
= start
+ m_text
.length() - 1;
6428 m_range
.SetRange(start
, end
);
6432 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6434 wxRichTextRange r
= range
;
6436 r
.LimitTo(GetRange());
6438 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6444 long startIndex
= r
.GetStart() - GetRange().GetStart();
6445 long len
= r
.GetLength();
6447 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6451 /// Get text for the given range.
6452 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6454 wxRichTextRange r
= range
;
6456 r
.LimitTo(GetRange());
6458 long startIndex
= r
.GetStart() - GetRange().GetStart();
6459 long len
= r
.GetLength();
6461 return m_text
.Mid(startIndex
, len
);
6464 /// Returns true if this object can merge itself with the given one.
6465 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6467 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6468 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6471 /// Returns true if this object merged itself with the given one.
6472 /// The calling code will then delete the given object.
6473 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6475 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6476 wxASSERT( textObject
!= NULL
);
6480 m_text
+= textObject
->GetText();
6481 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6488 /// Dump to output stream for debugging
6489 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6491 wxRichTextObject::Dump(stream
);
6492 stream
<< m_text
<< wxT("\n");
6495 /// Get the first position from pos that has a line break character.
6496 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6499 int len
= m_text
.length();
6500 int startPos
= pos
- m_range
.GetStart();
6501 for (i
= startPos
; i
< len
; i
++)
6503 wxChar ch
= m_text
[i
];
6504 if (ch
== wxRichTextLineBreakChar
)
6506 return i
+ m_range
.GetStart();
6514 * This is a kind of box, used to represent the whole buffer
6517 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6519 wxList
wxRichTextBuffer::sm_handlers
;
6520 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6521 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6522 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6525 void wxRichTextBuffer::Init()
6527 m_commandProcessor
= new wxCommandProcessor
;
6528 m_styleSheet
= NULL
;
6530 m_batchedCommandDepth
= 0;
6531 m_batchedCommand
= NULL
;
6538 wxRichTextBuffer::~wxRichTextBuffer()
6540 delete m_commandProcessor
;
6541 delete m_batchedCommand
;
6544 ClearEventHandlers();
6547 void wxRichTextBuffer::ResetAndClearCommands()
6551 GetCommandProcessor()->ClearCommands();
6554 Invalidate(wxRICHTEXT_ALL
);
6557 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6559 wxRichTextParagraphLayoutBox::Copy(obj
);
6561 m_styleSheet
= obj
.m_styleSheet
;
6562 m_modified
= obj
.m_modified
;
6563 m_batchedCommandDepth
= 0;
6564 if (m_batchedCommand
)
6565 delete m_batchedCommand
;
6566 m_batchedCommand
= NULL
;
6567 m_suppressUndo
= obj
.m_suppressUndo
;
6568 m_invalidRange
= obj
.m_invalidRange
;
6571 /// Push style sheet to top of stack
6572 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6575 styleSheet
->InsertSheet(m_styleSheet
);
6577 SetStyleSheet(styleSheet
);
6582 /// Pop style sheet from top of stack
6583 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6587 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6588 m_styleSheet
= oldSheet
->GetNextSheet();
6597 /// Submit command to insert paragraphs
6598 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6600 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(pos
, paragraphs
, ctrl
, this, flags
);
6603 /// Submit command to insert paragraphs
6604 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6606 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6608 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6610 wxRichTextAttr
* p
= NULL
;
6611 wxRichTextAttr paraAttr
;
6612 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6614 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6615 if (!paraAttr
.IsDefault())
6621 action
->GetNewParagraphs() = paragraphs
;
6623 if (p
&& !p
->IsDefault())
6625 for (wxRichTextObjectList::compatibility_iterator node
= action
->GetNewParagraphs().GetChildren().GetFirst(); node
; node
= node
->GetNext())
6627 wxRichTextObject
* child
= node
->GetData();
6628 child
->SetAttributes(*p
);
6632 action
->SetPosition(pos
);
6634 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6635 if (!paragraphs
.GetPartialParagraph())
6636 range
.SetEnd(range
.GetEnd()+1);
6638 // Set the range we'll need to delete in Undo
6639 action
->SetRange(range
);
6641 buffer
->SubmitAction(action
);
6646 /// Submit command to insert the given text
6647 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6649 return ctrl
->GetFocusObject()->InsertTextWithUndo(pos
, text
, ctrl
, this, flags
);
6652 /// Submit command to insert the given text
6653 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6655 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6657 wxRichTextAttr
* p
= NULL
;
6658 wxRichTextAttr paraAttr
;
6659 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6661 // Get appropriate paragraph style
6662 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6663 if (!paraAttr
.IsDefault())
6667 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6669 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6671 if (!text
.empty() && text
.Last() != wxT('\n'))
6673 // Don't count the newline when undoing
6675 action
->GetNewParagraphs().SetPartialParagraph(true);
6677 else if (!text
.empty() && text
.Last() == wxT('\n'))
6680 action
->SetPosition(pos
);
6682 // Set the range we'll need to delete in Undo
6683 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6685 buffer
->SubmitAction(action
);
6690 /// Submit command to insert the given text
6691 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6693 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(pos
, ctrl
, this, flags
);
6696 /// Submit command to insert the given text
6697 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6699 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6701 wxRichTextAttr
* p
= NULL
;
6702 wxRichTextAttr paraAttr
;
6703 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6705 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6706 if (!paraAttr
.IsDefault())
6710 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6712 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6713 action
->GetNewParagraphs().AppendChild(newPara
);
6714 action
->GetNewParagraphs().UpdateRanges();
6715 action
->GetNewParagraphs().SetPartialParagraph(false);
6716 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6720 newPara
->SetAttributes(*p
);
6722 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6724 if (para
&& para
->GetRange().GetEnd() == pos
)
6727 // Now see if we need to number the paragraph.
6728 if (newPara
->GetAttributes().HasBulletNumber())
6730 wxRichTextAttr numberingAttr
;
6731 if (FindNextParagraphNumber(para
, numberingAttr
))
6732 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6736 action
->SetPosition(pos
);
6738 // Use the default character style
6739 // Use the default character style
6740 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6742 // Check whether the default style merely reflects the paragraph/basic style,
6743 // in which case don't apply it.
6744 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6745 wxRichTextAttr toApply
;
6748 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6749 wxRichTextAttr newAttr
;
6750 // This filters out attributes that are accounted for by the current
6751 // paragraph/basic style
6752 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6755 toApply
= defaultStyle
;
6757 if (!toApply
.IsDefault())
6758 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6761 // Set the range we'll need to delete in Undo
6762 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6764 buffer
->SubmitAction(action
);
6769 /// Submit command to insert the given image
6770 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6771 const wxRichTextAttr
& textAttr
)
6773 return ctrl
->GetFocusObject()->InsertImageWithUndo(pos
, imageBlock
, ctrl
, this, flags
, textAttr
);
6776 /// Submit command to insert the given image
6777 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
,
6778 wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
,
6779 const wxRichTextAttr
& textAttr
)
6781 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6783 wxRichTextAttr
* p
= NULL
;
6784 wxRichTextAttr paraAttr
;
6785 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6787 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6788 if (!paraAttr
.IsDefault())
6792 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6794 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6796 newPara
->SetAttributes(*p
);
6798 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6799 newPara
->AppendChild(imageObject
);
6800 imageObject
->SetAttributes(textAttr
);
6801 action
->GetNewParagraphs().AppendChild(newPara
);
6802 action
->GetNewParagraphs().UpdateRanges();
6804 action
->GetNewParagraphs().SetPartialParagraph(true);
6806 action
->SetPosition(pos
);
6808 // Set the range we'll need to delete in Undo
6809 action
->SetRange(wxRichTextRange(pos
, pos
));
6811 buffer
->SubmitAction(action
);
6816 // Insert an object with no change of it
6817 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6819 return ctrl
->GetFocusObject()->InsertObjectWithUndo(pos
, object
, ctrl
, this, flags
);
6822 // Insert an object with no change of it
6823 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6825 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6827 wxRichTextAttr
* p
= NULL
;
6828 wxRichTextAttr paraAttr
;
6829 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6831 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6832 if (!paraAttr
.IsDefault())
6836 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6838 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6840 newPara
->SetAttributes(*p
);
6842 newPara
->AppendChild(object
);
6843 action
->GetNewParagraphs().AppendChild(newPara
);
6844 action
->GetNewParagraphs().UpdateRanges();
6846 action
->GetNewParagraphs().SetPartialParagraph(true);
6848 action
->SetPosition(pos
);
6850 // Set the range we'll need to delete in Undo
6851 action
->SetRange(wxRichTextRange(pos
, pos
));
6853 buffer
->SubmitAction(action
);
6855 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
6859 /// Get the style that is appropriate for a new paragraph at this position.
6860 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6862 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
6864 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
6867 wxRichTextAttr attr
;
6868 bool foundAttributes
= false;
6870 // Look for a matching paragraph style
6871 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
6873 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
6876 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6877 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
6879 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
6882 foundAttributes
= true;
6883 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6887 // If we didn't find the 'next style', use this style instead.
6888 if (!foundAttributes
)
6890 foundAttributes
= true;
6891 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6896 // Also apply list style if present
6897 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
6899 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
6902 int thisIndent
= para
->GetAttributes().GetLeftIndent();
6903 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
6905 // Apply the overall list style, and item style for this level
6906 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
6907 wxRichTextApplyStyle(attr
, listStyle
);
6908 attr
.SetOutlineLevel(thisLevel
);
6909 if (para
->GetAttributes().HasBulletNumber())
6910 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
6914 if (!foundAttributes
)
6916 attr
= para
->GetAttributes();
6917 int flags
= attr
.GetFlags();
6919 // Eliminate character styles
6920 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
6921 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
6922 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
6923 attr
.SetFlags(flags
);
6929 return wxRichTextAttr();
6932 /// Submit command to delete this range
6933 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
6935 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
6938 /// Submit command to delete this range
6939 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
6941 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
6943 action
->SetPosition(ctrl
->GetCaretPosition());
6945 // Set the range to delete
6946 action
->SetRange(range
);
6948 // Copy the fragment that we'll need to restore in Undo
6949 CopyFragment(range
, action
->GetOldParagraphs());
6951 // See if we're deleting a paragraph marker, in which case we need to
6952 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6953 if (range
.GetStart() == range
.GetEnd())
6955 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
6956 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
6958 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
6959 if (nextPara
&& nextPara
!= para
)
6961 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
6962 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
6967 buffer
->SubmitAction(action
);
6972 /// Collapse undo/redo commands
6973 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
6975 if (m_batchedCommandDepth
== 0)
6977 wxASSERT(m_batchedCommand
== NULL
);
6978 if (m_batchedCommand
)
6980 GetCommandProcessor()->Store(m_batchedCommand
);
6982 m_batchedCommand
= new wxRichTextCommand(cmdName
);
6985 m_batchedCommandDepth
++;
6990 /// Collapse undo/redo commands
6991 bool wxRichTextBuffer::EndBatchUndo()
6993 m_batchedCommandDepth
--;
6995 wxASSERT(m_batchedCommandDepth
>= 0);
6996 wxASSERT(m_batchedCommand
!= NULL
);
6998 if (m_batchedCommandDepth
== 0)
7000 GetCommandProcessor()->Store(m_batchedCommand
);
7001 m_batchedCommand
= NULL
;
7007 /// Submit immediately, or delay according to whether collapsing is on
7008 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7010 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7012 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7013 cmd
->AddAction(action
);
7015 cmd
->GetActions().Clear();
7018 m_batchedCommand
->AddAction(action
);
7022 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7023 cmd
->AddAction(action
);
7025 // Only store it if we're not suppressing undo.
7026 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7032 /// Begin suppressing undo/redo commands.
7033 bool wxRichTextBuffer::BeginSuppressUndo()
7040 /// End suppressing undo/redo commands.
7041 bool wxRichTextBuffer::EndSuppressUndo()
7048 /// Begin using a style
7049 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7051 wxRichTextAttr
newStyle(GetDefaultStyle());
7053 // Save the old default style
7054 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7056 wxRichTextApplyStyle(newStyle
, style
);
7057 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7059 SetDefaultStyle(newStyle
);
7065 bool wxRichTextBuffer::EndStyle()
7067 if (!m_attributeStack
.GetFirst())
7069 wxLogDebug(_("Too many EndStyle calls!"));
7073 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7074 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7075 m_attributeStack
.Erase(node
);
7077 SetDefaultStyle(*attr
);
7084 bool wxRichTextBuffer::EndAllStyles()
7086 while (m_attributeStack
.GetCount() != 0)
7091 /// Clear the style stack
7092 void wxRichTextBuffer::ClearStyleStack()
7094 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7095 delete (wxRichTextAttr
*) node
->GetData();
7096 m_attributeStack
.Clear();
7099 /// Begin using bold
7100 bool wxRichTextBuffer::BeginBold()
7102 wxRichTextAttr attr
;
7103 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7105 return BeginStyle(attr
);
7108 /// Begin using italic
7109 bool wxRichTextBuffer::BeginItalic()
7111 wxRichTextAttr attr
;
7112 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7114 return BeginStyle(attr
);
7117 /// Begin using underline
7118 bool wxRichTextBuffer::BeginUnderline()
7120 wxRichTextAttr attr
;
7121 attr
.SetFontUnderlined(true);
7123 return BeginStyle(attr
);
7126 /// Begin using point size
7127 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7129 wxRichTextAttr attr
;
7130 attr
.SetFontSize(pointSize
);
7132 return BeginStyle(attr
);
7135 /// Begin using this font
7136 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7138 wxRichTextAttr attr
;
7141 return BeginStyle(attr
);
7144 /// Begin using this colour
7145 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7147 wxRichTextAttr attr
;
7148 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7149 attr
.SetTextColour(colour
);
7151 return BeginStyle(attr
);
7154 /// Begin using alignment
7155 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7157 wxRichTextAttr attr
;
7158 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7159 attr
.SetAlignment(alignment
);
7161 return BeginStyle(attr
);
7164 /// Begin left indent
7165 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7167 wxRichTextAttr attr
;
7168 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7169 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7171 return BeginStyle(attr
);
7174 /// Begin right indent
7175 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7177 wxRichTextAttr attr
;
7178 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7179 attr
.SetRightIndent(rightIndent
);
7181 return BeginStyle(attr
);
7184 /// Begin paragraph spacing
7185 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7189 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7191 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7193 wxRichTextAttr attr
;
7194 attr
.SetFlags(flags
);
7195 attr
.SetParagraphSpacingBefore(before
);
7196 attr
.SetParagraphSpacingAfter(after
);
7198 return BeginStyle(attr
);
7201 /// Begin line spacing
7202 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7204 wxRichTextAttr attr
;
7205 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7206 attr
.SetLineSpacing(lineSpacing
);
7208 return BeginStyle(attr
);
7211 /// Begin numbered bullet
7212 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7214 wxRichTextAttr attr
;
7215 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7216 attr
.SetBulletStyle(bulletStyle
);
7217 attr
.SetBulletNumber(bulletNumber
);
7218 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7220 return BeginStyle(attr
);
7223 /// Begin symbol bullet
7224 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7226 wxRichTextAttr attr
;
7227 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7228 attr
.SetBulletStyle(bulletStyle
);
7229 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7230 attr
.SetBulletText(symbol
);
7232 return BeginStyle(attr
);
7235 /// Begin standard bullet
7236 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7238 wxRichTextAttr attr
;
7239 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7240 attr
.SetBulletStyle(bulletStyle
);
7241 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7242 attr
.SetBulletName(bulletName
);
7244 return BeginStyle(attr
);
7247 /// Begin named character style
7248 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7250 if (GetStyleSheet())
7252 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7255 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7256 return BeginStyle(attr
);
7262 /// Begin named paragraph style
7263 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7265 if (GetStyleSheet())
7267 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7270 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7271 return BeginStyle(attr
);
7277 /// Begin named list style
7278 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7280 if (GetStyleSheet())
7282 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7285 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7287 attr
.SetBulletNumber(number
);
7289 return BeginStyle(attr
);
7296 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7298 wxRichTextAttr attr
;
7300 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7302 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7305 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7310 return BeginStyle(attr
);
7313 /// Adds a handler to the end
7314 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7316 sm_handlers
.Append(handler
);
7319 /// Inserts a handler at the front
7320 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7322 sm_handlers
.Insert( handler
);
7325 /// Removes a handler
7326 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7328 wxRichTextFileHandler
*handler
= FindHandler(name
);
7331 sm_handlers
.DeleteObject(handler
);
7339 /// Finds a handler by filename or, if supplied, type
7340 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7341 wxRichTextFileType imageType
)
7343 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7344 return FindHandler(imageType
);
7345 else if (!filename
.IsEmpty())
7347 wxString path
, file
, ext
;
7348 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7349 return FindHandler(ext
, imageType
);
7356 /// Finds a handler by name
7357 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7359 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7362 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7363 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7365 node
= node
->GetNext();
7370 /// Finds a handler by extension and type
7371 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7373 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7376 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7377 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7378 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7380 node
= node
->GetNext();
7385 /// Finds a handler by type
7386 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7388 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7391 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7392 if (handler
->GetType() == type
) return handler
;
7393 node
= node
->GetNext();
7398 void wxRichTextBuffer::InitStandardHandlers()
7400 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7401 AddHandler(new wxRichTextPlainTextHandler
);
7404 void wxRichTextBuffer::CleanUpHandlers()
7406 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7409 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7410 wxList::compatibility_iterator next
= node
->GetNext();
7415 sm_handlers
.Clear();
7418 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7425 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7429 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7430 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7435 wildcard
+= wxT(";");
7436 wildcard
+= wxT("*.") + handler
->GetExtension();
7441 wildcard
+= wxT("|");
7442 wildcard
+= handler
->GetName();
7443 wildcard
+= wxT(" ");
7444 wildcard
+= _("files");
7445 wildcard
+= wxT(" (*.");
7446 wildcard
+= handler
->GetExtension();
7447 wildcard
+= wxT(")|*.");
7448 wildcard
+= handler
->GetExtension();
7450 types
->Add(handler
->GetType());
7455 node
= node
->GetNext();
7459 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7464 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7466 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7469 SetDefaultStyle(wxRichTextAttr());
7470 handler
->SetFlags(GetHandlerFlags());
7471 bool success
= handler
->LoadFile(this, filename
);
7472 Invalidate(wxRICHTEXT_ALL
);
7480 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7482 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7485 handler
->SetFlags(GetHandlerFlags());
7486 return handler
->SaveFile(this, filename
);
7492 /// Load from a stream
7493 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7495 wxRichTextFileHandler
* handler
= FindHandler(type
);
7498 SetDefaultStyle(wxRichTextAttr());
7499 handler
->SetFlags(GetHandlerFlags());
7500 bool success
= handler
->LoadFile(this, stream
);
7501 Invalidate(wxRICHTEXT_ALL
);
7508 /// Save to a stream
7509 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7511 wxRichTextFileHandler
* handler
= FindHandler(type
);
7514 handler
->SetFlags(GetHandlerFlags());
7515 return handler
->SaveFile(this, stream
);
7521 /// Copy the range to the clipboard
7522 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7524 bool success
= false;
7525 wxRichTextParagraphLayoutBox
* container
= this;
7526 if (GetRichTextCtrl())
7527 container
= GetRichTextCtrl()->GetFocusObject();
7529 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7531 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7533 wxTheClipboard
->Clear();
7535 // Add composite object
7537 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7540 wxString text
= container
->GetTextForRange(range
);
7543 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7546 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7549 // Add rich text buffer data object. This needs the XML handler to be present.
7551 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7553 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7554 container
->CopyFragment(range
, *richTextBuf
);
7556 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7559 if (wxTheClipboard
->SetData(compositeObject
))
7562 wxTheClipboard
->Close();
7571 /// Paste the clipboard content to the buffer
7572 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7574 bool success
= false;
7575 wxRichTextParagraphLayoutBox
* container
= this;
7576 if (GetRichTextCtrl())
7577 container
= GetRichTextCtrl()->GetFocusObject();
7579 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7580 if (CanPasteFromClipboard())
7582 if (wxTheClipboard
->Open())
7584 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7586 wxRichTextBufferDataObject data
;
7587 wxTheClipboard
->GetData(data
);
7588 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7591 container
->InsertParagraphsWithUndo(position
+1, *richTextBuffer
, GetRichTextCtrl(), this, 0);
7592 if (GetRichTextCtrl())
7593 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7594 delete richTextBuffer
;
7597 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7599 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7603 wxTextDataObject data
;
7604 wxTheClipboard
->GetData(data
);
7605 wxString
text(data
.GetText());
7608 text2
.Alloc(text
.Length()+1);
7610 for (i
= 0; i
< text
.Length(); i
++)
7612 wxChar ch
= text
[i
];
7613 if (ch
!= wxT('\r'))
7617 wxString text2
= text
;
7619 container
->InsertTextWithUndo(position
+1, text2
, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7621 if (GetRichTextCtrl())
7622 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7626 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7628 wxBitmapDataObject data
;
7629 wxTheClipboard
->GetData(data
);
7630 wxBitmap
bitmap(data
.GetBitmap());
7631 wxImage
image(bitmap
.ConvertToImage());
7633 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7635 action
->GetNewParagraphs().AddImage(image
);
7637 if (action
->GetNewParagraphs().GetChildCount() == 1)
7638 action
->GetNewParagraphs().SetPartialParagraph(true);
7640 action
->SetPosition(position
+1);
7642 // Set the range we'll need to delete in Undo
7643 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7645 SubmitAction(action
);
7649 wxTheClipboard
->Close();
7653 wxUnusedVar(position
);
7658 /// Can we paste from the clipboard?
7659 bool wxRichTextBuffer::CanPasteFromClipboard() const
7661 bool canPaste
= false;
7662 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7663 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7665 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7667 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7669 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7670 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7674 wxTheClipboard
->Close();
7680 /// Dumps contents of buffer for debugging purposes
7681 void wxRichTextBuffer::Dump()
7685 wxStringOutputStream
stream(& text
);
7686 wxTextOutputStream
textStream(stream
);
7693 /// Add an event handler
7694 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7696 m_eventHandlers
.Append(handler
);
7700 /// Remove an event handler
7701 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7703 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7706 m_eventHandlers
.Erase(node
);
7716 /// Clear event handlers
7717 void wxRichTextBuffer::ClearEventHandlers()
7719 m_eventHandlers
.Clear();
7722 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7723 /// otherwise will stop at the first successful one.
7724 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7726 bool success
= false;
7727 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7729 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7730 if (handler
->ProcessEvent(event
))
7740 /// Set style sheet and notify of the change
7741 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7743 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7745 wxWindowID id
= wxID_ANY
;
7746 if (GetRichTextCtrl())
7747 id
= GetRichTextCtrl()->GetId();
7749 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, id
);
7750 event
.SetEventObject(GetRichTextCtrl());
7751 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7752 event
.SetOldStyleSheet(oldSheet
);
7753 event
.SetNewStyleSheet(sheet
);
7756 if (SendEvent(event
) && !event
.IsAllowed())
7758 if (sheet
!= oldSheet
)
7764 if (oldSheet
&& oldSheet
!= sheet
)
7767 SetStyleSheet(sheet
);
7769 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7770 event
.SetOldStyleSheet(NULL
);
7773 return SendEvent(event
);
7776 /// Set renderer, deleting old one
7777 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7781 sm_renderer
= renderer
;
7784 /// Hit-testing: returns a flag indicating hit test details, plus
7785 /// information about position
7786 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7788 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7789 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7795 textPosition
= m_ownRange
.GetEnd()-1;
7798 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7802 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7804 if (bulletAttr
.GetTextColour().Ok())
7806 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7807 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7811 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7812 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7816 if (bulletAttr
.HasFont())
7818 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
7821 font
= (*wxNORMAL_FONT
);
7823 wxCheckSetFont(dc
, font
);
7825 int charHeight
= dc
.GetCharHeight();
7827 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
7828 int bulletHeight
= bulletWidth
;
7832 // Calculate the top position of the character (as opposed to the whole line height)
7833 int y
= rect
.y
+ (rect
.height
- charHeight
);
7835 // Calculate where the bullet should be positioned
7836 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
7838 // The margin between a bullet and text.
7839 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7841 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7842 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
7843 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7844 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
7846 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
7848 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
7850 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
7853 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
7854 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
7855 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
7856 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
7858 dc
.DrawPolygon(4, pts
);
7860 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
7863 pts
[0].x
= x
; pts
[0].y
= y
;
7864 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
7865 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
7867 dc
.DrawPolygon(3, pts
);
7869 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
7871 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
7872 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7874 else // "standard/circle", and catch-all
7876 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7882 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
7887 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
7889 wxRichTextAttr fontAttr
;
7890 fontAttr
.SetFontSize(attr
.GetFontSize());
7891 fontAttr
.SetFontStyle(attr
.GetFontStyle());
7892 fontAttr
.SetFontWeight(attr
.GetFontWeight());
7893 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
7894 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
7895 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
7897 else if (attr
.HasFont())
7898 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
7900 font
= (*wxNORMAL_FONT
);
7902 wxCheckSetFont(dc
, font
);
7904 if (attr
.GetTextColour().Ok())
7905 dc
.SetTextForeground(attr
.GetTextColour());
7907 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
7909 int charHeight
= dc
.GetCharHeight();
7911 dc
.GetTextExtent(text
, & tw
, & th
);
7915 // Calculate the top position of the character (as opposed to the whole line height)
7916 int y
= rect
.y
+ (rect
.height
- charHeight
);
7918 // The margin between a bullet and text.
7919 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7921 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7922 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
7923 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7924 x
= x
+ (rect
.width
)/2 - tw
/2;
7926 dc
.DrawText(text
, x
, y
);
7934 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
7936 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7937 // with the buffer. The store will allow retrieval from memory, disk or other means.
7941 /// Enumerate the standard bullet names currently supported
7942 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
7944 bulletNames
.Add(wxTRANSLATE("standard/circle"));
7945 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
7946 bulletNames
.Add(wxTRANSLATE("standard/square"));
7947 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
7948 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
7957 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
7959 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
7960 wxRichTextParagraphLayoutBox(parent
)
7965 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
7970 // TODO: if the active object in the control, draw an indication.
7971 // We need to add the concept of active object, and not just focus object,
7972 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
7973 // Ultimately we would like to be able to interactively resize an active object
7974 // using drag handles.
7975 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
7979 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
7981 wxRichTextParagraphLayoutBox::Copy(obj
);
7984 // Edit properties via a GUI
7985 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
7987 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
7988 boxDlg
.SetAttributes(GetAttributes());
7990 if (boxDlg
.ShowModal() == wxID_OK
)
7992 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
7993 // indeterminate in the object.
7994 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8001 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8003 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8004 wxRichTextBox(parent
)
8009 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8011 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8015 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8017 wxRichTextBox::Copy(obj
);
8020 // Edit properties via a GUI
8021 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8023 // We need to gather common attributes for all selected cells.
8025 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8026 bool multipleCells
= false;
8027 wxRichTextAttr attr
;
8029 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8030 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8032 wxRichTextAttr clashingAttr
, absentAttr
;
8033 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8035 int selectedCellCount
= 0;
8036 for (i
= 0; i
< sel
.GetCount(); i
++)
8038 const wxRichTextRange
& range
= sel
[i
];
8039 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8042 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8044 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8046 selectedCellCount
++;
8049 multipleCells
= selectedCellCount
> 1;
8053 attr
= GetAttributes();
8058 caption
= _("Multiple Cell Properties");
8060 caption
= _("Cell Properties");
8062 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8063 cellDlg
.SetAttributes(attr
);
8065 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8068 // We don't want position and floating controls for a cell.
8069 sizePage
->ShowPositionControls(false);
8070 sizePage
->ShowFloatingControls(false);
8073 if (cellDlg
.ShowModal() == wxID_OK
)
8077 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8078 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8079 // since it may represent clashing attributes across multiple objects.
8080 table
->SetCellStyle(sel
, attr
);
8083 // For a single object, indeterminate attributes set by the user should be reflected in the
8084 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8085 // the style directly instead of applying (which ignores indeterminate attributes,
8086 // leaving them as they were).
8087 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8094 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8096 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8098 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8104 // Draws the object.
8105 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8107 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8110 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8111 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8113 // Lays the object out. rect is the space available for layout. Often it will
8114 // be the specified overall space for this object, if trying to constrain
8115 // layout to a particular size, or it could be the total space available in the
8116 // parent. rect is the overall size, so we must subtract margins and padding.
8117 // to get the actual available space.
8118 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
8120 SetPosition(rect
.GetPosition());
8122 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8123 // minimum size if within alloted size, then divide up remaining size
8124 // between rows/cols.
8127 wxRichTextBuffer
* buffer
= GetBuffer();
8128 if (buffer
) scale
= buffer
->GetScale();
8130 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8131 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8133 // If we have no fixed table size, and assuming we're not pushed for
8134 // space, then we don't have to try to stretch the table to fit the contents.
8135 bool stretchToFitTableWidth
= false;
8137 int tableWidth
= rect
.width
;
8138 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8140 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8142 // Fixed table width, so we do want to stretch columns out if necessary.
8143 stretchToFitTableWidth
= true;
8145 // Shouldn't be able to exceed the size passed to this function
8146 tableWidth
= wxMin(rect
.width
, tableWidth
);
8149 // Get internal padding
8150 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
8151 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8152 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8153 if (GetAttributes().GetTextBoxAttr().GetPadding().GetRight().IsValid())
8154 paddingRight
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetRight());
8155 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8156 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8157 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8158 paddingBottom
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetBottom());
8160 // Assume that left and top padding are also used for inter-cell padding.
8161 int paddingX
= paddingLeft
;
8162 int paddingY
= paddingTop
;
8164 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8165 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8167 // Internal table width - the area for content
8168 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8170 int rowCount
= m_cells
.GetCount();
8171 if (m_colCount
== 0 || rowCount
== 0)
8173 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8174 SetCachedSize(overallRect
.GetSize());
8176 // Zero content size
8177 SetMinSize(overallRect
.GetSize());
8178 SetMaxSize(GetMinSize());
8182 // The final calculated widths
8183 wxArrayInt
colWidths(m_colCount
);
8185 wxArrayInt
absoluteColWidths(m_colCount
);
8186 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8187 wxArrayInt
percentageColWidths(m_colCount
);
8188 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8189 // These are only relevant when the first column contains spanning information.
8190 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8191 wxArrayInt
maxColWidths(m_colCount
);
8192 wxArrayInt
minColWidths(m_colCount
);
8194 wxSize
tableSize(tableWidth
, 0);
8198 for (i
= 0; i
< m_colCount
; i
++)
8200 absoluteColWidths
[i
] = 0;
8201 // absoluteColWidthsSpanning[i] = 0;
8202 percentageColWidths
[i
] = -1;
8203 // percentageColWidthsSpanning[i] = -1;
8205 maxColWidths
[i
] = 0;
8206 minColWidths
[i
] = 0;
8207 // columnSpans[i] = 1;
8210 // (0) Determine which cells are visible according to spans
8212 // __________________
8217 // |------------------|
8218 // |__________________| 4
8220 // To calculate cell visibility:
8221 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8222 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8223 // that cell, hide the cell.
8225 // We can also use this array to match the size of spanning cells to the grid. Or just do
8226 // this when we iterate through all cells.
8228 // 0.1: add spanning cells to an array
8229 wxRichTextRectArray rectArray
;
8230 for (j
= 0; j
< m_rowCount
; j
++)
8232 for (i
= 0; i
< m_colCount
; i
++)
8234 wxRichTextBox
* cell
= GetCell(j
, i
);
8235 int colSpan
= 1, rowSpan
= 1;
8236 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8237 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8238 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8239 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8240 if (colSpan
> 1 || rowSpan
> 1)
8242 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8246 // 0.2: find which cells are subsumed by a spanning cell
8247 for (j
= 0; j
< m_rowCount
; j
++)
8249 for (i
= 0; i
< m_colCount
; i
++)
8251 wxRichTextBox
* cell
= GetCell(j
, i
);
8252 if (rectArray
.GetCount() == 0)
8258 int colSpan
= 1, rowSpan
= 1;
8259 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8260 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8261 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8262 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8263 if (colSpan
> 1 || rowSpan
> 1)
8265 // Assume all spanning cells are shown
8271 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8273 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8285 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8286 // overlap with a spanned cell starting at a previous column position.
8287 // This means we need to keep an array of rects so we can check. However
8288 // it does also mean that some spans simply may not be taken into account
8289 // where there are different spans happening on different rows. In these cases,
8290 // they will simply be as wide as their constituent columns.
8292 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8293 // the absolute or percentage width of each column.
8295 for (j
= 0; j
< m_rowCount
; j
++)
8297 // First get the overall margins so we can calculate percentage widths based on
8298 // the available content space for all cells on the row
8300 int overallRowContentMargin
= 0;
8301 int visibleCellCount
= 0;
8303 for (i
= 0; i
< m_colCount
; i
++)
8305 wxRichTextBox
* cell
= GetCell(j
, i
);
8306 if (cell
->IsShown())
8308 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8309 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8311 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8312 visibleCellCount
++;
8316 // Add in inter-cell padding
8317 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8319 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8320 wxSize
rowTableSize(rowContentWidth
, 0);
8321 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8323 for (i
= 0; i
< m_colCount
; i
++)
8325 wxRichTextBox
* cell
= GetCell(j
, i
);
8326 if (cell
->IsShown())
8329 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8330 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8332 // Lay out cell to find min/max widths
8333 cell
->Invalidate(wxRICHTEXT_ALL
);
8334 cell
->Layout(dc
, availableSpace
, style
);
8338 int absoluteCellWidth
= -1;
8339 int percentageCellWidth
= -1;
8341 // I think we need to calculate percentages from the internal table size,
8342 // minus the padding between cells which we'll need to calculate from the
8343 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8344 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8345 // so if we want to conform to that we'll need to add in the overall cell margins.
8346 // However, this will make it difficult to specify percentages that add up to
8347 // 100% and still fit within the table width.
8348 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8349 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8350 // If we're using internal content size for the width, we would calculate the
8351 // the overall cell width for n cells as:
8352 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8353 // + thisOverallCellMargin
8354 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8355 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8357 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8359 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8360 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8362 percentageCellWidth
= w
;
8366 absoluteCellWidth
= w
;
8368 // Override absolute width with minimum width if necessary
8369 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8370 absoluteCellWidth
= cell
->GetMinSize().x
;
8373 if (absoluteCellWidth
!= -1)
8375 if (absoluteCellWidth
> absoluteColWidths
[i
])
8376 absoluteColWidths
[i
] = absoluteCellWidth
;
8379 if (percentageCellWidth
!= -1)
8381 if (percentageCellWidth
> percentageColWidths
[i
])
8382 percentageColWidths
[i
] = percentageCellWidth
;
8385 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8386 minColWidths
[i
] = cell
->GetMinSize().x
;
8387 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8388 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8394 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8395 // TODO: simply merge this into (1).
8396 for (i
= 0; i
< m_colCount
; i
++)
8398 if (absoluteColWidths
[i
] > 0)
8400 colWidths
[i
] = absoluteColWidths
[i
];
8402 else if (percentageColWidths
[i
] > 0)
8404 colWidths
[i
] = percentageColWidths
[i
];
8406 // This is rubbish - we calculated the absolute widths from percentages, so
8407 // we can't do it again here.
8408 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8412 // (3) Process absolute or proportional widths of spanning columns,
8413 // now that we know what our fixed column widths are going to be.
8414 // Spanned cells will try to adjust columns so the span will fit.
8415 // Even existing fixed column widths can be expanded if necessary.
8416 // Actually, currently fixed columns widths aren't adjusted; instead,
8417 // the algorithm favours earlier rows and adjusts unspecified column widths
8418 // the first time only. After that, we can't know whether the column has been
8419 // specified explicitly or not. (We could make a note if necessary.)
8420 for (j
= 0; j
< m_rowCount
; j
++)
8422 // First get the overall margins so we can calculate percentage widths based on
8423 // the available content space for all cells on the row
8425 int overallRowContentMargin
= 0;
8426 int visibleCellCount
= 0;
8428 for (i
= 0; i
< m_colCount
; i
++)
8430 wxRichTextBox
* cell
= GetCell(j
, i
);
8431 if (cell
->IsShown())
8433 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8434 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8436 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8437 visibleCellCount
++;
8441 // Add in inter-cell padding
8442 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8444 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8445 wxSize
rowTableSize(rowContentWidth
, 0);
8446 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8448 for (i
= 0; i
< m_colCount
; i
++)
8450 wxRichTextBox
* cell
= GetCell(j
, i
);
8451 if (cell
->IsShown())
8454 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8455 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8459 int spans
= wxMin(colSpan
, m_colCount
- i
);
8463 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8465 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8466 // Override absolute width with minimum width if necessary
8467 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8468 cellWidth
= cell
->GetMinSize().x
;
8472 // Do we want to do this? It's the only chance we get to
8473 // use the cell's min/max sizes, so we need to work out
8474 // how we're going to balance the unspecified spanning cell
8475 // width with the possibility more-constrained constituent cell widths.
8476 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8477 // don't want to constraint all the spanned columns to fit into this cell.
8478 // OK, let's say that if any of the constituent columns don't fit,
8479 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8480 // cells to the columns later.
8481 cellWidth
= cell
->GetMinSize().x
;
8482 if (cell
->GetMaxSize().x
> cellWidth
)
8483 cellWidth
= cell
->GetMaxSize().x
;
8486 // Subtract the padding between cells
8487 int spanningWidth
= cellWidth
;
8488 spanningWidth
-= paddingX
* (spans
-1);
8490 if (spanningWidth
> 0)
8492 // Now share the spanning width between columns within that span
8493 // TODO: take into account min widths of columns within the span
8494 int spanningWidthLeft
= spanningWidth
;
8495 int stretchColCount
= 0;
8496 for (k
= i
; k
< (i
+spans
); k
++)
8498 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8499 spanningWidthLeft
-= colWidths
[k
];
8503 // Now divide what's left between the remaining columns
8505 if (stretchColCount
> 0)
8506 colShare
= spanningWidthLeft
/ stretchColCount
;
8507 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8509 // If fixed-width columns are currently too big, then we'll later
8510 // stretch the spanned cell to fit.
8512 if (spanningWidthLeft
> 0)
8514 for (k
= i
; k
< (i
+spans
); k
++)
8516 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8518 int newWidth
= colShare
;
8519 if (k
== (i
+spans
-1))
8520 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8521 colWidths
[k
] = newWidth
;
8532 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8533 // TODO: take into account min widths of columns within the span
8534 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8535 int widthLeft
= tableWidthMinusPadding
;
8536 int stretchColCount
= 0;
8537 for (i
= 0; i
< m_colCount
; i
++)
8539 // TODO: we need to take into account min widths.
8540 // Subtract min width from width left, then
8541 // add the colShare to the min width
8542 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8543 widthLeft
-= colWidths
[i
];
8546 if (minColWidths
[i
] > 0)
8547 widthLeft
-= minColWidths
[i
];
8553 // Now divide what's left between the remaining columns
8555 if (stretchColCount
> 0)
8556 colShare
= widthLeft
/ stretchColCount
;
8557 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8559 // Check we don't have enough space, in which case shrink all columns, overriding
8560 // any absolute/proportional widths
8561 // TODO: actually we would like to divide up the shrinkage according to size.
8562 // How do we calculate the proportions that will achieve this?
8563 // Could first choose an arbitrary value for stretching cells, and then calculate
8564 // factors to multiply each width by.
8565 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8566 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8568 colShare
= tableWidthMinusPadding
/ m_colCount
;
8569 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8570 for (i
= 0; i
< m_colCount
; i
++)
8573 minColWidths
[i
] = 0;
8577 // We have to adjust the columns if either we need to shrink the
8578 // table to fit the parent/table width, or we explicitly set the
8579 // table width and need to stretch out the table.
8580 if (widthLeft
< 0 || stretchToFitTableWidth
)
8582 for (i
= 0; i
< m_colCount
; i
++)
8584 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8586 if (minColWidths
[i
] > 0)
8587 colWidths
[i
] = minColWidths
[i
] + colShare
;
8589 colWidths
[i
] = colShare
;
8590 if (i
== (m_colCount
-1))
8591 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8596 // TODO: if spanned cells have no specified or max width, make them the
8597 // as big as the columns they span. Do this for all spanned cells in all
8598 // rows, of course. Size any spanned cells left over at the end - even if they
8599 // have width > 0, make sure they're limited to the appropriate column edge.
8603 Sort out confusion between content width
8604 and overall width later. For now, assume we specify overall width.
8606 So, now we've laid out the table to fit into the given space
8607 and have used specified widths and minimum widths.
8609 Now we need to consider how we will try to take maximum width into account.
8613 // (??) TODO: take max width into account
8615 // (6) Lay out all cells again with the current values
8618 int y
= availableSpace
.y
;
8619 for (j
= 0; j
< m_rowCount
; j
++)
8621 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8622 int maxCellHeight
= 0;
8623 int maxSpecifiedCellHeight
= 0;
8625 wxArrayInt
actualWidths(m_colCount
);
8627 wxTextAttrDimensionConverter
converter(dc
, scale
);
8628 for (i
= 0; i
< m_colCount
; i
++)
8630 wxRichTextCell
* cell
= GetCell(j
, i
);
8631 if (cell
->IsShown())
8633 wxASSERT(colWidths
[i
] > 0);
8635 // Get max specified cell height
8636 // Don't handle percentages for height
8637 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8639 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8640 if (h
> maxSpecifiedCellHeight
)
8641 maxSpecifiedCellHeight
= h
;
8644 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8647 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8648 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8650 wxRect availableCellSpace
;
8652 // TODO: take into acount spans
8655 // Calculate the size of this spanning cell from its constituent columns
8657 int spans
= wxMin(colSpan
, m_colCount
- i
);
8658 for (k
= i
; k
< spans
; k
++)
8664 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8667 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8669 // Store actual width so we can force cell to be the appropriate width on the final loop
8670 actualWidths
[i
] = availableCellSpace
.GetWidth();
8673 cell
->Invalidate(wxRICHTEXT_ALL
);
8674 cell
->Layout(dc
, availableCellSpace
, style
);
8676 // TODO: use GetCachedSize().x to compute 'natural' size
8678 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8679 if (cell
->GetCachedSize().y
> maxCellHeight
)
8680 maxCellHeight
= cell
->GetCachedSize().y
;
8685 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8687 for (i
= 0; i
< m_colCount
; i
++)
8689 wxRichTextCell
* cell
= GetCell(j
, i
);
8690 if (cell
->IsShown())
8692 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8693 // Lay out cell with new height
8694 cell
->Invalidate(wxRICHTEXT_ALL
);
8695 cell
->Layout(dc
, availableCellSpace
, style
);
8697 // Make sure the cell size really is the appropriate size,
8698 // not the calculated box size
8699 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8701 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8706 if (j
< (m_rowCount
-1))
8710 // We need to add back the margins etc.
8712 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8713 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8714 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8715 SetCachedSize(marginRect
.GetSize());
8718 // TODO: calculate max size
8720 SetMaxSize(GetCachedSize());
8723 // TODO: calculate min size
8725 SetMinSize(GetCachedSize());
8728 // TODO: currently we use either a fixed table width or the parent's size.
8729 // We also want to be able to calculate the table width from its content,
8730 // whether using fixed column widths or cell content min/max width.
8731 // Probably need a boolean flag to say whether we need to stretch cells
8732 // to fit the table width, or to simply use min/max cell widths. The
8733 // trouble with this is that if cell widths are not specified, they
8734 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8735 // Anyway, ignoring that problem, we probably need to factor layout into a function
8736 // that can can calculate the maximum unconstrained layout in case table size is
8737 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8738 // constrain Layout(), or the previously-calculated max size to constraint layout.
8743 // Finds the absolute position and row height for the given character position
8744 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8746 wxRichTextCell
* child
= GetCell(index
+1);
8749 // Find the position at the start of the child cell, since the table doesn't
8750 // have any caret position of its own.
8751 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8757 // Get the cell at the given character position (in the range of the table).
8758 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8760 int row
= 0, col
= 0;
8761 if (GetCellRowColumnPosition(pos
, row
, col
))
8763 return GetCell(row
, col
);
8769 // Get the row/column for a given character position
8770 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8772 if (m_colCount
== 0 || m_rowCount
== 0)
8775 row
= (int) (pos
/ m_colCount
);
8776 col
= pos
- (row
* m_colCount
);
8778 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8780 if (row
< m_rowCount
&& col
< m_colCount
)
8786 // Calculate range, taking row/cell ordering into account instead of relying
8787 // on list ordering.
8788 void wxRichTextTable::CalculateRange(long start
, long& end
)
8790 long current
= start
;
8791 long lastEnd
= current
;
8800 for (i
= 0; i
< m_rowCount
; i
++)
8802 for (j
= 0; j
< m_colCount
; j
++)
8804 wxRichTextCell
* child
= GetCell(i
, j
);
8809 child
->CalculateRange(current
, childEnd
);
8812 current
= childEnd
+ 1;
8817 // A top-level object always has a range of size 1,
8818 // because its children don't count at this level.
8820 m_range
.SetRange(start
, start
);
8822 // An object with no children has zero length
8823 if (m_children
.GetCount() == 0)
8825 m_ownRange
.SetRange(0, lastEnd
);
8828 // Gets the range size.
8829 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8831 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
8834 // Deletes content in the given range.
8835 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
8837 // TODO: implement deletion of cells
8841 // Gets any text in this object for the given range.
8842 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
8844 return wxRichTextBox::GetTextForRange(range
);
8847 // Copies this object.
8848 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
8850 wxRichTextBox::Copy(obj
);
8854 m_rowCount
= obj
.m_rowCount
;
8855 m_colCount
= obj
.m_colCount
;
8857 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
8860 for (i
= 0; i
< m_rowCount
; i
++)
8862 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8863 for (j
= 0; j
< m_colCount
; j
++)
8865 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
8873 void wxRichTextTable::ClearTable()
8879 bool wxRichTextTable::CreateTable(int rows
, int cols
)
8886 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
8889 for (i
= 0; i
< rows
; i
++)
8891 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8892 for (j
= 0; j
< cols
; j
++)
8894 wxRichTextCell
* cell
= new wxRichTextCell
;
8896 cell
->AddParagraph(wxEmptyString
);
8905 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
8907 wxASSERT(row
< m_rowCount
);
8908 wxASSERT(col
< m_colCount
);
8910 if (row
< m_rowCount
&& col
< m_colCount
)
8912 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
8913 wxRichTextObject
* obj
= colArray
[col
];
8914 return wxDynamicCast(obj
, wxRichTextCell
);
8920 // Returns a selection object specifying the selections between start and end character positions.
8921 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8922 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
8924 wxRichTextSelection selection
;
8925 selection
.SetContainer((wxRichTextTable
*) this);
8934 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
8936 if (end
>= (m_colCount
* m_rowCount
))
8939 // We need to find the rectangle of cells that is described by the rectangle
8940 // with start, end as the diagonal. Make sure we don't add cells that are
8941 // not currenty visible because they are overlapped by spanning cells.
8943 --------------------------
8944 | 0 | 1 | 2 | 3 | 4 |
8945 --------------------------
8946 | 5 | 6 | 7 | 8 | 9 |
8947 --------------------------
8948 | 10 | 11 | 12 | 13 | 14 |
8949 --------------------------
8950 | 15 | 16 | 17 | 18 | 19 |
8951 --------------------------
8953 Let's say we select 6 -> 18.
8955 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8956 which is left and which is right.
8958 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8960 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8966 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
8967 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
8969 int topRow
= int(start
/m_colCount
);
8970 int bottomRow
= int(end
/m_colCount
);
8972 if (leftCol
> rightCol
)
8979 if (topRow
> bottomRow
)
8981 int tmp
= bottomRow
;
8987 for (i
= topRow
; i
<= bottomRow
; i
++)
8989 for (j
= leftCol
; j
<= rightCol
; j
++)
8991 wxRichTextCell
* cell
= GetCell(i
, j
);
8992 if (cell
&& cell
->IsShown())
8993 selection
.Add(cell
->GetRange());
9000 // Sets the attributes for the cells specified by the selection.
9001 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9003 if (selection
.GetContainer() != this)
9006 wxRichTextBuffer
* buffer
= GetBuffer();
9007 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9008 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9011 buffer
->BeginBatchUndo(_("Set Cell Style"));
9013 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9016 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9017 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9018 SetStyle(cell
, style
, flags
);
9019 node
= node
->GetNext();
9022 // Do action, or delay it until end of batch.
9024 buffer
->EndBatchUndo();
9029 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9031 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9032 if ((startRow
+ noRows
) >= m_rowCount
)
9036 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9038 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9039 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9041 wxRichTextObject
* cell
= colArray
[j
];
9042 RemoveChild(cell
, true);
9045 // Keep deleting at the same position, since we move all
9047 m_cells
.RemoveAt(startRow
);
9050 m_rowCount
= m_rowCount
- noRows
;
9055 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9057 wxASSERT((startCol
+ noCols
) < m_colCount
);
9058 if ((startCol
+ noCols
) >= m_colCount
)
9061 bool deleteRows
= (noCols
== m_colCount
);
9064 for (i
= 0; i
< m_rowCount
; i
++)
9066 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9067 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9069 wxRichTextObject
* cell
= colArray
[j
];
9070 RemoveChild(cell
, true);
9074 m_cells
.RemoveAt(0);
9079 m_colCount
= m_colCount
- noCols
;
9084 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9086 wxASSERT(startRow
<= m_rowCount
);
9087 if (startRow
> m_rowCount
)
9091 for (i
= 0; i
< noRows
; i
++)
9094 if (startRow
== m_rowCount
)
9096 m_cells
.Add(wxRichTextObjectPtrArray());
9097 idx
= m_cells
.GetCount() - 1;
9101 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9105 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9106 for (j
= 0; j
< m_colCount
; j
++)
9108 wxRichTextCell
* cell
= new wxRichTextCell
;
9109 cell
->GetAttributes() = attr
;
9116 m_rowCount
= m_rowCount
+ noRows
;
9120 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9122 wxASSERT(startCol
<= m_colCount
);
9123 if (startCol
> m_colCount
)
9127 for (i
= 0; i
< m_rowCount
; i
++)
9129 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9130 for (j
= 0; j
< noCols
; j
++)
9132 wxRichTextCell
* cell
= new wxRichTextCell
;
9133 cell
->GetAttributes() = attr
;
9137 if (startCol
== m_colCount
)
9140 colArray
.Insert(cell
, startCol
+j
);
9144 m_colCount
= m_colCount
+ noCols
;
9149 // Edit properties via a GUI
9150 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9152 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9153 boxDlg
.SetAttributes(GetAttributes());
9155 if (boxDlg
.ShowModal() == wxID_OK
)
9157 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9165 * Module to initialise and clean up handlers
9168 class wxRichTextModule
: public wxModule
9170 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9172 wxRichTextModule() {}
9175 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9176 wxRichTextBuffer::InitStandardHandlers();
9177 wxRichTextParagraph::InitDefaultTabs();
9182 wxRichTextBuffer::CleanUpHandlers();
9183 wxRichTextDecimalToRoman(-1);
9184 wxRichTextParagraph::ClearDefaultTabs();
9185 wxRichTextCtrl::ClearAvailableFontNames();
9186 wxRichTextBuffer::SetRenderer(NULL
);
9190 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9193 // If the richtext lib is dynamically loaded after the app has already started
9194 // (such as from wxPython) then the built-in module system will not init this
9195 // module. Provide this function to do it manually.
9196 void wxRichTextModuleInit()
9198 wxModule
* module = new wxRichTextModule
;
9200 wxModule::RegisterModule(module);
9205 * Commands for undo/redo
9209 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9210 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9212 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9215 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9219 wxRichTextCommand::~wxRichTextCommand()
9224 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9226 if (!m_actions
.Member(action
))
9227 m_actions
.Append(action
);
9230 bool wxRichTextCommand::Do()
9232 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9234 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9241 bool wxRichTextCommand::Undo()
9243 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9245 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9252 void wxRichTextCommand::ClearActions()
9254 WX_CLEAR_LIST(wxList
, m_actions
);
9262 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9263 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9264 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9268 m_containerAddress
.Create(buffer
, container
);
9269 m_ignoreThis
= ignoreFirstTime
;
9274 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9275 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9277 cmd
->AddAction(this);
9280 wxRichTextAction::~wxRichTextAction()
9286 // Returns the container that this action refers to, using the container address and top-level buffer.
9287 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9289 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9294 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9296 // Store a list of line start character and y positions so we can figure out which area
9297 // we need to refresh
9299 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9300 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9301 wxASSERT(container
!= NULL
);
9305 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9306 // If we had several actions, which only invalidate and leave layout until the
9307 // paint handler is called, then this might not be true. So we may need to switch
9308 // optimisation on only when we're simply adding text and not simultaneously
9309 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9310 // first, but of course this means we'll be doing it twice.
9311 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9313 wxSize clientSize
= m_ctrl
->GetClientSize();
9314 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9315 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9317 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9318 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9321 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9322 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9325 wxRichTextLine
* line
= node2
->GetData();
9326 wxPoint pt
= line
->GetAbsolutePosition();
9327 wxRichTextRange range
= line
->GetAbsoluteRange();
9331 node2
= wxRichTextLineList::compatibility_iterator();
9332 node
= wxRichTextObjectList::compatibility_iterator();
9334 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9336 optimizationLineCharPositions
.Add(range
.GetStart());
9337 optimizationLineYPositions
.Add(pt
.y
);
9341 node2
= node2
->GetNext();
9345 node
= node
->GetNext();
9351 bool wxRichTextAction::Do()
9353 m_buffer
->Modify(true);
9355 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9356 wxASSERT(container
!= NULL
);
9362 case wxRICHTEXT_INSERT
:
9364 // Store a list of line start character and y positions so we can figure out which area
9365 // we need to refresh
9366 wxArrayInt optimizationLineCharPositions
;
9367 wxArrayInt optimizationLineYPositions
;
9369 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9370 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9373 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9374 container
->UpdateRanges();
9376 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9377 // Layout() would stop prematurely at the top level.
9378 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9380 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9382 // Character position to caret position
9383 newCaretPosition
--;
9385 // Don't take into account the last newline
9386 if (m_newParagraphs
.GetPartialParagraph())
9387 newCaretPosition
--;
9389 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9391 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9392 if (p
->GetRange().GetLength() == 1)
9393 newCaretPosition
--;
9396 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9398 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9400 wxRichTextEvent
cmdEvent(
9401 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9402 m_ctrl
? m_ctrl
->GetId() : -1);
9403 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9404 cmdEvent
.SetRange(GetRange());
9405 cmdEvent
.SetPosition(GetRange().GetStart());
9406 cmdEvent
.SetContainer(container
);
9408 m_buffer
->SendEvent(cmdEvent
);
9412 case wxRICHTEXT_DELETE
:
9414 wxArrayInt optimizationLineCharPositions
;
9415 wxArrayInt optimizationLineYPositions
;
9417 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9418 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9421 container
->DeleteRange(GetRange());
9422 container
->UpdateRanges();
9423 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9424 // Layout() would stop prematurely at the top level.
9425 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9427 long caretPos
= GetRange().GetStart()-1;
9428 if (caretPos
>= container
->GetOwnRange().GetEnd())
9431 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9433 wxRichTextEvent
cmdEvent(
9434 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9435 m_ctrl
? m_ctrl
->GetId() : -1);
9436 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9437 cmdEvent
.SetRange(GetRange());
9438 cmdEvent
.SetPosition(GetRange().GetStart());
9439 cmdEvent
.SetContainer(container
);
9441 m_buffer
->SendEvent(cmdEvent
);
9445 case wxRICHTEXT_CHANGE_STYLE
:
9447 ApplyParagraphs(GetNewParagraphs());
9449 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9450 // Layout() would stop prematurely at the top level.
9451 container
->InvalidateHierarchy(GetRange());
9453 UpdateAppearance(GetPosition());
9455 wxRichTextEvent
cmdEvent(
9456 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9457 m_ctrl
? m_ctrl
->GetId() : -1);
9458 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9459 cmdEvent
.SetRange(GetRange());
9460 cmdEvent
.SetPosition(GetRange().GetStart());
9461 cmdEvent
.SetContainer(container
);
9463 m_buffer
->SendEvent(cmdEvent
);
9467 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9469 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9472 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9473 obj
->GetAttributes() = m_attributes
;
9474 m_attributes
= oldAttr
;
9477 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9478 // Layout() would stop prematurely at the top level.
9479 container
->InvalidateHierarchy(GetRange());
9481 UpdateAppearance(GetPosition());
9483 wxRichTextEvent
cmdEvent(
9484 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9485 m_ctrl
? m_ctrl
->GetId() : -1);
9486 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9487 cmdEvent
.SetRange(GetRange());
9488 cmdEvent
.SetPosition(GetRange().GetStart());
9489 cmdEvent
.SetContainer(container
);
9491 m_buffer
->SendEvent(cmdEvent
);
9495 case wxRICHTEXT_CHANGE_OBJECT
:
9497 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9498 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9499 if (obj
&& m_object
)
9501 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9504 wxRichTextObject
* obj
= node
->GetData();
9505 node
->SetData(m_object
);
9510 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9511 // Layout() would stop prematurely at the top level.
9512 container
->InvalidateHierarchy(GetRange());
9514 UpdateAppearance(GetPosition());
9516 // TODO: send new kind of modification event
9527 bool wxRichTextAction::Undo()
9529 m_buffer
->Modify(true);
9531 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9532 wxASSERT(container
!= NULL
);
9538 case wxRICHTEXT_INSERT
:
9540 wxArrayInt optimizationLineCharPositions
;
9541 wxArrayInt optimizationLineYPositions
;
9543 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9544 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9547 container
->DeleteRange(GetRange());
9548 container
->UpdateRanges();
9549 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9550 // Layout() would stop prematurely at the top level.
9551 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9553 long newCaretPosition
= GetPosition() - 1;
9555 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9557 wxRichTextEvent
cmdEvent(
9558 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9559 m_ctrl
? m_ctrl
->GetId() : -1);
9560 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9561 cmdEvent
.SetRange(GetRange());
9562 cmdEvent
.SetPosition(GetRange().GetStart());
9563 cmdEvent
.SetContainer(container
);
9565 m_buffer
->SendEvent(cmdEvent
);
9569 case wxRICHTEXT_DELETE
:
9571 wxArrayInt optimizationLineCharPositions
;
9572 wxArrayInt optimizationLineYPositions
;
9574 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9575 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9578 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9579 container
->UpdateRanges();
9580 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9581 // Layout() would stop prematurely at the top level.
9582 container
->InvalidateHierarchy(GetRange());
9584 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9586 wxRichTextEvent
cmdEvent(
9587 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9588 m_ctrl
? m_ctrl
->GetId() : -1);
9589 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9590 cmdEvent
.SetRange(GetRange());
9591 cmdEvent
.SetPosition(GetRange().GetStart());
9592 cmdEvent
.SetContainer(container
);
9594 m_buffer
->SendEvent(cmdEvent
);
9598 case wxRICHTEXT_CHANGE_STYLE
:
9600 ApplyParagraphs(GetOldParagraphs());
9601 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9602 // Layout() would stop prematurely at the top level.
9603 container
->InvalidateHierarchy(GetRange());
9605 UpdateAppearance(GetPosition());
9607 wxRichTextEvent
cmdEvent(
9608 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9609 m_ctrl
? m_ctrl
->GetId() : -1);
9610 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9611 cmdEvent
.SetRange(GetRange());
9612 cmdEvent
.SetPosition(GetRange().GetStart());
9613 cmdEvent
.SetContainer(container
);
9615 m_buffer
->SendEvent(cmdEvent
);
9619 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9620 case wxRICHTEXT_CHANGE_OBJECT
:
9631 /// Update the control appearance
9632 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9634 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9635 wxASSERT(container
!= NULL
);
9641 m_ctrl
->SetFocusObject(container
);
9642 m_ctrl
->SetCaretPosition(caretPosition
);
9644 if (!m_ctrl
->IsFrozen())
9646 wxRect containerRect
= container
->GetRect();
9648 m_ctrl
->LayoutContent();
9650 // Refresh everything if there were floating objects or the container changed size
9651 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9652 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9654 m_ctrl
->Refresh(false);
9658 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9659 // Find refresh rectangle if we are in a position to optimise refresh
9660 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9664 wxSize clientSize
= m_ctrl
->GetClientSize();
9665 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9667 // Start/end positions
9669 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9671 bool foundEnd
= false;
9673 // position offset - how many characters were inserted
9674 int positionOffset
= GetRange().GetLength();
9676 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9677 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9678 positionOffset
= - positionOffset
;
9680 // find the first line which is being drawn at the same position as it was
9681 // before. Since we're talking about a simple insertion, we can assume
9682 // that the rest of the window does not need to be redrawn.
9684 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9685 // Since we support floating layout, we should redraw the whole para instead of just
9686 // the first line touching the invalid range.
9689 firstY
= para
->GetPosition().y
;
9692 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9695 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9696 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9699 wxRichTextLine
* line
= node2
->GetData();
9700 wxPoint pt
= line
->GetAbsolutePosition();
9701 wxRichTextRange range
= line
->GetAbsoluteRange();
9703 // we want to find the first line that is in the same position
9704 // as before. This will mean we're at the end of the changed text.
9706 if (pt
.y
> lastY
) // going past the end of the window, no more info
9708 node2
= wxRichTextLineList::compatibility_iterator();
9709 node
= wxRichTextObjectList::compatibility_iterator();
9711 // Detect last line in the buffer
9712 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9714 // If deleting text, make sure we refresh below as well as above
9715 if (positionOffset
>= 0)
9718 lastY
= pt
.y
+ line
->GetSize().y
;
9721 node2
= wxRichTextLineList::compatibility_iterator();
9722 node
= wxRichTextObjectList::compatibility_iterator();
9728 // search for this line being at the same position as before
9729 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9731 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9732 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9734 // Stop, we're now the same as we were
9739 node2
= wxRichTextLineList::compatibility_iterator();
9740 node
= wxRichTextObjectList::compatibility_iterator();
9748 node2
= node2
->GetNext();
9752 node
= node
->GetNext();
9755 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9757 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9759 // Convert to device coordinates
9760 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9761 m_ctrl
->RefreshRect(rect
);
9765 m_ctrl
->Refresh(false);
9767 m_ctrl
->PositionCaret();
9769 // This causes styles to persist when doing programmatic
9770 // content creation except when Freeze/Thaw is used, so
9771 // disable this and check for the consequences.
9772 // m_ctrl->SetDefaultStyleToCursorStyle();
9774 if (sendUpdateEvent
)
9775 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9780 /// Replace the buffer paragraphs with the new ones.
9781 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9783 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9784 wxASSERT(container
!= NULL
);
9788 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9791 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9792 wxASSERT (para
!= NULL
);
9794 // We'll replace the existing paragraph by finding the paragraph at this position,
9795 // delete its node data, and setting a copy as the new node data.
9796 // TODO: make more efficient by simply swapping old and new paragraph objects.
9798 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9801 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9804 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9805 newPara
->SetParent(container
);
9807 bufferParaNode
->SetData(newPara
);
9809 delete existingPara
;
9813 node
= node
->GetNext();
9820 * This stores beginning and end positions for a range of data.
9823 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
9825 /// Limit this range to be within 'range'
9826 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
9828 if (m_start
< range
.m_start
)
9829 m_start
= range
.m_start
;
9831 if (m_end
> range
.m_end
)
9832 m_end
= range
.m_end
;
9838 * wxRichTextImage implementation
9839 * This object represents an image.
9842 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
9844 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9845 wxRichTextObject(parent
)
9847 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
9849 SetAttributes(*charStyle
);
9852 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9853 wxRichTextObject(parent
)
9855 m_imageBlock
= imageBlock
;
9857 SetAttributes(*charStyle
);
9860 /// Create a cached image at the required size
9861 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
9863 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9865 if (!m_imageBlock
.IsOk())
9869 m_imageBlock
.Load(image
);
9873 int width
= image
.GetWidth();
9874 int height
= image
.GetHeight();
9876 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9878 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9879 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9881 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9883 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9885 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9886 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9888 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9891 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
9892 m_imageCache
= wxBitmap(image
);
9895 // If the original width and height is small, e.g. 400 or below,
9896 // scale up and then down to improve image quality. This can make
9897 // a big difference, with not much performance hit.
9898 int upscaleThreshold
= 400;
9900 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
9902 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
9903 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9906 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9907 m_imageCache
= wxBitmap(img
);
9911 return m_imageCache
.IsOk();
9915 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
9920 // Don't need cached size AFAIK
9921 // wxSize size = GetCachedSize();
9922 if (!LoadImageCache(dc
))
9925 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9928 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
9930 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
9933 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9934 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9935 marginRect
= rect
; // outer rectangle, will calculate contentRect
9936 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9938 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
9940 if (selection
.WithinSelection(range
.GetStart(), this))
9942 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9943 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9944 dc
.SetLogicalFunction(wxINVERT
);
9945 dc
.DrawRectangle(contentRect
);
9946 dc
.SetLogicalFunction(wxCOPY
);
9952 /// Lay the item out
9953 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(style
))
9955 if (!LoadImageCache(dc
))
9958 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9959 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9960 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9961 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9963 wxSize overallSize
= marginRect
.GetSize();
9965 SetCachedSize(overallSize
);
9966 SetMaxSize(overallSize
);
9967 SetMinSize(overallSize
);
9968 SetPosition(rect
.GetPosition());
9973 /// Get/set the object size for the given range. Returns false if the range
9974 /// is invalid for this object.
9975 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
9977 if (!range
.IsWithin(GetRange()))
9980 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
9982 size
.x
= 0; size
.y
= 0;
9984 partialExtents
->Add(0);
9988 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9989 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9990 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9991 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9993 wxSize overallSize
= marginRect
.GetSize();
9996 partialExtents
->Add(overallSize
.x
);
10003 // Get the 'natural' size for an object. For an image, it would be the
10005 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10007 wxTextAttrSize size
;
10008 if (GetImageCache().IsOk())
10010 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10011 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10018 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10020 wxRichTextObject::Copy(obj
);
10022 m_imageBlock
= obj
.m_imageBlock
;
10025 /// Edit properties via a GUI
10026 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10028 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10029 imageDlg
.SetAttributes(GetAttributes());
10031 if (imageDlg
.ShowModal() == wxID_OK
)
10033 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10034 // indeterminate in the object.
10035 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10047 /// Compare two attribute objects
10048 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10050 return (attr1
== attr2
);
10053 // Partial equality test taking flags into account
10054 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10056 return attr1
.EqPartial(attr2
);
10060 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10062 if (tabs1
.GetCount() != tabs2
.GetCount())
10066 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10068 if (tabs1
[i
] != tabs2
[i
])
10074 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10076 return destStyle
.Apply(style
, compareWith
);
10079 // Remove attributes
10080 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10082 return destStyle
.RemoveStyle(style
);
10085 /// Combine two bitlists, specifying the bits of interest with separate flags.
10086 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10088 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10091 /// Compare two bitlists
10092 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10094 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10097 /// Split into paragraph and character styles
10098 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10100 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10103 /// Convert a decimal to Roman numerals
10104 wxString
wxRichTextDecimalToRoman(long n
)
10106 static wxArrayInt decimalNumbers
;
10107 static wxArrayString romanNumbers
;
10112 decimalNumbers
.Clear();
10113 romanNumbers
.Clear();
10114 return wxEmptyString
;
10117 if (decimalNumbers
.GetCount() == 0)
10119 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10121 wxRichTextAddDecRom(1000, wxT("M"));
10122 wxRichTextAddDecRom(900, wxT("CM"));
10123 wxRichTextAddDecRom(500, wxT("D"));
10124 wxRichTextAddDecRom(400, wxT("CD"));
10125 wxRichTextAddDecRom(100, wxT("C"));
10126 wxRichTextAddDecRom(90, wxT("XC"));
10127 wxRichTextAddDecRom(50, wxT("L"));
10128 wxRichTextAddDecRom(40, wxT("XL"));
10129 wxRichTextAddDecRom(10, wxT("X"));
10130 wxRichTextAddDecRom(9, wxT("IX"));
10131 wxRichTextAddDecRom(5, wxT("V"));
10132 wxRichTextAddDecRom(4, wxT("IV"));
10133 wxRichTextAddDecRom(1, wxT("I"));
10139 while (n
> 0 && i
< 13)
10141 if (n
>= decimalNumbers
[i
])
10143 n
-= decimalNumbers
[i
];
10144 roman
+= romanNumbers
[i
];
10151 if (roman
.IsEmpty())
10157 * wxRichTextFileHandler
10158 * Base class for file handlers
10161 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10163 #if wxUSE_FFILE && wxUSE_STREAMS
10164 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10166 wxFFileInputStream
stream(filename
);
10168 return LoadFile(buffer
, stream
);
10173 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10175 wxFFileOutputStream
stream(filename
);
10177 return SaveFile(buffer
, stream
);
10181 #endif // wxUSE_FFILE && wxUSE_STREAMS
10183 /// Can we handle this filename (if using files)? By default, checks the extension.
10184 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10186 wxString path
, file
, ext
;
10187 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10189 return (ext
.Lower() == GetExtension());
10193 * wxRichTextTextHandler
10194 * Plain text handler
10197 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10200 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10202 if (!stream
.IsOk())
10208 while (!stream
.Eof())
10210 int ch
= stream
.GetC();
10214 if (ch
== 10 && lastCh
!= 13)
10217 if (ch
> 0 && ch
!= 10)
10224 buffer
->ResetAndClearCommands();
10226 buffer
->AddParagraphs(str
);
10227 buffer
->UpdateRanges();
10232 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10234 if (!stream
.IsOk())
10237 wxString text
= buffer
->GetText();
10239 wxString newLine
= wxRichTextLineBreakChar
;
10240 text
.Replace(newLine
, wxT("\n"));
10242 wxCharBuffer buf
= text
.ToAscii();
10244 stream
.Write((const char*) buf
, text
.length());
10247 #endif // wxUSE_STREAMS
10250 * Stores information about an image, in binary in-memory form
10253 wxRichTextImageBlock::wxRichTextImageBlock()
10258 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10264 wxRichTextImageBlock::~wxRichTextImageBlock()
10269 void wxRichTextImageBlock::Init()
10273 m_imageType
= wxBITMAP_TYPE_INVALID
;
10276 void wxRichTextImageBlock::Clear()
10280 m_imageType
= wxBITMAP_TYPE_INVALID
;
10284 // Load the original image into a memory block.
10285 // If the image is not a JPEG, we must convert it into a JPEG
10286 // to conserve space.
10287 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10288 // load the image a 2nd time.
10290 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10291 wxImage
& image
, bool convertToJPEG
)
10293 m_imageType
= imageType
;
10295 wxString
filenameToRead(filename
);
10296 bool removeFile
= false;
10298 if (imageType
== wxBITMAP_TYPE_INVALID
)
10299 return false; // Could not determine image type
10301 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10303 wxString tempFile
=
10304 wxFileName::CreateTempFileName(_("image"));
10306 wxASSERT(!tempFile
.IsEmpty());
10308 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10309 filenameToRead
= tempFile
;
10312 m_imageType
= wxBITMAP_TYPE_JPEG
;
10315 if (!file
.Open(filenameToRead
))
10318 m_dataSize
= (size_t) file
.Length();
10323 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10326 wxRemoveFile(filenameToRead
);
10328 return (m_data
!= NULL
);
10331 // Make an image block from the wxImage in the given
10333 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10335 image
.SetOption(wxT("quality"), quality
);
10337 if (imageType
== wxBITMAP_TYPE_INVALID
)
10338 return false; // Could not determine image type
10340 return DoMakeImageBlock(image
, imageType
);
10343 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10344 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10346 if (imageType
== wxBITMAP_TYPE_INVALID
)
10347 return false; // Could not determine image type
10349 return DoMakeImageBlock(image
, imageType
);
10352 // Makes the image block
10353 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10355 wxMemoryOutputStream memStream
;
10356 if (!image
.SaveFile(memStream
, imageType
))
10361 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10369 m_imageType
= imageType
;
10370 m_dataSize
= memStream
.GetSize();
10372 memStream
.CopyTo(m_data
, m_dataSize
);
10374 return (m_data
!= NULL
);
10378 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10380 return WriteBlock(filename
, m_data
, m_dataSize
);
10383 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10385 m_imageType
= block
.m_imageType
;
10387 m_dataSize
= block
.m_dataSize
;
10388 if (m_dataSize
== 0)
10391 m_data
= new unsigned char[m_dataSize
];
10393 for (i
= 0; i
< m_dataSize
; i
++)
10394 m_data
[i
] = block
.m_data
[i
];
10398 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10403 // Load a wxImage from the block
10404 bool wxRichTextImageBlock::Load(wxImage
& image
)
10409 // Read in the image.
10411 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10412 bool success
= image
.LoadFile(mstream
, GetImageType());
10414 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10415 wxASSERT(!tempFile
.IsEmpty());
10417 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10421 success
= image
.LoadFile(tempFile
, GetImageType());
10422 wxRemoveFile(tempFile
);
10428 // Write data in hex to a stream
10429 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10431 const int bufSize
= 512;
10432 char buf
[bufSize
+1];
10434 int left
= m_dataSize
;
10439 if (left
*2 > bufSize
)
10441 n
= bufSize
; left
-= (bufSize
/2);
10445 n
= left
*2; left
= 0;
10449 for (i
= 0; i
< (n
/2); i
++)
10451 wxDecToHex(m_data
[j
], b
, b
+1);
10456 stream
.Write((const char*) buf
, n
);
10461 // Read data in hex from a stream
10462 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10464 int dataSize
= length
/2;
10469 // create a null terminated temporary string:
10473 m_data
= new unsigned char[dataSize
];
10475 for (i
= 0; i
< dataSize
; i
++)
10477 str
[0] = (char)stream
.GetC();
10478 str
[1] = (char)stream
.GetC();
10480 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10483 m_dataSize
= dataSize
;
10484 m_imageType
= imageType
;
10489 // Allocate and read from stream as a block of memory
10490 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10492 unsigned char* block
= new unsigned char[size
];
10496 stream
.Read(block
, size
);
10501 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10503 wxFileInputStream
stream(filename
);
10507 return ReadBlock(stream
, size
);
10510 // Write memory block to stream
10511 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10513 stream
.Write((void*) block
, size
);
10514 return stream
.IsOk();
10518 // Write memory block to file
10519 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10521 wxFileOutputStream
outStream(filename
);
10522 if (!outStream
.Ok())
10525 return WriteBlock(outStream
, block
, size
);
10528 // Gets the extension for the block's type
10529 wxString
wxRichTextImageBlock::GetExtension() const
10531 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10533 return handler
->GetExtension();
10535 return wxEmptyString
;
10541 * The data object for a wxRichTextBuffer
10544 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10546 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10548 m_richTextBuffer
= richTextBuffer
;
10550 // this string should uniquely identify our format, but is otherwise
10552 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10554 SetFormat(m_formatRichTextBuffer
);
10557 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10559 delete m_richTextBuffer
;
10562 // after a call to this function, the richTextBuffer is owned by the caller and it
10563 // is responsible for deleting it!
10564 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10566 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10567 m_richTextBuffer
= NULL
;
10569 return richTextBuffer
;
10572 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10574 return m_formatRichTextBuffer
;
10577 size_t wxRichTextBufferDataObject::GetDataSize() const
10579 if (!m_richTextBuffer
)
10585 wxStringOutputStream
stream(& bufXML
);
10586 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10588 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10594 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10595 return strlen(buffer
) + 1;
10597 return bufXML
.Length()+1;
10601 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10603 if (!pBuf
|| !m_richTextBuffer
)
10609 wxStringOutputStream
stream(& bufXML
);
10610 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10612 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10618 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10619 size_t len
= strlen(buffer
);
10620 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10621 ((char*) pBuf
)[len
] = 0;
10623 size_t len
= bufXML
.Length();
10624 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10625 ((char*) pBuf
)[len
] = 0;
10631 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10633 wxDELETE(m_richTextBuffer
);
10635 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10637 m_richTextBuffer
= new wxRichTextBuffer
;
10639 wxStringInputStream
stream(bufXML
);
10640 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10642 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10644 wxDELETE(m_richTextBuffer
);
10656 * wxRichTextFontTable
10657 * Manages quick access to a pool of fonts for rendering rich text
10660 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10662 class wxRichTextFontTableData
: public wxObjectRefData
10665 wxRichTextFontTableData() {}
10667 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10669 wxRichTextFontTableHashMap m_hashMap
;
10672 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10674 wxString
facename(fontSpec
.GetFontFaceName());
10675 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()));
10676 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10678 if ( entry
== m_hashMap
.end() )
10680 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10681 m_hashMap
[spec
] = font
;
10686 return entry
->second
;
10690 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10692 wxRichTextFontTable::wxRichTextFontTable()
10694 m_refData
= new wxRichTextFontTableData
;
10697 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10703 wxRichTextFontTable::~wxRichTextFontTable()
10708 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10710 return (m_refData
== table
.m_refData
);
10713 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10718 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10720 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10722 return data
->FindFont(fontSpec
);
10727 void wxRichTextFontTable::Clear()
10729 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10731 data
->m_hashMap
.clear();
10737 void wxTextBoxAttr::Reset()
10740 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10741 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10742 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10743 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10747 m_position
.Reset();
10756 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10759 m_flags
== attr
.m_flags
&&
10760 m_floatMode
== attr
.m_floatMode
&&
10761 m_clearMode
== attr
.m_clearMode
&&
10762 m_collapseMode
== attr
.m_collapseMode
&&
10763 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10765 m_margins
== attr
.m_margins
&&
10766 m_padding
== attr
.m_padding
&&
10767 m_position
== attr
.m_position
&&
10769 m_size
== attr
.m_size
&&
10771 m_border
== attr
.m_border
&&
10772 m_outline
== attr
.m_outline
10776 // Partial equality test
10777 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10779 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10782 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10785 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10788 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10793 if (!m_position
.EqPartial(attr
.m_position
))
10798 if (!m_margins
.EqPartial(attr
.m_margins
))
10803 if (!m_padding
.EqPartial(attr
.m_padding
))
10808 if (!GetBorder().EqPartial(attr
.GetBorder()))
10813 if (!GetOutline().EqPartial(attr
.GetOutline()))
10819 // Merges the given attributes. If compareWith
10820 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10821 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10822 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
10824 if (attr
.HasFloatMode())
10826 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
10827 SetFloatMode(attr
.GetFloatMode());
10830 if (attr
.HasClearMode())
10832 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
10833 SetClearMode(attr
.GetClearMode());
10836 if (attr
.HasCollapseBorders())
10838 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
10839 SetCollapseBorders(attr
.GetCollapseBorders());
10842 if (attr
.HasVerticalAlignment())
10844 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
10845 SetVerticalAlignment(attr
.GetVerticalAlignment());
10848 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
10849 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
10850 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
10852 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
10854 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
10855 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
10860 // Remove specified attributes from this object
10861 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
10863 if (attr
.HasFloatMode())
10864 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10866 if (attr
.HasClearMode())
10867 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10869 if (attr
.HasCollapseBorders())
10870 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10872 if (attr
.HasVerticalAlignment())
10873 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10875 m_margins
.RemoveStyle(attr
.m_margins
);
10876 m_padding
.RemoveStyle(attr
.m_padding
);
10877 m_position
.RemoveStyle(attr
.m_position
);
10879 m_size
.RemoveStyle(attr
.m_size
);
10881 m_border
.RemoveStyle(attr
.m_border
);
10882 m_outline
.RemoveStyle(attr
.m_outline
);
10887 // Collects the attributes that are common to a range of content, building up a note of
10888 // which attributes are absent in some objects and which clash in some objects.
10889 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
10891 if (attr
.HasFloatMode())
10893 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
10895 if (HasFloatMode())
10897 if (GetFloatMode() != attr
.GetFloatMode())
10899 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10900 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10904 SetFloatMode(attr
.GetFloatMode());
10908 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10910 if (attr
.HasClearMode())
10912 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
10914 if (HasClearMode())
10916 if (GetClearMode() != attr
.GetClearMode())
10918 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10919 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10923 SetClearMode(attr
.GetClearMode());
10927 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10929 if (attr
.HasCollapseBorders())
10931 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
10933 if (HasCollapseBorders())
10935 if (GetCollapseBorders() != attr
.GetCollapseBorders())
10937 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10938 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10942 SetCollapseBorders(attr
.GetCollapseBorders());
10946 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10948 if (attr
.HasVerticalAlignment())
10950 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
10952 if (HasVerticalAlignment())
10954 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
10956 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10957 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10961 SetVerticalAlignment(attr
.GetVerticalAlignment());
10965 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10967 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
10968 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
10969 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
10971 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
10973 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
10974 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
10979 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
10981 wxTextAttr::Copy(attr
);
10983 m_textBoxAttr
= attr
.m_textBoxAttr
;
10986 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
10988 if (!(wxTextAttr::operator==(attr
)))
10991 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
10994 // Partial equality test taking comparison object into account
10995 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
10997 if (!(wxTextAttr::EqPartial(attr
)))
11000 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11003 // Merges the given attributes. If compareWith
11004 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11005 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11006 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11008 wxTextAttr::Apply(style
, compareWith
);
11010 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11013 // Remove specified attributes from this object
11014 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11016 wxTextAttr::RemoveStyle(*this, attr
);
11018 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11021 // Collects the attributes that are common to a range of content, building up a note of
11022 // which attributes are absent in some objects and which clash in some objects.
11023 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11025 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11027 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11030 // Partial equality test
11031 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11033 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11036 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11039 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11045 // Apply border to 'this', but not if the same as compareWith
11046 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11048 if (border
.HasStyle())
11050 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11051 SetStyle(border
.GetStyle());
11053 if (border
.HasColour())
11055 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11056 SetColour(border
.GetColourLong());
11058 if (border
.HasWidth())
11060 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11061 SetWidth(border
.GetWidth());
11067 // Remove specified attributes from this object
11068 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11070 if (attr
.HasStyle() && HasStyle())
11071 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11072 if (attr
.HasColour() && HasColour())
11073 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11074 if (attr
.HasWidth() && HasWidth())
11075 m_borderWidth
.Reset();
11080 // Collects the attributes that are common to a range of content, building up a note of
11081 // which attributes are absent in some objects and which clash in some objects.
11082 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11084 if (attr
.HasStyle())
11086 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11090 if (GetStyle() != attr
.GetStyle())
11092 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11093 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11097 SetStyle(attr
.GetStyle());
11101 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11103 if (attr
.HasColour())
11105 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11109 if (GetColour() != attr
.GetColour())
11111 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11112 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11116 SetColour(attr
.GetColourLong());
11120 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11122 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11125 // Partial equality test
11126 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11128 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11129 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11132 // Apply border to 'this', but not if the same as compareWith
11133 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11135 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11136 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11137 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11138 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11142 // Remove specified attributes from this object
11143 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11145 m_left
.RemoveStyle(attr
.m_left
);
11146 m_right
.RemoveStyle(attr
.m_right
);
11147 m_top
.RemoveStyle(attr
.m_top
);
11148 m_bottom
.RemoveStyle(attr
.m_bottom
);
11152 // Collects the attributes that are common to a range of content, building up a note of
11153 // which attributes are absent in some objects and which clash in some objects.
11154 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11156 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11157 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11158 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11159 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11162 // Set style of all borders
11163 void wxTextAttrBorders::SetStyle(int style
)
11165 m_left
.SetStyle(style
);
11166 m_right
.SetStyle(style
);
11167 m_top
.SetStyle(style
);
11168 m_bottom
.SetStyle(style
);
11171 // Set colour of all borders
11172 void wxTextAttrBorders::SetColour(unsigned long colour
)
11174 m_left
.SetColour(colour
);
11175 m_right
.SetColour(colour
);
11176 m_top
.SetColour(colour
);
11177 m_bottom
.SetColour(colour
);
11180 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11182 m_left
.SetColour(colour
);
11183 m_right
.SetColour(colour
);
11184 m_top
.SetColour(colour
);
11185 m_bottom
.SetColour(colour
);
11188 // Set width of all borders
11189 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11191 m_left
.SetWidth(width
);
11192 m_right
.SetWidth(width
);
11193 m_top
.SetWidth(width
);
11194 m_bottom
.SetWidth(width
);
11197 // Partial equality test
11198 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11200 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11206 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11210 if (!(compareWith
&& dim
== (*compareWith
)))
11217 // Collects the attributes that are common to a range of content, building up a note of
11218 // which attributes are absent in some objects and which clash in some objects.
11219 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11221 if (attr
.IsValid())
11223 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11227 if (!((*this) == attr
))
11229 clashingAttr
.SetValid(true);
11238 absentAttr
.SetValid(true);
11241 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11243 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11246 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11248 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11251 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11253 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11256 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11258 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11261 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11263 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11264 return ConvertTenthsMMToPixels(dim
.GetValue());
11265 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11266 return dim
.GetValue();
11267 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11269 wxASSERT(m_parentSize
!= wxDefaultSize
);
11270 if (direction
== wxHORIZONTAL
)
11271 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11273 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11282 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11284 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11285 return dim
.GetValue();
11286 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11287 return ConvertPixelsToTenthsMM(dim
.GetValue());
11295 // Partial equality test
11296 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11298 if (!m_left
.EqPartial(dims
.m_left
))
11301 if (!m_right
.EqPartial(dims
.m_right
))
11304 if (!m_top
.EqPartial(dims
.m_top
))
11307 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11313 // Apply border to 'this', but not if the same as compareWith
11314 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11316 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11317 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11318 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11319 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11324 // Remove specified attributes from this object
11325 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11327 if (attr
.m_left
.IsValid())
11329 if (attr
.m_right
.IsValid())
11331 if (attr
.m_top
.IsValid())
11333 if (attr
.m_bottom
.IsValid())
11339 // Collects the attributes that are common to a range of content, building up a note of
11340 // which attributes are absent in some objects and which clash in some objects.
11341 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11343 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11344 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11345 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11346 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11349 // Partial equality test
11350 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11352 if (!m_width
.EqPartial(size
.m_width
))
11355 if (!m_height
.EqPartial(size
.m_height
))
11361 // Apply border to 'this', but not if the same as compareWith
11362 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11364 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11365 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11370 // Remove specified attributes from this object
11371 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11373 if (attr
.m_width
.IsValid())
11375 if (attr
.m_height
.IsValid())
11381 // Collects the attributes that are common to a range of content, building up a note of
11382 // which attributes are absent in some objects and which clash in some objects.
11383 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11385 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11386 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11389 // Collects the attributes that are common to a range of content, building up a note of
11390 // which attributes are absent in some objects and which clash in some objects.
11391 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11393 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11394 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11396 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11398 if (attr
.HasFont())
11400 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11402 if (currentStyle
.HasFontSize())
11404 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11406 // Clash of attr - mark as such
11407 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11408 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11412 currentStyle
.SetFontSize(attr
.GetFontSize());
11415 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11417 if (currentStyle
.HasFontItalic())
11419 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11421 // Clash of attr - mark as such
11422 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11423 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11427 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11430 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11432 if (currentStyle
.HasFontFamily())
11434 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11436 // Clash of attr - mark as such
11437 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11438 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11442 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11445 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11447 if (currentStyle
.HasFontWeight())
11449 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11451 // Clash of attr - mark as such
11452 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11453 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11457 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11460 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11462 if (currentStyle
.HasFontFaceName())
11464 wxString
faceName1(currentStyle
.GetFontFaceName());
11465 wxString
faceName2(attr
.GetFontFaceName());
11467 if (faceName1
!= faceName2
)
11469 // Clash of attr - mark as such
11470 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11471 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11475 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11478 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11480 if (currentStyle
.HasFontUnderlined())
11482 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11484 // Clash of attr - mark as such
11485 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11486 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11490 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11494 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11496 if (currentStyle
.HasTextColour())
11498 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11500 // Clash of attr - mark as such
11501 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11502 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11506 currentStyle
.SetTextColour(attr
.GetTextColour());
11509 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11511 if (currentStyle
.HasBackgroundColour())
11513 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11515 // Clash of attr - mark as such
11516 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11517 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11521 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11524 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11526 if (currentStyle
.HasAlignment())
11528 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11530 // Clash of attr - mark as such
11531 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11532 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11536 currentStyle
.SetAlignment(attr
.GetAlignment());
11539 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11541 if (currentStyle
.HasTabs())
11543 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11545 // Clash of attr - mark as such
11546 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11547 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11551 currentStyle
.SetTabs(attr
.GetTabs());
11554 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11556 if (currentStyle
.HasLeftIndent())
11558 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11560 // Clash of attr - mark as such
11561 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11562 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11566 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11569 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11571 if (currentStyle
.HasRightIndent())
11573 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11575 // Clash of attr - mark as such
11576 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11577 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11581 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11584 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11586 if (currentStyle
.HasParagraphSpacingAfter())
11588 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11590 // Clash of attr - mark as such
11591 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11592 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11596 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11599 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11601 if (currentStyle
.HasParagraphSpacingBefore())
11603 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11605 // Clash of attr - mark as such
11606 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11607 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11611 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11614 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11616 if (currentStyle
.HasLineSpacing())
11618 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11620 // Clash of attr - mark as such
11621 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11622 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11626 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11629 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11631 if (currentStyle
.HasCharacterStyleName())
11633 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11635 // Clash of attr - mark as such
11636 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11637 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11641 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11644 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11646 if (currentStyle
.HasParagraphStyleName())
11648 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11650 // Clash of attr - mark as such
11651 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11652 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11656 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11659 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11661 if (currentStyle
.HasListStyleName())
11663 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11665 // Clash of attr - mark as such
11666 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11667 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11671 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11674 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11676 if (currentStyle
.HasBulletStyle())
11678 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11680 // Clash of attr - mark as such
11681 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11682 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11686 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11689 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11691 if (currentStyle
.HasBulletNumber())
11693 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11695 // Clash of attr - mark as such
11696 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11697 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11701 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11704 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11706 if (currentStyle
.HasBulletText())
11708 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11710 // Clash of attr - mark as such
11711 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11712 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11717 currentStyle
.SetBulletText(attr
.GetBulletText());
11718 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11722 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11724 if (currentStyle
.HasBulletName())
11726 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11728 // Clash of attr - mark as such
11729 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11730 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11735 currentStyle
.SetBulletName(attr
.GetBulletName());
11739 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11741 if (currentStyle
.HasURL())
11743 if (currentStyle
.GetURL() != attr
.GetURL())
11745 // Clash of attr - mark as such
11746 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11747 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
11752 currentStyle
.SetURL(attr
.GetURL());
11756 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
11758 if (currentStyle
.HasTextEffects())
11760 // We need to find the bits in the new attr that are different:
11761 // just look at those bits that are specified by the new attr.
11763 // We need to remove the bits and flags that are not common between current attr
11764 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11765 // previous styles.
11767 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
11768 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
11770 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
11772 // Find the text effects that were different, using XOR
11773 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
11775 // Clash of attr - mark as such
11776 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
11777 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
11782 currentStyle
.SetTextEffects(attr
.GetTextEffects());
11783 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
11786 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11787 // that we've looked at so far
11788 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
11789 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
11791 if (currentStyle
.GetTextEffectFlags() == 0)
11792 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
11795 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
11797 if (currentStyle
.HasOutlineLevel())
11799 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
11801 // Clash of attr - mark as such
11802 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11803 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11807 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
11811 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
11813 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
11815 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
11817 if (m_properties
.GetCount() != props
.GetCount())
11821 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11823 const wxVariant
& var1
= m_properties
[i
];
11824 int idx
= props
.Find(var1
.GetName());
11827 const wxVariant
& var2
= props
.m_properties
[idx
];
11828 if (!(var1
== var2
))
11835 wxArrayString
wxRichTextProperties::GetPropertyNames() const
11839 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11841 arr
.Add(m_properties
[i
].GetName());
11846 int wxRichTextProperties::Find(const wxString
& name
) const
11849 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11851 if (m_properties
[i
].GetName() == name
)
11857 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
11859 int idx
= Find(name
);
11860 if (idx
== wxNOT_FOUND
)
11861 SetProperty(name
, wxString());
11863 if (idx
!= wxNOT_FOUND
)
11865 return & (*this)[idx
];
11871 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
11873 static const wxVariant nullVariant
;
11874 int idx
= Find(name
);
11876 return m_properties
[idx
];
11878 return nullVariant
;
11881 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
11883 return GetProperty(name
).GetString();
11886 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
11888 return GetProperty(name
).GetLong();
11891 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
11893 return GetProperty(name
).GetBool();
11896 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
11898 return GetProperty(name
).GetDouble();
11901 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
11903 wxASSERT(!variant
.GetName().IsEmpty());
11905 int idx
= Find(variant
.GetName());
11908 m_properties
.Add(variant
);
11910 m_properties
[idx
] = variant
;
11913 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
11915 int idx
= Find(name
);
11916 wxVariant
var(variant
);
11920 m_properties
.Add(var
);
11922 m_properties
[idx
] = var
;
11925 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
11927 SetProperty(name
, wxVariant(value
, name
));
11930 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
11932 SetProperty(name
, wxVariant(value
, name
));
11935 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
11937 SetProperty(name
, wxVariant(value
, name
));
11940 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
11942 SetProperty(name
, wxVariant(value
, name
));
11945 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
11947 if (m_address
.GetCount() == 0)
11948 return topLevelContainer
;
11950 wxRichTextCompositeObject
* p
= topLevelContainer
;
11952 while (p
&& i
< m_address
.GetCount())
11954 int pos
= m_address
[i
];
11955 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
11956 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
11959 wxRichTextObject
* p1
= p
->GetChild(pos
);
11960 if (i
== (m_address
.GetCount()-1))
11963 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
11969 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
11973 if (topLevelContainer
== obj
)
11976 wxRichTextObject
* o
= obj
;
11979 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
11983 int pos
= p
->GetChildren().IndexOf(o
);
11987 m_address
.Insert(pos
, 0);
11989 if (p
== topLevelContainer
)
11998 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12000 if (m_container
!= sel
.m_container
)
12002 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12005 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12006 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12011 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12012 // or none at the level of the object's container.
12013 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12017 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12019 if (container
== m_container
)
12022 container
= obj
->GetContainer();
12025 if (container
->GetParent())
12027 // If we found that our object's container is within the range of
12028 // a selection higher up, then assume the whole original object
12029 // is also selected.
12030 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12031 if (parentContainer
== m_container
)
12033 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12035 wxRichTextRangeArray ranges
;
12036 ranges
.Add(obj
->GetRange());
12041 container
= parentContainer
;
12050 return wxRichTextRangeArray();
12053 // Is the given position within the selection?
12054 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12060 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12061 return WithinSelection(pos
, selectionRanges
);
12065 // Is the given position within the selection range?
12066 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12069 for (i
= 0; i
< ranges
.GetCount(); i
++)
12071 const wxRichTextRange
& range
= ranges
[i
];
12072 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12078 // Is the given range completely within the selection range?
12079 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12082 for (i
= 0; i
< ranges
.GetCount(); i
++)
12084 const wxRichTextRange
& eachRange
= ranges
[i
];
12085 if (range
.IsWithin(eachRange
))