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 // Unsure if this is needed
1923 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1925 if (child
&& child
->IsShown())
1927 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1928 if ( !forceQuickLayout
&&
1930 child
->GetLines().IsEmpty() ||
1931 !child
->GetRange().IsOutside(invalidRange
)) )
1933 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1934 // lays out the object again using the minimum size
1935 child
->LayoutToBestSize(dc
, GetBuffer(),
1936 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1938 // Layout must set the cached size
1939 availableSpace
.y
+= child
->GetCachedSize().y
;
1940 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1941 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1942 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1944 // If we're just formatting the visible part of the buffer,
1945 // and we're now past the bottom of the window, and we don't have any
1946 // floating objects (since they may cause wrapping to change for the rest of the
1947 // the buffer), start quick layout.
1948 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1949 forceQuickLayout
= true;
1953 // We're outside the immediately affected range, so now let's just
1954 // move everything up or down. This assumes that all the children have previously
1955 // been laid out and have wrapped line lists associated with them.
1956 // TODO: check all paragraphs before the affected range.
1958 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1962 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1965 if (child
->GetLines().GetCount() == 0)
1967 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1968 // lays out the object again using the minimum size
1969 child
->LayoutToBestSize(dc
, GetBuffer(),
1970 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1972 //child->Layout(dc, availableChildRect, style);
1975 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1977 availableSpace
.y
+= child
->GetCachedSize().y
;
1978 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1979 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1980 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1983 node
= node
->GetNext();
1989 node
= node
->GetNext();
1992 node
= m_children
.GetLast();
1993 if (node
&& node
->GetData()->IsShown())
1995 wxRichTextObject
* child
= node
->GetData();
1996 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
1997 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2000 maxHeight
= 0; // topMargin + bottomMargin;
2002 // TODO: (also in para layout) should set the
2003 // object's size to an absolute one if specified,
2004 // but if not specified, calculate it from content.
2006 // We need to add back the margins etc.
2008 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2009 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2010 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2011 SetCachedSize(marginRect
.GetSize());
2014 // The maximum size is the greatest of all maximum widths for all paragraphs.
2016 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2017 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2018 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2019 SetMaxSize(marginRect
.GetSize());
2022 // The minimum size is the greatest of all minimum widths for all paragraphs.
2024 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2025 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2026 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2027 SetMinSize(marginRect
.GetSize());
2030 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2031 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2034 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2035 if (leftOverSpace
> 0)
2037 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2039 yOffset
= (leftOverSpace
/2);
2041 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2043 yOffset
= leftOverSpace
;
2047 // Move all the children to vertically align the content
2048 // This doesn't take into account floating objects, unfortunately.
2051 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2054 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2056 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2058 node
= node
->GetNext();
2063 m_invalidRange
= wxRICHTEXT_NONE
;
2068 /// Get/set the size for the given range.
2069 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2073 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2074 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2076 // First find the first paragraph whose starting position is within the range.
2077 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2080 // child is a paragraph
2081 wxRichTextObject
* child
= node
->GetData();
2082 const wxRichTextRange
& r
= child
->GetRange();
2084 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2090 node
= node
->GetNext();
2093 // Next find the last paragraph containing part of the range
2094 node
= m_children
.GetFirst();
2097 // child is a paragraph
2098 wxRichTextObject
* child
= node
->GetData();
2099 const wxRichTextRange
& r
= child
->GetRange();
2101 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2107 node
= node
->GetNext();
2110 if (!startPara
|| !endPara
)
2113 // Now we can add up the sizes
2114 for (node
= startPara
; node
; node
= node
->GetNext())
2116 // child is a paragraph
2117 wxRichTextObject
* child
= node
->GetData();
2118 const wxRichTextRange
& childRange
= child
->GetRange();
2119 wxRichTextRange rangeToFind
= range
;
2120 rangeToFind
.LimitTo(childRange
);
2122 if (child
->IsTopLevel())
2123 rangeToFind
= child
->GetOwnRange();
2127 int childDescent
= 0;
2128 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2130 descent
= wxMax(childDescent
, descent
);
2132 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2133 sz
.y
+= childSize
.y
;
2135 if (node
== endPara
)
2144 /// Get the paragraph at the given position
2145 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2150 // First find the first paragraph whose starting position is within the range.
2151 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2154 // child is a paragraph
2155 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2156 // wxASSERT (child != NULL);
2160 // Return first child in buffer if position is -1
2164 if (child
->GetRange().Contains(pos
))
2168 node
= node
->GetNext();
2173 /// Get the line at the given position
2174 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2179 // First find the first paragraph whose starting position is within the range.
2180 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2183 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2184 if (obj
->GetRange().Contains(pos
))
2186 // child is a paragraph
2187 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2188 // wxASSERT (child != NULL);
2192 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2195 wxRichTextLine
* line
= node2
->GetData();
2197 wxRichTextRange range
= line
->GetAbsoluteRange();
2199 if (range
.Contains(pos
) ||
2201 // If the position is end-of-paragraph, then return the last line of
2202 // of the paragraph.
2203 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2206 node2
= node2
->GetNext();
2211 node
= node
->GetNext();
2214 int lineCount
= GetLineCount();
2216 return GetLineForVisibleLineNumber(lineCount
-1);
2221 /// Get the line at the given y pixel position, or the last line.
2222 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2224 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2227 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2228 // wxASSERT (child != NULL);
2232 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2235 wxRichTextLine
* line
= node2
->GetData();
2237 wxRect
rect(line
->GetRect());
2239 if (y
<= rect
.GetBottom())
2242 node2
= node2
->GetNext();
2246 node
= node
->GetNext();
2250 int lineCount
= GetLineCount();
2252 return GetLineForVisibleLineNumber(lineCount
-1);
2257 /// Get the number of visible lines
2258 int wxRichTextParagraphLayoutBox::GetLineCount() const
2262 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2265 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2266 // wxASSERT (child != NULL);
2269 count
+= child
->GetLines().GetCount();
2271 node
= node
->GetNext();
2277 /// Get the paragraph for a given line
2278 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2280 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2283 /// Get the line size at the given position
2284 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2286 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2289 return line
->GetSize();
2292 return wxSize(0, 0);
2296 /// Convenience function to add a paragraph of text
2297 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2299 // Don't use the base style, just the default style, and the base style will
2300 // be combined at display time.
2301 // Divide into paragraph and character styles.
2303 wxRichTextAttr defaultCharStyle
;
2304 wxRichTextAttr defaultParaStyle
;
2306 // If the default style is a named paragraph style, don't apply any character formatting
2307 // to the initial text string.
2308 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2310 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2312 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2315 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2317 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2318 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2320 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2326 return para
->GetRange();
2329 /// Adds multiple paragraphs, based on newlines.
2330 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2332 // Don't use the base style, just the default style, and the base style will
2333 // be combined at display time.
2334 // Divide into paragraph and character styles.
2336 wxRichTextAttr defaultCharStyle
;
2337 wxRichTextAttr defaultParaStyle
;
2339 // If the default style is a named paragraph style, don't apply any character formatting
2340 // to the initial text string.
2341 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2343 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2345 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2348 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2350 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2351 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2353 wxRichTextParagraph
* firstPara
= NULL
;
2354 wxRichTextParagraph
* lastPara
= NULL
;
2356 wxRichTextRange
range(-1, -1);
2359 size_t len
= text
.length();
2361 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2370 wxChar ch
= text
[i
];
2371 if (ch
== wxT('\n') || ch
== wxT('\r'))
2375 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2376 plainText
->SetText(line
);
2378 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2383 line
= wxEmptyString
;
2394 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2395 plainText
->SetText(line
);
2400 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2403 /// Convenience function to add an image
2404 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2406 // Don't use the base style, just the default style, and the base style will
2407 // be combined at display time.
2408 // Divide into paragraph and character styles.
2410 wxRichTextAttr defaultCharStyle
;
2411 wxRichTextAttr defaultParaStyle
;
2413 // If the default style is a named paragraph style, don't apply any character formatting
2414 // to the initial text string.
2415 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2417 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2419 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2422 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2424 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2425 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2427 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2429 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2433 return para
->GetRange();
2437 /// Insert fragment into this box at the given position. If partialParagraph is true,
2438 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2441 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2443 // First, find the first paragraph whose starting position is within the range.
2444 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2447 wxRichTextAttr originalAttr
= para
->GetAttributes();
2449 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2451 // Now split at this position, returning the object to insert the new
2452 // ones in front of.
2453 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2455 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2456 // text, for example, so let's optimize.
2458 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2460 // Add the first para to this para...
2461 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2465 // Iterate through the fragment paragraph inserting the content into this paragraph.
2466 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2467 wxASSERT (firstPara
!= NULL
);
2469 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2472 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2477 para
->AppendChild(newObj
);
2481 // Insert before nextObject
2482 para
->InsertChild(newObj
, nextObject
);
2485 objectNode
= objectNode
->GetNext();
2492 // Procedure for inserting a fragment consisting of a number of
2495 // 1. Remove and save the content that's after the insertion point, for adding
2496 // back once we've added the fragment.
2497 // 2. Add the content from the first fragment paragraph to the current
2499 // 3. Add remaining fragment paragraphs after the current paragraph.
2500 // 4. Add back the saved content from the first paragraph. If partialParagraph
2501 // is true, add it to the last paragraph added and not a new one.
2503 // 1. Remove and save objects after split point.
2504 wxList savedObjects
;
2506 para
->MoveToList(nextObject
, savedObjects
);
2508 // 2. Add the content from the 1st fragment paragraph.
2509 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2513 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2514 wxASSERT(firstPara
!= NULL
);
2516 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2517 para
->SetAttributes(firstPara
->GetAttributes());
2519 // Save empty paragraph attributes for appending later
2520 // These are character attributes deliberately set for a new paragraph. Without this,
2521 // we couldn't pass default attributes when appending a new paragraph.
2522 wxRichTextAttr emptyParagraphAttributes
;
2524 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2526 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2527 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2531 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2534 para
->AppendChild(newObj
);
2536 objectNode
= objectNode
->GetNext();
2539 // 3. Add remaining fragment paragraphs after the current paragraph.
2540 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2541 wxRichTextObject
* nextParagraph
= NULL
;
2542 if (nextParagraphNode
)
2543 nextParagraph
= nextParagraphNode
->GetData();
2545 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2546 wxRichTextParagraph
* finalPara
= para
;
2548 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2550 // If there was only one paragraph, we need to insert a new one.
2553 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2554 wxASSERT( para
!= NULL
);
2556 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2559 InsertChild(finalPara
, nextParagraph
);
2561 AppendChild(finalPara
);
2566 // If there was only one paragraph, or we have full paragraphs in our fragment,
2567 // we need to insert a new one.
2570 finalPara
= new wxRichTextParagraph
;
2573 InsertChild(finalPara
, nextParagraph
);
2575 AppendChild(finalPara
);
2578 // 4. Add back the remaining content.
2582 finalPara
->MoveFromList(savedObjects
);
2584 // Ensure there's at least one object
2585 if (finalPara
->GetChildCount() == 0)
2587 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2588 text
->SetAttributes(emptyParagraphAttributes
);
2590 finalPara
->AppendChild(text
);
2594 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2595 finalPara
->SetAttributes(firstPara
->GetAttributes());
2596 else if (finalPara
&& finalPara
!= para
)
2597 finalPara
->SetAttributes(originalAttr
);
2605 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2608 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2609 wxASSERT( para
!= NULL
);
2611 AppendChild(para
->Clone());
2620 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2621 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2622 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2624 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2627 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2628 wxASSERT( para
!= NULL
);
2630 if (!para
->GetRange().IsOutside(range
))
2632 fragment
.AppendChild(para
->Clone());
2637 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2638 if (!fragment
.IsEmpty())
2640 wxRichTextRange
topTailRange(range
);
2642 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2643 wxASSERT( firstPara
!= NULL
);
2645 // Chop off the start of the paragraph
2646 if (topTailRange
.GetStart() > firstPara
->GetRange().GetStart())
2648 wxRichTextRange
r(firstPara
->GetRange().GetStart(), topTailRange
.GetStart()-1);
2649 firstPara
->DeleteRange(r
);
2651 // Make sure the numbering is correct
2653 fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
);
2655 // Now, we've deleted some positions, so adjust the range
2657 topTailRange
.SetEnd(topTailRange
.GetEnd() - r
.GetLength());
2660 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2661 wxASSERT( lastPara
!= NULL
);
2663 if (topTailRange
.GetEnd() < (lastPara
->GetRange().GetEnd()-1))
2665 wxRichTextRange
r(topTailRange
.GetEnd()+1, lastPara
->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
2666 lastPara
->DeleteRange(r
);
2668 // Make sure the numbering is correct
2670 fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
);
2672 // We only have part of a paragraph at the end
2673 fragment
.SetPartialParagraph(true);
2677 if (topTailRange
.GetEnd() == (lastPara
->GetRange().GetEnd() - 1))
2678 // We have a partial paragraph (don't save last new paragraph marker)
2679 fragment
.SetPartialParagraph(true);
2681 // We have a complete paragraph
2682 fragment
.SetPartialParagraph(false);
2689 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2690 /// starting from zero at the start of the buffer.
2691 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2698 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2701 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2702 // wxASSERT( child != NULL );
2706 if (child
->GetRange().Contains(pos
))
2708 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2711 wxRichTextLine
* line
= node2
->GetData();
2712 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2714 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2716 // If the caret is displayed at the end of the previous wrapped line,
2717 // we want to return the line it's _displayed_ at (not the actual line
2718 // containing the position).
2719 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2720 return lineCount
- 1;
2727 node2
= node2
->GetNext();
2729 // If we didn't find it in the lines, it must be
2730 // the last position of the paragraph. So return the last line.
2734 lineCount
+= child
->GetLines().GetCount();
2737 node
= node
->GetNext();
2744 /// Given a line number, get the corresponding wxRichTextLine object.
2745 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2749 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2752 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2753 // wxASSERT(child != NULL);
2757 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2759 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2762 wxRichTextLine
* line
= node2
->GetData();
2764 if (lineCount
== lineNumber
)
2769 node2
= node2
->GetNext();
2773 lineCount
+= child
->GetLines().GetCount();
2776 node
= node
->GetNext();
2783 /// Delete range from layout.
2784 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2786 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2788 wxRichTextParagraph
* firstPara
= NULL
;
2791 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2792 // wxASSERT (obj != NULL);
2794 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2798 // Delete the range in each paragraph
2800 if (!obj
->GetRange().IsOutside(range
))
2802 // Deletes the content of this object within the given range
2803 obj
->DeleteRange(range
);
2805 wxRichTextRange thisRange
= obj
->GetRange();
2806 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2808 // If the whole paragraph is within the range to delete,
2809 // delete the whole thing.
2810 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2812 // Delete the whole object
2813 RemoveChild(obj
, true);
2816 else if (!firstPara
)
2819 // If the range includes the paragraph end, we need to join this
2820 // and the next paragraph.
2821 if (range
.GetEnd() <= thisRange
.GetEnd())
2823 // We need to move the objects from the next paragraph
2824 // to this paragraph
2826 wxRichTextParagraph
* nextParagraph
= NULL
;
2827 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2828 nextParagraph
= obj
;
2831 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2833 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2836 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2838 wxRichTextAttr nextParaAttr
;
2839 if (applyFinalParagraphStyle
)
2841 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2842 // not the next one.
2843 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2844 nextParaAttr
= thisAttr
;
2846 nextParaAttr
= nextParagraph
->GetAttributes();
2849 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2851 // Move the objects to the previous para
2852 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2856 wxRichTextObject
* obj1
= node1
->GetData();
2858 firstPara
->AppendChild(obj1
);
2860 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2861 nextParagraph
->GetChildren().Erase(node1
);
2866 // Delete the paragraph
2867 RemoveChild(nextParagraph
, true);
2870 // Avoid empty paragraphs
2871 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2873 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2874 firstPara
->AppendChild(text
);
2877 if (applyFinalParagraphStyle
)
2878 firstPara
->SetAttributes(nextParaAttr
);
2891 /// Get any text in this object for the given range
2892 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2896 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2899 wxRichTextObject
* child
= node
->GetData();
2900 if (!child
->GetRange().IsOutside(range
))
2902 wxRichTextRange childRange
= range
;
2903 childRange
.LimitTo(child
->GetRange());
2905 wxString childText
= child
->GetTextForRange(childRange
);
2909 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2914 node
= node
->GetNext();
2920 /// Get all the text
2921 wxString
wxRichTextParagraphLayoutBox::GetText() const
2923 return GetTextForRange(GetOwnRange());
2926 /// Get the paragraph by number
2927 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2929 if ((size_t) paragraphNumber
>= GetChildCount())
2932 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2935 /// Get the length of the paragraph
2936 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2938 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2940 return para
->GetRange().GetLength() - 1; // don't include newline
2945 /// Get the text of the paragraph
2946 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2948 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2950 return para
->GetTextForRange(para
->GetRange());
2952 return wxEmptyString
;
2955 /// Convert zero-based line column and paragraph number to a position.
2956 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
2958 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
2961 return para
->GetRange().GetStart() + x
;
2967 /// Convert zero-based position to line column and paragraph number
2968 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
2970 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
2974 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2977 wxRichTextObject
* child
= node
->GetData();
2981 node
= node
->GetNext();
2985 *x
= pos
- para
->GetRange().GetStart();
2993 /// Get the leaf object in a paragraph at this position.
2994 /// Given a line number, get the corresponding wxRichTextLine object.
2995 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
2997 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3000 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3004 wxRichTextObject
* child
= node
->GetData();
3005 if (child
->GetRange().Contains(position
))
3008 node
= node
->GetNext();
3010 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3011 return para
->GetChildren().GetLast()->GetData();
3016 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3017 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3019 bool characterStyle
= false;
3020 bool paragraphStyle
= false;
3022 if (style
.IsCharacterStyle())
3023 characterStyle
= true;
3024 if (style
.IsParagraphStyle())
3025 paragraphStyle
= true;
3027 wxRichTextBuffer
* buffer
= GetBuffer();
3029 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3030 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3031 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3032 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3033 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3034 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3036 // Apply paragraph style first, if any
3037 wxRichTextAttr
wholeStyle(style
);
3039 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3041 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3043 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3046 // Limit the attributes to be set to the content to only character attributes.
3047 wxRichTextAttr
characterAttributes(wholeStyle
);
3048 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3050 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3052 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3054 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3057 // If we are associated with a control, make undoable; otherwise, apply immediately
3060 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3062 wxRichTextAction
* action
= NULL
;
3064 if (haveControl
&& withUndo
)
3066 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3067 action
->SetRange(range
);
3068 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3071 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3074 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3075 // wxASSERT (para != NULL);
3077 if (para
&& para
->GetChildCount() > 0)
3079 // Stop searching if we're beyond the range of interest
3080 if (para
->GetRange().GetStart() > range
.GetEnd())
3083 if (!para
->GetRange().IsOutside(range
))
3085 // We'll be using a copy of the paragraph to make style changes,
3086 // not updating the buffer directly.
3087 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3089 if (haveControl
&& withUndo
)
3091 newPara
= new wxRichTextParagraph(*para
);
3092 action
->GetNewParagraphs().AppendChild(newPara
);
3094 // Also store the old ones for Undo
3095 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3100 // If we're specifying paragraphs only, then we really mean character formatting
3101 // to be included in the paragraph style
3102 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3106 // Removes the given style from the paragraph
3107 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3109 else if (resetExistingStyle
)
3110 newPara
->GetAttributes() = wholeStyle
;
3115 // Only apply attributes that will make a difference to the combined
3116 // style as seen on the display
3117 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3118 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3121 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3125 // When applying paragraph styles dynamically, don't change the text objects' attributes
3126 // since they will computed as needed. Only apply the character styling if it's _only_
3127 // character styling. This policy is subject to change and might be put under user control.
3129 // Hm. we might well be applying a mix of paragraph and character styles, in which
3130 // case we _do_ want to apply character styles regardless of what para styles are set.
3131 // But if we're applying a paragraph style, which has some character attributes, but
3132 // we only want the paragraphs to hold this character style, then we _don't_ want to
3133 // apply the character style. So we need to be able to choose.
3135 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3137 wxRichTextRange
childRange(range
);
3138 childRange
.LimitTo(newPara
->GetRange());
3140 // Find the starting position and if necessary split it so
3141 // we can start applying a different style.
3142 // TODO: check that the style actually changes or is different
3143 // from style outside of range
3144 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3145 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3147 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3148 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3150 firstObject
= newPara
->SplitAt(range
.GetStart());
3152 // Increment by 1 because we're apply the style one _after_ the split point
3153 long splitPoint
= childRange
.GetEnd();
3154 if (splitPoint
!= newPara
->GetRange().GetEnd())
3158 if (splitPoint
== newPara
->GetRange().GetEnd())
3159 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3161 // lastObject is set as a side-effect of splitting. It's
3162 // returned as the object before the new object.
3163 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3165 wxASSERT(firstObject
!= NULL
);
3166 wxASSERT(lastObject
!= NULL
);
3168 if (!firstObject
|| !lastObject
)
3171 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3172 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3174 wxASSERT(firstNode
);
3177 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3181 wxRichTextObject
* child
= node2
->GetData();
3185 // Removes the given style from the paragraph
3186 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3188 else if (resetExistingStyle
)
3189 child
->GetAttributes() = characterAttributes
;
3194 // Only apply attributes that will make a difference to the combined
3195 // style as seen on the display
3196 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3197 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3200 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3203 if (node2
== lastNode
)
3206 node2
= node2
->GetNext();
3212 node
= node
->GetNext();
3215 // Do action, or delay it until end of batch.
3216 if (haveControl
&& withUndo
)
3217 buffer
->SubmitAction(action
);
3222 // Just change the attributes for this single object.
3223 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3225 wxRichTextBuffer
* buffer
= GetBuffer();
3226 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3227 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3228 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3230 wxRichTextAction
*action
= NULL
;
3231 wxRichTextAttr newAttr
= obj
->GetAttributes();
3232 if (resetExistingStyle
)
3235 newAttr
.Apply(textAttr
);
3237 if (haveControl
&& withUndo
)
3239 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3240 action
->SetRange(obj
->GetRange().FromInternal());
3241 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3242 action
->MakeObject(obj
);
3244 action
->GetAttributes() = newAttr
;
3247 obj
->GetAttributes() = newAttr
;
3249 if (haveControl
&& withUndo
)
3250 buffer
->SubmitAction(action
);
3253 /// Get the text attributes for this position.
3254 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3256 return DoGetStyle(position
, style
, true);
3259 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3261 return DoGetStyle(position
, style
, false);
3264 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3265 /// context attributes.
3266 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3268 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3270 if (style
.IsParagraphStyle())
3272 obj
= GetParagraphAtPosition(position
);
3277 // Start with the base style
3278 style
= GetAttributes();
3280 // Apply the paragraph style
3281 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3284 style
= obj
->GetAttributes();
3291 obj
= GetLeafObjectAtPosition(position
);
3296 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3297 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3300 style
= obj
->GetAttributes();
3308 static bool wxHasStyle(long flags
, long style
)
3310 return (flags
& style
) != 0;
3313 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3315 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3317 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3322 /// Get the combined style for a range - if any attribute is different within the range,
3323 /// that attribute is not present within the flags.
3324 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3326 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3328 style
= wxRichTextAttr();
3330 wxRichTextAttr clashingAttr
;
3331 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3333 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3336 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3337 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3339 if (para
->GetChildren().GetCount() == 0)
3341 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3343 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3347 wxRichTextRange
paraRange(para
->GetRange());
3348 paraRange
.LimitTo(range
);
3350 // First collect paragraph attributes only
3351 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3352 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3353 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3355 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3359 wxRichTextObject
* child
= childNode
->GetData();
3360 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3362 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3364 // Now collect character attributes only
3365 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3367 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3370 childNode
= childNode
->GetNext();
3374 node
= node
->GetNext();
3379 /// Set default style
3380 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3382 m_defaultAttributes
= style
;
3386 /// Test if this whole range has character attributes of the specified kind. If any
3387 /// of the attributes are different within the range, the test fails. You
3388 /// can use this to implement, for example, bold button updating. style must have
3389 /// flags indicating which attributes are of interest.
3390 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3393 int matchingCount
= 0;
3395 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3398 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3399 // wxASSERT (para != NULL);
3403 // Stop searching if we're beyond the range of interest
3404 if (para
->GetRange().GetStart() > range
.GetEnd())
3405 return foundCount
== matchingCount
&& foundCount
!= 0;
3407 if (!para
->GetRange().IsOutside(range
))
3409 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3413 wxRichTextObject
* child
= node2
->GetData();
3414 // Allow for empty string if no buffer
3415 wxRichTextRange childRange
= child
->GetRange();
3416 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3417 childRange
.SetEnd(childRange
.GetEnd()+1);
3419 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3422 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3424 if (wxTextAttrEqPartial(textAttr
, style
))
3428 node2
= node2
->GetNext();
3433 node
= node
->GetNext();
3436 return foundCount
== matchingCount
&& foundCount
!= 0;
3439 /// Test if this whole range has paragraph attributes of the specified kind. If any
3440 /// of the attributes are different within the range, the test fails. You
3441 /// can use this to implement, for example, centering button updating. style must have
3442 /// flags indicating which attributes are of interest.
3443 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3446 int matchingCount
= 0;
3448 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3451 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3452 // wxASSERT (para != NULL);
3456 // Stop searching if we're beyond the range of interest
3457 if (para
->GetRange().GetStart() > range
.GetEnd())
3458 return foundCount
== matchingCount
&& foundCount
!= 0;
3460 if (!para
->GetRange().IsOutside(range
))
3462 wxRichTextAttr textAttr
= GetAttributes();
3463 // Apply the paragraph style
3464 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3467 if (wxTextAttrEqPartial(textAttr
, style
))
3472 node
= node
->GetNext();
3474 return foundCount
== matchingCount
&& foundCount
!= 0;
3477 void wxRichTextParagraphLayoutBox::Reset()
3481 wxRichTextBuffer
* buffer
= GetBuffer();
3482 if (buffer
&& buffer
->GetRichTextCtrl())
3484 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3485 event
.SetEventObject(buffer
->GetRichTextCtrl());
3486 event
.SetContainer(this);
3488 buffer
->SendEvent(event
, true);
3491 AddParagraph(wxEmptyString
);
3493 InvalidateHierarchy(wxRICHTEXT_ALL
);
3496 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3497 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3499 wxRichTextCompositeObject::Invalidate(invalidRange
);
3501 DoInvalidate(invalidRange
);
3504 // Do the (in)validation for this object only
3505 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3507 if (invalidRange
== wxRICHTEXT_ALL
)
3509 m_invalidRange
= wxRICHTEXT_ALL
;
3511 // Already invalidating everything
3512 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3517 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3518 m_invalidRange
.SetStart(invalidRange
.GetStart());
3519 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3520 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3524 // Do the (in)validation both up and down the hierarchy
3525 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3527 Invalidate(invalidRange
);
3529 if (invalidRange
!= wxRICHTEXT_NONE
)
3531 // Now go up the hierarchy
3532 wxRichTextObject
* thisObj
= this;
3533 wxRichTextObject
* p
= GetParent();
3536 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3538 l
->DoInvalidate(thisObj
->GetRange());
3546 /// Get invalid range, rounding to entire paragraphs if argument is true.
3547 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3549 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3550 return m_invalidRange
;
3552 wxRichTextRange range
= m_invalidRange
;
3554 if (wholeParagraphs
)
3556 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3558 range
.SetStart(para1
->GetRange().GetStart());
3559 // floating layout make all child should be relayout
3560 range
.SetEnd(GetOwnRange().GetEnd());
3565 /// Apply the style sheet to the buffer, for example if the styles have changed.
3566 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3568 wxASSERT(styleSheet
!= NULL
);
3574 wxRichTextAttr
attr(GetBasicStyle());
3575 if (GetBasicStyle().HasParagraphStyleName())
3577 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3580 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3581 SetBasicStyle(attr
);
3586 if (GetBasicStyle().HasCharacterStyleName())
3588 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3591 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3592 SetBasicStyle(attr
);
3597 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3600 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3601 // wxASSERT (para != NULL);
3605 // Combine paragraph and list styles. If there is a list style in the original attributes,
3606 // the current indentation overrides anything else and is used to find the item indentation.
3607 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3608 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3609 // exception as above).
3610 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3611 // So when changing a list style interactively, could retrieve level based on current style, then
3612 // set appropriate indent and apply new style.
3616 if (para
->GetAttributes().HasOutlineLevel())
3617 outline
= para
->GetAttributes().GetOutlineLevel();
3618 if (para
->GetAttributes().HasBulletNumber())
3619 num
= para
->GetAttributes().GetBulletNumber();
3621 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3623 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3625 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3626 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3627 if (paraDef
&& !listDef
)
3629 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3632 else if (listDef
&& !paraDef
)
3634 // Set overall style defined for the list style definition
3635 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3637 // Apply the style for this level
3638 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3641 else if (listDef
&& paraDef
)
3643 // Combines overall list style, style for level, and paragraph style
3644 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3648 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3650 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3652 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3654 // Overall list definition style
3655 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3657 // Style for this level
3658 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3662 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3664 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3667 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3673 para
->GetAttributes().SetOutlineLevel(outline
);
3675 para
->GetAttributes().SetBulletNumber(num
);
3678 node
= node
->GetNext();
3680 return foundCount
!= 0;
3684 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3686 wxRichTextBuffer
* buffer
= GetBuffer();
3687 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3689 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3690 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3691 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3692 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3694 // Current number, if numbering
3697 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3699 // If we are associated with a control, make undoable; otherwise, apply immediately
3702 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3704 wxRichTextAction
* action
= NULL
;
3706 if (haveControl
&& withUndo
)
3708 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3709 action
->SetRange(range
);
3710 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3713 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3716 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3717 // wxASSERT (para != NULL);
3719 if (para
&& para
->GetChildCount() > 0)
3721 // Stop searching if we're beyond the range of interest
3722 if (para
->GetRange().GetStart() > range
.GetEnd())
3725 if (!para
->GetRange().IsOutside(range
))
3727 // We'll be using a copy of the paragraph to make style changes,
3728 // not updating the buffer directly.
3729 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3731 if (haveControl
&& withUndo
)
3733 newPara
= new wxRichTextParagraph(*para
);
3734 action
->GetNewParagraphs().AppendChild(newPara
);
3736 // Also store the old ones for Undo
3737 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3744 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3745 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3747 // How is numbering going to work?
3748 // If we are renumbering, or numbering for the first time, we need to keep
3749 // track of the number for each level. But we might be simply applying a different
3751 // In Word, applying a style to several paragraphs, even if at different levels,
3752 // reverts the level back to the same one. So we could do the same here.
3753 // Renumbering will need to be done when we promote/demote a paragraph.
3755 // Apply the overall list style, and item style for this level
3756 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3757 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3759 // Now we need to do numbering
3762 newPara
->GetAttributes().SetBulletNumber(n
);
3767 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3769 // if def is NULL, remove list style, applying any associated paragraph style
3770 // to restore the attributes
3772 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3773 newPara
->GetAttributes().SetLeftIndent(0, 0);
3774 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3776 // Eliminate the main list-related attributes
3777 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
);
3779 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3781 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3784 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3791 node
= node
->GetNext();
3794 // Do action, or delay it until end of batch.
3795 if (haveControl
&& withUndo
)
3796 buffer
->SubmitAction(action
);
3801 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3803 wxRichTextBuffer
* buffer
= GetBuffer();
3804 if (buffer
&& buffer
->GetStyleSheet())
3806 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3808 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
3813 /// Clear list for given range
3814 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3816 return SetListStyle(range
, NULL
, flags
);
3819 /// Number/renumber any list elements in the given range
3820 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3822 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
3825 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3826 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
3827 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3829 wxRichTextBuffer
* buffer
= GetBuffer();
3830 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3832 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3833 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3835 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3838 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3840 // Max number of levels
3841 const int maxLevels
= 10;
3843 // The level we're looking at now
3844 int currentLevel
= -1;
3846 // The item number for each level
3847 int levels
[maxLevels
];
3850 // Reset all numbering
3851 for (i
= 0; i
< maxLevels
; i
++)
3853 if (startFrom
!= -1)
3854 levels
[i
] = startFrom
-1;
3855 else if (renumber
) // start again
3858 levels
[i
] = -1; // start from the number we found, if any
3861 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3863 // If we are associated with a control, make undoable; otherwise, apply immediately
3866 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3868 wxRichTextAction
* action
= NULL
;
3870 if (haveControl
&& withUndo
)
3872 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3873 action
->SetRange(range
);
3874 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3877 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3880 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3881 // wxASSERT (para != NULL);
3883 if (para
&& para
->GetChildCount() > 0)
3885 // Stop searching if we're beyond the range of interest
3886 if (para
->GetRange().GetStart() > range
.GetEnd())
3889 if (!para
->GetRange().IsOutside(range
))
3891 // We'll be using a copy of the paragraph to make style changes,
3892 // not updating the buffer directly.
3893 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3895 if (haveControl
&& withUndo
)
3897 newPara
= new wxRichTextParagraph(*para
);
3898 action
->GetNewParagraphs().AppendChild(newPara
);
3900 // Also store the old ones for Undo
3901 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3906 wxRichTextListStyleDefinition
* defToUse
= def
;
3909 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
3910 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
3915 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3916 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
3918 // If we've specified a level to apply to all, change the level.
3919 if (specifiedLevel
!= -1)
3920 thisLevel
= specifiedLevel
;
3922 // Do promotion if specified
3923 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
3925 thisLevel
= thisLevel
- promoteBy
;
3932 // Apply the overall list style, and item style for this level
3933 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3934 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3936 // OK, we've (re)applied the style, now let's get the numbering right.
3938 if (currentLevel
== -1)
3939 currentLevel
= thisLevel
;
3941 // Same level as before, do nothing except increment level's number afterwards
3942 if (currentLevel
== thisLevel
)
3945 // A deeper level: start renumbering all levels after current level
3946 else if (thisLevel
> currentLevel
)
3948 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
3952 currentLevel
= thisLevel
;
3954 else if (thisLevel
< currentLevel
)
3956 currentLevel
= thisLevel
;
3959 // Use the current numbering if -1 and we have a bullet number already
3960 if (levels
[currentLevel
] == -1)
3962 if (newPara
->GetAttributes().HasBulletNumber())
3963 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
3965 levels
[currentLevel
] = 1;
3969 levels
[currentLevel
] ++;
3972 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
3974 // Create the bullet text if an outline list
3975 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
3978 for (i
= 0; i
<= currentLevel
; i
++)
3980 if (!text
.IsEmpty())
3982 text
+= wxString::Format(wxT("%d"), levels
[i
]);
3984 newPara
->GetAttributes().SetBulletText(text
);
3990 node
= node
->GetNext();
3993 // Do action, or delay it until end of batch.
3994 if (haveControl
&& withUndo
)
3995 buffer
->SubmitAction(action
);
4000 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4002 wxRichTextBuffer
* buffer
= GetBuffer();
4003 if (buffer
->GetStyleSheet())
4005 wxRichTextListStyleDefinition
* def
= NULL
;
4006 if (!defName
.IsEmpty())
4007 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4008 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4013 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4014 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4017 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4018 // to NumberList with a flag indicating promotion is required within one of the ranges.
4019 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4020 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4021 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4022 // list position will start from 1.
4023 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4024 // We can end the renumbering at this point.
4026 // For now, only renumber within the promotion range.
4028 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4031 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4033 wxRichTextBuffer
* buffer
= GetBuffer();
4034 if (buffer
->GetStyleSheet())
4036 wxRichTextListStyleDefinition
* def
= NULL
;
4037 if (!defName
.IsEmpty())
4038 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4039 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4044 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4045 /// position of the paragraph that it had to start looking from.
4046 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4048 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4051 wxRichTextBuffer
* buffer
= GetBuffer();
4052 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4053 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4055 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4058 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4059 // int thisLevel = def->FindLevelForIndent(thisIndent);
4061 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4063 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4064 if (previousParagraph
->GetAttributes().HasBulletName())
4065 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4066 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4067 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4069 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4070 attr
.SetBulletNumber(nextNumber
);
4074 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4075 if (!text
.IsEmpty())
4077 int pos
= text
.Find(wxT('.'), true);
4078 if (pos
!= wxNOT_FOUND
)
4080 text
= text
.Mid(0, text
.Length() - pos
- 1);
4083 text
= wxEmptyString
;
4084 if (!text
.IsEmpty())
4086 text
+= wxString::Format(wxT("%d"), nextNumber
);
4087 attr
.SetBulletText(text
);
4101 * wxRichTextParagraph
4102 * This object represents a single paragraph (or in a straight text editor, a line).
4105 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4107 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4109 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4110 wxRichTextCompositeObject(parent
)
4113 SetAttributes(*style
);
4116 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4117 wxRichTextCompositeObject(parent
)
4120 SetAttributes(*paraStyle
);
4122 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4125 wxRichTextParagraph::~wxRichTextParagraph()
4131 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4136 // Currently we don't merge these attributes with the parent, but we
4137 // should consider whether we should (e.g. if we set a border colour
4138 // for all paragraphs). But generally box attributes are likely to be
4139 // different for different objects.
4140 wxRect paraRect
= GetRect();
4141 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4143 wxRichTextAttr attr
= GetCombinedAttributes();
4145 // Draw the bullet, if any
4146 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4148 if (attr
.GetLeftSubIndent() != 0)
4150 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4151 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4153 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4155 // Combine with the font of the first piece of content, if one is specified
4156 if (GetChildren().GetCount() > 0)
4158 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4159 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4161 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4165 // Get line height from first line, if any
4166 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4169 int lineHeight
wxDUMMY_INITIALIZE(0);
4172 lineHeight
= line
->GetSize().y
;
4173 linePos
= line
->GetPosition() + GetPosition();
4178 if (bulletAttr
.HasFont() && GetBuffer())
4179 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4181 font
= (*wxNORMAL_FONT
);
4183 wxCheckSetFont(dc
, font
);
4185 lineHeight
= dc
.GetCharHeight();
4186 linePos
= GetPosition();
4187 linePos
.y
+= spaceBeforePara
;
4190 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4192 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4194 if (wxRichTextBuffer::GetRenderer())
4195 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4197 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4199 if (wxRichTextBuffer::GetRenderer())
4200 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4204 wxString bulletText
= GetBulletText();
4206 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4207 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4212 // Draw the range for each line, one object at a time.
4214 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4217 wxRichTextLine
* line
= node
->GetData();
4218 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4220 // Lines are specified relative to the paragraph
4222 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4224 // Don't draw if off the screen
4225 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4227 wxPoint objectPosition
= linePosition
;
4228 int maxDescent
= line
->GetDescent();
4230 // Loop through objects until we get to the one within range
4231 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4236 wxRichTextObject
* child
= node2
->GetData();
4238 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4240 // Draw this part of the line at the correct position
4241 wxRichTextRange
objectRange(child
->GetRange());
4242 objectRange
.LimitTo(lineRange
);
4245 if (child
->IsTopLevel())
4247 objectSize
= child
->GetCachedSize();
4248 objectRange
= child
->GetOwnRange();
4252 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4253 if (i
< (int) line
->GetObjectSizes().GetCount())
4255 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4261 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4265 // Use the child object's width, but the whole line's height
4266 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4267 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4269 objectPosition
.x
+= objectSize
.x
;
4272 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4273 // Can break out of inner loop now since we've passed this line's range
4276 node2
= node2
->GetNext();
4280 node
= node
->GetNext();
4286 // Get the range width using partial extents calculated for the whole paragraph.
4287 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4289 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4291 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4294 int leftMostPos
= 0;
4295 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4296 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4298 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4300 int w
= rightMostPos
- leftMostPos
;
4305 /// Lay the item out
4306 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
4308 // Deal with floating objects firstly before the normal layout
4309 wxRichTextBuffer
* buffer
= GetBuffer();
4311 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4312 wxASSERT(collector
);
4313 LayoutFloat(dc
, rect
, style
, collector
);
4315 wxRichTextAttr attr
= GetCombinedAttributes();
4319 // Increase the size of the paragraph due to spacing
4320 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4321 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4322 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4323 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4324 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4326 int lineSpacing
= 0;
4328 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4329 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().Ok())
4331 wxCheckSetFont(dc
, attr
.GetFont());
4332 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4335 // Start position for each line relative to the paragraph
4336 int startPositionFirstLine
= leftIndent
;
4337 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4339 // If we have a bullet in this paragraph, the start position for the first line's text
4340 // is actually leftIndent + leftSubIndent.
4341 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4342 startPositionFirstLine
= startPositionSubsequentLines
;
4344 long lastEndPos
= GetRange().GetStart()-1;
4345 long lastCompletedEndPos
= lastEndPos
;
4347 int currentWidth
= 0;
4348 SetPosition(rect
.GetPosition());
4350 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4353 int maxHeight
= currentPosition
.y
;
4358 int lineDescent
= 0;
4360 wxRichTextObjectList::compatibility_iterator node
;
4362 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4364 wxArrayInt partialExtents
;
4367 int paraDescent
= 0;
4369 // This calculates the partial text extents
4370 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4372 node
= m_children
.GetFirst();
4375 wxRichTextObject
* child
= node
->GetData();
4377 //child->SetCachedSize(wxDefaultSize);
4378 child
->Layout(dc
, rect
, style
);
4380 node
= node
->GetNext();
4387 // We may need to go back to a previous child, in which case create the new line,
4388 // find the child corresponding to the start position of the string, and
4391 wxRect availableRect
;
4393 node
= m_children
.GetFirst();
4396 wxRichTextObject
* child
= node
->GetData();
4398 // If floating, ignore. We already laid out floats.
4399 // Also ignore if empty object, except if we haven't got any
4401 if (child
->IsFloating() || !child
->IsShown() ||
4402 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4405 node
= node
->GetNext();
4409 // If this is e.g. a composite text box, it will need to be laid out itself.
4410 // But if just a text fragment or image, for example, this will
4411 // do nothing. NB: won't we need to set the position after layout?
4412 // since for example if position is dependent on vertical line size, we
4413 // can't tell the position until the size is determined. So possibly introduce
4414 // another layout phase.
4416 // We may only be looking at part of a child, if we searched back for wrapping
4417 // and found a suitable point some way into the child. So get the size for the fragment
4420 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4421 long lastPosToUse
= child
->GetRange().GetEnd();
4422 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4424 if (lineBreakInThisObject
)
4425 lastPosToUse
= nextBreakPos
;
4428 int childDescent
= 0;
4430 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4431 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4432 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4434 if (child
->IsTopLevel())
4436 wxSize oldSize
= child
->GetCachedSize();
4438 child
->Invalidate(wxRICHTEXT_ALL
);
4439 child
->SetPosition(wxPoint(0, 0));
4441 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4442 // lays out the object again using the minimum size
4443 // The position will be determined by its location in its line,
4444 // and not by the child's actual position.
4445 child
->LayoutToBestSize(dc
, buffer
,
4446 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4448 if (oldSize
!= child
->GetCachedSize())
4450 partialExtents
.Clear();
4452 // Recalculate the partial text extents since the child object changed size
4453 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4457 // Problem: we need to layout composites here for which we need the available width,
4458 // but we can't get the available width without using the float collector which
4459 // needs to know the object height.
4461 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4463 childSize
= child
->GetCachedSize();
4464 childDescent
= child
->GetDescent();
4468 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4469 // Get height only, then the width using the partial extents
4470 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4471 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4473 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4478 int loopIterations
= 0;
4480 // If there are nested objects that need to lay themselves out, we have to do this in a
4481 // loop because the height of the object may well depend on the available width.
4482 // And because of floating object positioning, the available width depends on the
4483 // height of the object and whether it will clash with the floating objects.
4484 // So, we see whether the available width changes due to the presence of floating images.
4485 // If it does, then we'll use the new restricted width to find the object height again.
4486 // If this causes another restriction in the available width, we'll try again, until
4487 // either we lose patience or the available width settles down.
4492 wxRect oldAvailableRect
= availableRect
;
4494 // Available width depends on the floating objects and the line height.
4495 // Note: the floating objects may be placed vertically along the two side of
4496 // buffer, so we may have different available line widths with different
4497 // [startY, endY]. So, we can't determine how wide the available
4498 // space is until we know the exact line height.
4499 lineDescent
= wxMax(childDescent
, maxDescent
);
4500 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4501 lineHeight
= lineDescent
+ lineAscent
;
4502 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4504 // Adjust availableRect to the space that is available when taking floating objects into account.
4506 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4508 int newX
= floatAvailableRect
.x
+ startOffset
;
4509 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4510 availableRect
.x
= newX
;
4511 availableRect
.width
= newW
;
4514 if (floatAvailableRect
.width
< availableRect
.width
)
4515 availableRect
.width
= floatAvailableRect
.width
;
4517 currentPosition
.x
= availableRect
.x
- rect
.x
;
4519 if (child
->IsTopLevel() && loopIterations
<= 20)
4521 if (availableRect
!= oldAvailableRect
)
4523 wxSize oldSize
= child
->GetCachedSize();
4525 //child->SetCachedSize(wxDefaultSize);
4526 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4527 // lays out the object again using the minimum size
4528 child
->Invalidate(wxRICHTEXT_ALL
);
4529 child
->LayoutToBestSize(dc
, buffer
,
4530 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4531 childSize
= child
->GetCachedSize();
4532 childDescent
= child
->GetDescent();
4533 //child->SetPosition(availableRect.GetPosition());
4535 if (oldSize
!= child
->GetCachedSize())
4537 partialExtents
.Clear();
4539 // Recalculate the partial text extents since the child object changed size
4540 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4543 // Go around the loop finding the available rect for the given floating objects
4554 // 1) There was a line break BEFORE the natural break
4555 // 2) There was a line break AFTER the natural break
4556 // 3) It's the last line
4557 // 4) The child still fits (carry on) - 'else' clause
4559 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4561 (childSize
.x
+ currentWidth
> availableRect
.width
)
4563 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4567 if (child
->IsTopLevel())
4569 // We can move it to the correct position at this point
4570 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4573 long wrapPosition
= 0;
4574 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4575 wrapPosition
= child
->GetRange().GetEnd();
4578 // Find a place to wrap. This may walk back to previous children,
4579 // for example if a word spans several objects.
4580 // Note: one object must contains only one wxTextAtrr, so the line height will not
4581 // change inside one object. Thus, we can pass the remain line width to the
4582 // FindWrapPosition function.
4583 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4585 // If the function failed, just cut it off at the end of this child.
4586 wrapPosition
= child
->GetRange().GetEnd();
4589 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4590 if (wrapPosition
<= lastCompletedEndPos
)
4591 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4593 // Line end position shouldn't be the same as the end, or greater.
4594 if (wrapPosition
>= GetRange().GetEnd())
4595 wrapPosition
= GetRange().GetEnd()-1;
4597 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4599 // Let's find the actual size of the current line now
4601 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4603 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4604 /// for the fragment we're about to add.
4605 childDescent
= maxDescent
;
4607 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4608 if (!child
->IsEmpty())
4610 // Get height only, then the width using the partial extents
4611 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4612 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4616 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4618 currentWidth
= actualSize
.x
;
4619 maxDescent
= wxMax(childDescent
, maxDescent
);
4620 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4621 lineHeight
= maxDescent
+ maxAscent
;
4623 if (lineHeight
== 0 && buffer
)
4625 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4626 wxCheckSetFont(dc
, font
);
4627 lineHeight
= dc
.GetCharHeight();
4630 if (maxDescent
== 0)
4633 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4637 wxRichTextLine
* line
= AllocateLine(lineCount
);
4639 // Set relative range so we won't have to change line ranges when paragraphs are moved
4640 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4641 line
->SetPosition(currentPosition
);
4642 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4643 line
->SetDescent(maxDescent
);
4645 maxHeight
= currentPosition
.y
+ lineHeight
;
4647 // Now move down a line. TODO: add margins, spacing
4648 currentPosition
.y
+= lineHeight
;
4649 currentPosition
.y
+= lineSpacing
;
4652 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4657 // TODO: account for zero-length objects, such as fields
4658 // wxASSERT(wrapPosition > lastCompletedEndPos);
4660 lastEndPos
= wrapPosition
;
4661 lastCompletedEndPos
= lastEndPos
;
4665 if (wrapPosition
< GetRange().GetEnd()-1)
4667 // May need to set the node back to a previous one, due to searching back in wrapping
4668 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4669 if (childAfterWrapPosition
)
4670 node
= m_children
.Find(childAfterWrapPosition
);
4672 node
= node
->GetNext();
4675 node
= node
->GetNext();
4677 // Apply paragraph styles such as alignment to the wrapped line
4678 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4682 // We still fit, so don't add a line, and keep going
4683 currentWidth
+= childSize
.x
;
4684 maxDescent
= wxMax(childDescent
, maxDescent
);
4685 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4686 lineHeight
= maxDescent
+ maxAscent
;
4688 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4689 lastEndPos
= child
->GetRange().GetEnd();
4691 node
= node
->GetNext();
4695 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4697 // Remove remaining unused line objects, if any
4698 ClearUnusedLines(lineCount
);
4700 // We need to add back the margins etc.
4702 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4703 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4704 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4705 SetCachedSize(marginRect
.GetSize());
4708 // The maximum size is the length of the paragraph stretched out into a line.
4709 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4710 // this size. TODO: take into account line breaks.
4712 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4713 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
, currentPosition
.y
+ spaceAfterPara
));
4714 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4715 SetMaxSize(marginRect
.GetSize());
4718 // Find the greatest minimum size. Currently we only look at non-text objects,
4719 // which isn't ideal but it would be slow to find the maximum word width to
4720 // use as the minimum.
4723 node
= m_children
.GetFirst();
4726 wxRichTextObject
* child
= node
->GetData();
4728 // If floating, ignore. We already laid out floats.
4729 // Also ignore if empty object, except if we haven't got any
4731 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4733 if (child
->GetCachedSize().x
> minWidth
)
4734 minWidth
= child
->GetMinSize().x
;
4736 node
= node
->GetNext();
4739 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4740 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4741 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4742 SetMinSize(marginRect
.GetSize());
4746 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4747 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4748 // Use the text extents to calculate the size of each fragment in each line
4749 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4752 wxRichTextLine
* line
= lineNode
->GetData();
4753 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4755 // Loop through objects until we get to the one within range
4756 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4760 wxRichTextObject
* child
= node2
->GetData();
4762 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4764 wxRichTextRange rangeToUse
= lineRange
;
4765 rangeToUse
.LimitTo(child
->GetRange());
4767 // Find the size of the child from the text extents, and store in an array
4768 // for drawing later
4770 if (rangeToUse
.GetStart() > GetRange().GetStart())
4771 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4772 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4773 int sz
= right
- left
;
4774 line
->GetObjectSizes().Add(sz
);
4776 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4777 // Can break out of inner loop now since we've passed this line's range
4780 node2
= node2
->GetNext();
4783 lineNode
= lineNode
->GetNext();
4791 /// Apply paragraph styles, such as centering, to wrapped lines
4792 /// TODO: take into account box attributes, possibly
4793 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4795 if (!attr
.HasAlignment())
4798 wxPoint pos
= line
->GetPosition();
4799 wxSize size
= line
->GetSize();
4801 // centering, right-justification
4802 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4804 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4805 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
4806 line
->SetPosition(pos
);
4808 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4810 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4811 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
4812 line
->SetPosition(pos
);
4816 /// Insert text at the given position
4817 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
4819 wxRichTextObject
* childToUse
= NULL
;
4820 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
4822 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4825 wxRichTextObject
* child
= node
->GetData();
4826 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
4833 node
= node
->GetNext();
4838 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
4841 int posInString
= pos
- textObject
->GetRange().GetStart();
4843 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
4844 text
+ textObject
->GetText().Mid(posInString
);
4845 textObject
->SetText(newText
);
4847 int textLength
= text
.length();
4849 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
4850 textObject
->GetRange().GetEnd() + textLength
));
4852 // Increment the end range of subsequent fragments in this paragraph.
4853 // We'll set the paragraph range itself at a higher level.
4855 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
4858 wxRichTextObject
* child
= node
->GetData();
4859 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
4860 textObject
->GetRange().GetEnd() + textLength
));
4862 node
= node
->GetNext();
4869 // TODO: if not a text object, insert at closest position, e.g. in front of it
4875 // Don't pass parent initially to suppress auto-setting of parent range.
4876 // We'll do that at a higher level.
4877 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
4879 AppendChild(textObject
);
4886 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
4888 wxRichTextCompositeObject::Copy(obj
);
4891 /// Clear the cached lines
4892 void wxRichTextParagraph::ClearLines()
4894 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
4897 /// Get/set the object size for the given range. Returns false if the range
4898 /// is invalid for this object.
4899 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
4901 if (!range
.IsWithin(GetRange()))
4904 if (flags
& wxRICHTEXT_UNFORMATTED
)
4906 // Just use unformatted data, assume no line breaks
4907 // TODO: take into account line breaks
4911 wxArrayInt childExtents
;
4918 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4922 wxRichTextObject
* child
= node
->GetData();
4923 if (!child
->GetRange().IsOutside(range
))
4925 // Floating objects have a zero size within the paragraph.
4926 if (child
->IsFloating())
4931 if (partialExtents
->GetCount() > 0)
4932 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4936 partialExtents
->Add(0 /* zero size */ + lastSize
);
4943 wxRichTextRange rangeToUse
= range
;
4944 rangeToUse
.LimitTo(child
->GetRange());
4946 if (child
->IsTopLevel())
4947 rangeToUse
= child
->GetOwnRange();
4949 int childDescent
= 0;
4951 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4952 // but it's only going to be used after caching has taken place.
4953 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
4955 childDescent
= child
->GetDescent();
4956 childSize
= child
->GetCachedSize();
4958 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4959 sz
.x
+= childSize
.x
;
4960 descent
= wxMax(descent
, childDescent
);
4962 else if (child
->IsTopLevel())
4964 childDescent
= child
->GetDescent();
4965 childSize
= child
->GetCachedSize();
4967 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4968 sz
.x
+= childSize
.x
;
4969 descent
= wxMax(descent
, childDescent
);
4970 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
4972 child
->SetCachedSize(childSize
);
4973 child
->SetDescent(childDescent
);
4979 if (partialExtents
->GetCount() > 0)
4980 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4984 partialExtents
->Add(childSize
.x
+ lastSize
);
4987 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
4989 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4990 sz
.x
+= childSize
.x
;
4991 descent
= wxMax(descent
, childDescent
);
4993 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
4995 child
->SetCachedSize(childSize
);
4996 child
->SetDescent(childDescent
);
5002 if (partialExtents
->GetCount() > 0)
5003 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5008 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5010 partialExtents
->Add(childExtents
[i
] + lastSize
);
5020 node
= node
->GetNext();
5026 // Use formatted data, with line breaks
5029 // We're going to loop through each line, and then for each line,
5030 // call GetRangeSize for the fragment that comprises that line.
5031 // Only we have to do that multiple times within the line, because
5032 // the line may be broken into pieces. For now ignore line break commands
5033 // (so we can assume that getting the unformatted size for a fragment
5034 // within a line is the actual size)
5036 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5039 wxRichTextLine
* line
= node
->GetData();
5040 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5041 if (!lineRange
.IsOutside(range
))
5045 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5048 wxRichTextObject
* child
= node2
->GetData();
5050 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5052 wxRichTextRange rangeToUse
= lineRange
;
5053 rangeToUse
.LimitTo(child
->GetRange());
5054 if (child
->IsTopLevel())
5055 rangeToUse
= child
->GetOwnRange();
5058 int childDescent
= 0;
5059 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5061 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5062 lineSize
.x
+= childSize
.x
;
5064 descent
= wxMax(descent
, childDescent
);
5067 node2
= node2
->GetNext();
5070 // Increase size by a line (TODO: paragraph spacing)
5072 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5074 node
= node
->GetNext();
5081 /// Finds the absolute position and row height for the given character position
5082 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5086 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5088 *height
= line
->GetSize().y
;
5090 *height
= dc
.GetCharHeight();
5092 // -1 means 'the start of the buffer'.
5095 pt
= pt
+ line
->GetPosition();
5100 // The final position in a paragraph is taken to mean the position
5101 // at the start of the next paragraph.
5102 if (index
== GetRange().GetEnd())
5104 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5105 wxASSERT( parent
!= NULL
);
5107 // Find the height at the next paragraph, if any
5108 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5111 *height
= line
->GetSize().y
;
5112 pt
= line
->GetAbsolutePosition();
5116 *height
= dc
.GetCharHeight();
5117 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5118 pt
= wxPoint(indent
, GetCachedSize().y
);
5124 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5127 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5130 wxRichTextLine
* line
= node
->GetData();
5131 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5132 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5134 // If this is the last point in the line, and we're forcing the
5135 // returned value to be the start of the next line, do the required
5137 if (index
== lineRange
.GetEnd() && forceLineStart
)
5139 if (node
->GetNext())
5141 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5142 *height
= nextLine
->GetSize().y
;
5143 pt
= nextLine
->GetAbsolutePosition();
5148 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5150 wxRichTextRange
r(lineRange
.GetStart(), index
);
5154 // We find the size of the line up to this point,
5155 // then we can add this size to the line start position and
5156 // paragraph start position to find the actual position.
5158 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5160 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5161 *height
= line
->GetSize().y
;
5168 node
= node
->GetNext();
5174 /// Hit-testing: returns a flag indicating hit test details, plus
5175 /// information about position
5176 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5179 return wxRICHTEXT_HITTEST_NONE
;
5181 // If we're in the top-level container, then we can return
5182 // a suitable hit test code even if the point is outside the container area,
5183 // so that we can position the caret sensibly even if we don't
5184 // click on valid content. If we're not at the top-level, and the point
5185 // is not within this paragraph object, then we don't want to stop more
5186 // precise hit-testing from working prematurely, so return immediately.
5187 // NEW STRATEGY: use the parent boundary to test whether we're in the
5188 // right region, not the paragraph, since the paragraph may be positioned
5189 // some way in from where the user clicks.
5192 wxRichTextObject
* tempObj
, *tempContextObj
;
5193 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5194 return wxRICHTEXT_HITTEST_NONE
;
5197 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5200 wxRichTextObject
* child
= objNode
->GetData();
5201 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5204 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5205 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5210 objNode
= objNode
->GetNext();
5213 wxPoint paraPos
= GetPosition();
5215 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5218 wxRichTextLine
* line
= node
->GetData();
5219 wxPoint linePos
= paraPos
+ line
->GetPosition();
5220 wxSize lineSize
= line
->GetSize();
5221 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5223 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5225 if (pt
.x
< linePos
.x
)
5227 textPosition
= lineRange
.GetStart();
5228 *obj
= FindObjectAtPosition(textPosition
);
5229 *contextObj
= GetContainer();
5230 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5232 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5234 textPosition
= lineRange
.GetEnd();
5235 *obj
= FindObjectAtPosition(textPosition
);
5236 *contextObj
= GetContainer();
5237 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5241 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5242 wxArrayInt partialExtents
;
5247 // This calculates the partial text extents
5248 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5250 int lastX
= linePos
.x
;
5252 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5254 int nextX
= partialExtents
[i
] + linePos
.x
;
5256 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5258 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5260 *obj
= FindObjectAtPosition(textPosition
);
5261 *contextObj
= GetContainer();
5263 // So now we know it's between i-1 and i.
5264 // Let's see if we can be more precise about
5265 // which side of the position it's on.
5267 int midPoint
= (nextX
+ lastX
)/2;
5268 if (pt
.x
>= midPoint
)
5269 return wxRICHTEXT_HITTEST_AFTER
;
5271 return wxRICHTEXT_HITTEST_BEFORE
;
5278 int lastX
= linePos
.x
;
5279 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5284 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5286 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5288 int nextX
= childSize
.x
+ linePos
.x
;
5290 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5294 *obj
= FindObjectAtPosition(textPosition
);
5295 *contextObj
= GetContainer();
5297 // So now we know it's between i-1 and i.
5298 // Let's see if we can be more precise about
5299 // which side of the position it's on.
5301 int midPoint
= (nextX
+ lastX
)/2;
5302 if (pt
.x
>= midPoint
)
5303 return wxRICHTEXT_HITTEST_AFTER
;
5305 return wxRICHTEXT_HITTEST_BEFORE
;
5316 node
= node
->GetNext();
5319 return wxRICHTEXT_HITTEST_NONE
;
5322 /// Split an object at this position if necessary, and return
5323 /// the previous object, or NULL if inserting at beginning.
5324 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5326 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5329 wxRichTextObject
* child
= node
->GetData();
5331 if (pos
== child
->GetRange().GetStart())
5335 if (node
->GetPrevious())
5336 *previousObject
= node
->GetPrevious()->GetData();
5338 *previousObject
= NULL
;
5344 if (child
->GetRange().Contains(pos
))
5346 // This should create a new object, transferring part of
5347 // the content to the old object and the rest to the new object.
5348 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5350 // If we couldn't split this object, just insert in front of it.
5353 // Maybe this is an empty string, try the next one
5358 // Insert the new object after 'child'
5359 if (node
->GetNext())
5360 m_children
.Insert(node
->GetNext(), newObject
);
5362 m_children
.Append(newObject
);
5363 newObject
->SetParent(this);
5366 *previousObject
= child
;
5372 node
= node
->GetNext();
5375 *previousObject
= NULL
;
5379 /// Move content to a list from obj on
5380 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5382 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5385 wxRichTextObject
* child
= node
->GetData();
5388 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5390 node
= node
->GetNext();
5392 m_children
.DeleteNode(oldNode
);
5396 /// Add content back from list
5397 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5399 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5401 AppendChild((wxRichTextObject
*) node
->GetData());
5406 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5408 wxRichTextCompositeObject::CalculateRange(start
, end
);
5410 // Add one for end of paragraph
5413 m_range
.SetRange(start
, end
);
5416 /// Find the object at the given position
5417 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5419 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5422 wxRichTextObject
* obj
= node
->GetData();
5423 if (obj
->GetRange().Contains(position
) ||
5424 obj
->GetRange().GetStart() == position
||
5425 obj
->GetRange().GetEnd() == position
)
5428 node
= node
->GetNext();
5433 /// Get the plain text searching from the start or end of the range.
5434 /// The resulting string may be shorter than the range given.
5435 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5437 text
= wxEmptyString
;
5441 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5444 wxRichTextObject
* obj
= node
->GetData();
5445 if (!obj
->GetRange().IsOutside(range
))
5447 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5450 text
+= textObj
->GetTextForRange(range
);
5458 node
= node
->GetNext();
5463 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5466 wxRichTextObject
* obj
= node
->GetData();
5467 if (!obj
->GetRange().IsOutside(range
))
5469 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5472 text
= textObj
->GetTextForRange(range
) + text
;
5476 text
= wxT(" ") + text
;
5480 node
= node
->GetPrevious();
5487 /// Find a suitable wrap position.
5488 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5490 if (range
.GetLength() <= 0)
5493 // Find the first position where the line exceeds the available space.
5495 long breakPosition
= range
.GetEnd();
5497 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5498 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5502 if (range
.GetStart() > GetRange().GetStart())
5503 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5508 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5510 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5512 if (widthFromStartOfThisRange
> availableSpace
)
5514 breakPosition
= i
-1;
5522 // Binary chop for speed
5523 long minPos
= range
.GetStart();
5524 long maxPos
= range
.GetEnd();
5527 if (minPos
== maxPos
)
5530 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5532 if (sz
.x
> availableSpace
)
5533 breakPosition
= minPos
- 1;
5536 else if ((maxPos
- minPos
) == 1)
5539 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5541 if (sz
.x
> availableSpace
)
5542 breakPosition
= minPos
- 1;
5545 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5546 if (sz
.x
> availableSpace
)
5547 breakPosition
= maxPos
-1;
5553 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5556 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5558 if (sz
.x
> availableSpace
)
5570 // Now we know the last position on the line.
5571 // Let's try to find a word break.
5574 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5576 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5577 if (newLinePos
!= wxNOT_FOUND
)
5579 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5583 int spacePos
= plainText
.Find(wxT(' '), true);
5584 int tabPos
= plainText
.Find(wxT('\t'), true);
5585 int pos
= wxMax(spacePos
, tabPos
);
5586 if (pos
!= wxNOT_FOUND
)
5588 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5589 breakPosition
= breakPosition
- positionsFromEndOfString
;
5594 wrapPosition
= breakPosition
;
5599 /// Get the bullet text for this paragraph.
5600 wxString
wxRichTextParagraph::GetBulletText()
5602 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5603 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5604 return wxEmptyString
;
5606 int number
= GetAttributes().GetBulletNumber();
5609 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5611 text
.Printf(wxT("%d"), number
);
5613 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5615 // TODO: Unicode, and also check if number > 26
5616 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5618 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5620 // TODO: Unicode, and also check if number > 26
5621 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5623 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5625 text
= wxRichTextDecimalToRoman(number
);
5627 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5629 text
= wxRichTextDecimalToRoman(number
);
5632 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5634 text
= GetAttributes().GetBulletText();
5637 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5639 // The outline style relies on the text being computed statically,
5640 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5641 // should be stored in the attributes; if not, just use the number for this
5642 // level, as previously computed.
5643 if (!GetAttributes().GetBulletText().IsEmpty())
5644 text
= GetAttributes().GetBulletText();
5647 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5649 text
= wxT("(") + text
+ wxT(")");
5651 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5653 text
= text
+ wxT(")");
5656 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5664 /// Allocate or reuse a line object
5665 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5667 if (pos
< (int) m_cachedLines
.GetCount())
5669 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5675 wxRichTextLine
* line
= new wxRichTextLine(this);
5676 m_cachedLines
.Append(line
);
5681 /// Clear remaining unused line objects, if any
5682 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5684 int cachedLineCount
= m_cachedLines
.GetCount();
5685 if ((int) cachedLineCount
> lineCount
)
5687 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5689 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5690 wxRichTextLine
* line
= node
->GetData();
5691 m_cachedLines
.Erase(node
);
5698 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5699 /// retrieve the actual style.
5700 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5702 wxRichTextAttr attr
;
5703 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5706 attr
= buf
->GetBasicStyle();
5707 if (!includingBoxAttr
)
5709 attr
.GetTextBoxAttr().Reset();
5710 // The background colour will be painted by the container, and we don't
5711 // want to unnecessarily overwrite the background when we're drawing text
5712 // because this may erase the guideline (which appears just under the text
5713 // if there's no padding).
5714 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5716 wxRichTextApplyStyle(attr
, GetAttributes());
5719 attr
= GetAttributes();
5721 wxRichTextApplyStyle(attr
, contentStyle
);
5725 /// Get combined attributes of the base style and paragraph style.
5726 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5728 wxRichTextAttr attr
;
5729 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5732 attr
= buf
->GetBasicStyle();
5733 if (!includingBoxAttr
)
5734 attr
.GetTextBoxAttr().Reset();
5735 wxRichTextApplyStyle(attr
, GetAttributes());
5738 attr
= GetAttributes();
5743 // Create default tabstop array
5744 void wxRichTextParagraph::InitDefaultTabs()
5746 // create a default tab list at 10 mm each.
5747 for (int i
= 0; i
< 20; ++i
)
5749 sm_defaultTabs
.Add(i
*100);
5753 // Clear default tabstop array
5754 void wxRichTextParagraph::ClearDefaultTabs()
5756 sm_defaultTabs
.Clear();
5759 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5761 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5764 wxRichTextObject
* anchored
= node
->GetData();
5765 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5769 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5772 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5774 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5775 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5777 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5781 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5783 /* Update the offset */
5784 int newOffsetY
= pos
- rect
.y
;
5785 if (newOffsetY
!= offsetY
)
5787 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5788 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5789 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5792 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5794 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5795 x
= rect
.x
+ rect
.width
- size
.x
;
5797 anchored
->SetPosition(wxPoint(x
, pos
));
5798 anchored
->SetCachedSize(size
);
5799 floatCollector
->CollectFloat(this, anchored
);
5802 node
= node
->GetNext();
5806 // Get the first position from pos that has a line break character.
5807 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
5809 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5812 wxRichTextObject
* obj
= node
->GetData();
5813 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
5815 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5818 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
5823 node
= node
->GetNext();
5830 * This object represents a line in a paragraph, and stores
5831 * offsets from the start of the paragraph representing the
5832 * start and end positions of the line.
5835 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
5841 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
5844 m_range
.SetRange(-1, -1);
5845 m_pos
= wxPoint(0, 0);
5846 m_size
= wxSize(0, 0);
5848 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5849 m_objectSizes
.Clear();
5854 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
5856 m_range
= obj
.m_range
;
5857 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5858 m_objectSizes
= obj
.m_objectSizes
;
5862 /// Get the absolute object position
5863 wxPoint
wxRichTextLine::GetAbsolutePosition() const
5865 return m_parent
->GetPosition() + m_pos
;
5868 /// Get the absolute range
5869 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
5871 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
5872 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
5877 * wxRichTextPlainText
5878 * This object represents a single piece of text.
5881 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
5883 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
5884 wxRichTextObject(parent
)
5887 SetAttributes(*style
);
5892 #define USE_KERNING_FIX 1
5894 // If insufficient tabs are defined, this is the tab width used
5895 #define WIDTH_FOR_DEFAULT_TABS 50
5898 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
5900 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
5901 wxASSERT (para
!= NULL
);
5903 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5905 // Let's make the assumption for now that for content in a paragraph, including
5906 // text, we never have a discontinuous selection. So we only deal with a
5908 wxRichTextRange selectionRange
;
5909 if (selection
.IsValid())
5911 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
5912 if (selectionRanges
.GetCount() > 0)
5913 selectionRange
= selectionRanges
[0];
5915 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5918 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5920 int offset
= GetRange().GetStart();
5922 // Replace line break characters with spaces
5923 wxString str
= m_text
;
5924 wxString toRemove
= wxRichTextLineBreakChar
;
5925 str
.Replace(toRemove
, wxT(" "));
5926 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
5929 long len
= range
.GetLength();
5930 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
5932 // Test for the optimized situations where all is selected, or none
5935 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
5936 wxCheckSetFont(dc
, textFont
);
5937 int charHeight
= dc
.GetCharHeight();
5940 if ( textFont
.Ok() )
5942 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
5944 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5945 textFont
.SetPointSize( static_cast<int>(size
) );
5948 wxCheckSetFont(dc
, textFont
);
5950 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
5952 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5953 textFont
.SetPointSize( static_cast<int>(size
) );
5955 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
5956 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
5957 wxCheckSetFont(dc
, textFont
);
5962 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5968 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5971 // TODO: new selection code
5973 // (a) All selected.
5974 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
5976 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
5978 // (b) None selected.
5979 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
5981 // Draw all unselected
5982 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
5986 // (c) Part selected, part not
5987 // Let's draw unselected chunk, selected chunk, then unselected chunk.
5989 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
5991 // 1. Initial unselected chunk, if any, up until start of selection.
5992 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
5994 int r1
= range
.GetStart();
5995 int s1
= selectionRange
.GetStart()-1;
5996 int fragmentLen
= s1
- r1
+ 1;
5997 if (fragmentLen
< 0)
5999 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6001 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6003 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6006 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6008 // Compensate for kerning difference
6009 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6010 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6012 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6013 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6014 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6015 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6017 int kerningDiff
= (w1
+ w3
) - w2
;
6018 x
= x
- kerningDiff
;
6023 // 2. Selected chunk, if any.
6024 if (selectionRange
.GetEnd() >= range
.GetStart())
6026 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6027 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6029 int fragmentLen
= s2
- s1
+ 1;
6030 if (fragmentLen
< 0)
6032 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6034 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6036 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6039 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6041 // Compensate for kerning difference
6042 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6043 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6045 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6046 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6047 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6048 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6050 int kerningDiff
= (w1
+ w3
) - w2
;
6051 x
= x
- kerningDiff
;
6056 // 3. Remaining unselected chunk, if any
6057 if (selectionRange
.GetEnd() < range
.GetEnd())
6059 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6060 int r2
= range
.GetEnd();
6062 int fragmentLen
= r2
- s2
+ 1;
6063 if (fragmentLen
< 0)
6065 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6067 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6069 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6076 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6078 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6080 wxArrayInt tabArray
;
6084 if (attr
.GetTabs().IsEmpty())
6085 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6087 tabArray
= attr
.GetTabs();
6088 tabCount
= tabArray
.GetCount();
6090 for (int i
= 0; i
< tabCount
; ++i
)
6092 int pos
= tabArray
[i
];
6093 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6100 int nextTabPos
= -1;
6106 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6107 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6109 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6110 wxCheckSetPen(dc
, wxPen(highlightColour
));
6111 dc
.SetTextForeground(highlightTextColour
);
6112 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6116 dc
.SetTextForeground(attr
.GetTextColour());
6118 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6120 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6121 dc
.SetTextBackground(attr
.GetBackgroundColour());
6124 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6127 wxCoord x_orig
= GetParent()->GetPosition().x
;
6130 // the string has a tab
6131 // break up the string at the Tab
6132 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6133 str
= str
.AfterFirst(wxT('\t'));
6134 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6136 bool not_found
= true;
6137 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6139 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6141 // Find the next tab position.
6142 // Even if we're at the end of the tab array, we must still draw the chunk.
6144 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6146 if (nextTabPos
<= tabPos
)
6148 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6149 nextTabPos
= tabPos
+ defaultTabWidth
;
6156 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6157 dc
.DrawRectangle(selRect
);
6159 dc
.DrawText(stringChunk
, x
, y
);
6161 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6163 wxPen oldPen
= dc
.GetPen();
6164 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6165 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6166 wxCheckSetPen(dc
, oldPen
);
6172 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6177 dc
.GetTextExtent(str
, & w
, & h
);
6180 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6181 dc
.DrawRectangle(selRect
);
6183 dc
.DrawText(str
, x
, y
);
6185 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6187 wxPen oldPen
= dc
.GetPen();
6188 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6189 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6190 wxCheckSetPen(dc
, oldPen
);
6199 /// Lay the item out
6200 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
))
6202 // Only lay out if we haven't already cached the size
6204 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6206 // Eventually we want to have a reasonable estimate of minimum size.
6207 m_minSize
= wxSize(0, 0);
6212 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6214 wxRichTextObject::Copy(obj
);
6216 m_text
= obj
.m_text
;
6219 /// Get/set the object size for the given range. Returns false if the range
6220 /// is invalid for this object.
6221 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6223 if (!range
.IsWithin(GetRange()))
6226 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6227 wxASSERT (para
!= NULL
);
6229 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6231 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6233 // Always assume unformatted text, since at this level we have no knowledge
6234 // of line breaks - and we don't need it, since we'll calculate size within
6235 // formatted text by doing it in chunks according to the line ranges
6237 bool bScript(false);
6238 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6241 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6242 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6244 wxFont textFont
= font
;
6245 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6246 textFont
.SetPointSize( static_cast<int>(size
) );
6247 wxCheckSetFont(dc
, textFont
);
6252 wxCheckSetFont(dc
, font
);
6256 bool haveDescent
= false;
6257 int startPos
= range
.GetStart() - GetRange().GetStart();
6258 long len
= range
.GetLength();
6260 wxString
str(m_text
);
6261 wxString toReplace
= wxRichTextLineBreakChar
;
6262 str
.Replace(toReplace
, wxT(" "));
6264 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6266 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6267 stringChunk
.MakeUpper();
6271 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6273 // the string has a tab
6274 wxArrayInt tabArray
;
6275 if (textAttr
.GetTabs().IsEmpty())
6276 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6278 tabArray
= textAttr
.GetTabs();
6280 int tabCount
= tabArray
.GetCount();
6282 for (int i
= 0; i
< tabCount
; ++i
)
6284 int pos
= tabArray
[i
];
6285 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6289 int nextTabPos
= -1;
6291 while (stringChunk
.Find(wxT('\t')) >= 0)
6293 int absoluteWidth
= 0;
6295 // the string has a tab
6296 // break up the string at the Tab
6297 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6298 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6303 if (partialExtents
->GetCount() > 0)
6304 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6308 // Add these partial extents
6310 dc
.GetPartialTextExtents(stringFragment
, p
);
6312 for (j
= 0; j
< p
.GetCount(); j
++)
6313 partialExtents
->Add(oldWidth
+ p
[j
]);
6315 if (partialExtents
->GetCount() > 0)
6316 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6318 absoluteWidth
= relativeX
;
6322 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6324 absoluteWidth
= width
+ relativeX
;
6328 bool notFound
= true;
6329 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6331 nextTabPos
= tabArray
.Item(i
);
6333 // Find the next tab position.
6334 // Even if we're at the end of the tab array, we must still process the chunk.
6336 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6338 if (nextTabPos
<= absoluteWidth
)
6340 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6341 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6345 width
= nextTabPos
- relativeX
;
6348 partialExtents
->Add(width
);
6354 if (!stringChunk
.IsEmpty())
6359 if (partialExtents
->GetCount() > 0)
6360 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6364 // Add these partial extents
6366 dc
.GetPartialTextExtents(stringChunk
, p
);
6368 for (j
= 0; j
< p
.GetCount(); j
++)
6369 partialExtents
->Add(oldWidth
+ p
[j
]);
6373 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6381 int charHeight
= dc
.GetCharHeight();
6382 if ((*partialExtents
).GetCount() > 0)
6383 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6386 size
= wxSize(w
, charHeight
);
6390 size
= wxSize(width
, dc
.GetCharHeight());
6394 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6402 /// Do a split, returning an object containing the second part, and setting
6403 /// the first part in 'this'.
6404 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6406 long index
= pos
- GetRange().GetStart();
6408 if (index
< 0 || index
>= (int) m_text
.length())
6411 wxString firstPart
= m_text
.Mid(0, index
);
6412 wxString secondPart
= m_text
.Mid(index
);
6416 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6417 newObject
->SetAttributes(GetAttributes());
6419 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6420 GetRange().SetEnd(pos
-1);
6426 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6428 end
= start
+ m_text
.length() - 1;
6429 m_range
.SetRange(start
, end
);
6433 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6435 wxRichTextRange r
= range
;
6437 r
.LimitTo(GetRange());
6439 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6445 long startIndex
= r
.GetStart() - GetRange().GetStart();
6446 long len
= r
.GetLength();
6448 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6452 /// Get text for the given range.
6453 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6455 wxRichTextRange r
= range
;
6457 r
.LimitTo(GetRange());
6459 long startIndex
= r
.GetStart() - GetRange().GetStart();
6460 long len
= r
.GetLength();
6462 return m_text
.Mid(startIndex
, len
);
6465 /// Returns true if this object can merge itself with the given one.
6466 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6468 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6469 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6472 /// Returns true if this object merged itself with the given one.
6473 /// The calling code will then delete the given object.
6474 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6476 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6477 wxASSERT( textObject
!= NULL
);
6481 m_text
+= textObject
->GetText();
6482 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6489 /// Dump to output stream for debugging
6490 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6492 wxRichTextObject::Dump(stream
);
6493 stream
<< m_text
<< wxT("\n");
6496 /// Get the first position from pos that has a line break character.
6497 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6500 int len
= m_text
.length();
6501 int startPos
= pos
- m_range
.GetStart();
6502 for (i
= startPos
; i
< len
; i
++)
6504 wxChar ch
= m_text
[i
];
6505 if (ch
== wxRichTextLineBreakChar
)
6507 return i
+ m_range
.GetStart();
6515 * This is a kind of box, used to represent the whole buffer
6518 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6520 wxList
wxRichTextBuffer::sm_handlers
;
6521 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6522 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6523 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6526 void wxRichTextBuffer::Init()
6528 m_commandProcessor
= new wxCommandProcessor
;
6529 m_styleSheet
= NULL
;
6531 m_batchedCommandDepth
= 0;
6532 m_batchedCommand
= NULL
;
6539 wxRichTextBuffer::~wxRichTextBuffer()
6541 delete m_commandProcessor
;
6542 delete m_batchedCommand
;
6545 ClearEventHandlers();
6548 void wxRichTextBuffer::ResetAndClearCommands()
6552 GetCommandProcessor()->ClearCommands();
6555 Invalidate(wxRICHTEXT_ALL
);
6558 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6560 wxRichTextParagraphLayoutBox::Copy(obj
);
6562 m_styleSheet
= obj
.m_styleSheet
;
6563 m_modified
= obj
.m_modified
;
6564 m_batchedCommandDepth
= 0;
6565 if (m_batchedCommand
)
6566 delete m_batchedCommand
;
6567 m_batchedCommand
= NULL
;
6568 m_suppressUndo
= obj
.m_suppressUndo
;
6569 m_invalidRange
= obj
.m_invalidRange
;
6572 /// Push style sheet to top of stack
6573 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6576 styleSheet
->InsertSheet(m_styleSheet
);
6578 SetStyleSheet(styleSheet
);
6583 /// Pop style sheet from top of stack
6584 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6588 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6589 m_styleSheet
= oldSheet
->GetNextSheet();
6598 /// Submit command to insert paragraphs
6599 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6601 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(pos
, paragraphs
, ctrl
, this, flags
);
6604 /// Submit command to insert paragraphs
6605 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6607 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6609 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6611 wxRichTextAttr
* p
= NULL
;
6612 wxRichTextAttr paraAttr
;
6613 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6615 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6616 if (!paraAttr
.IsDefault())
6622 action
->GetNewParagraphs() = paragraphs
;
6624 if (p
&& !p
->IsDefault())
6626 for (wxRichTextObjectList::compatibility_iterator node
= action
->GetNewParagraphs().GetChildren().GetFirst(); node
; node
= node
->GetNext())
6628 wxRichTextObject
* child
= node
->GetData();
6629 child
->SetAttributes(*p
);
6633 action
->SetPosition(pos
);
6635 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6636 if (!paragraphs
.GetPartialParagraph())
6637 range
.SetEnd(range
.GetEnd()+1);
6639 // Set the range we'll need to delete in Undo
6640 action
->SetRange(range
);
6642 buffer
->SubmitAction(action
);
6647 /// Submit command to insert the given text
6648 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6650 return ctrl
->GetFocusObject()->InsertTextWithUndo(pos
, text
, ctrl
, this, flags
);
6653 /// Submit command to insert the given text
6654 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6656 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6658 wxRichTextAttr
* p
= NULL
;
6659 wxRichTextAttr paraAttr
;
6660 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6662 // Get appropriate paragraph style
6663 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6664 if (!paraAttr
.IsDefault())
6668 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6670 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6672 if (!text
.empty() && text
.Last() != wxT('\n'))
6674 // Don't count the newline when undoing
6676 action
->GetNewParagraphs().SetPartialParagraph(true);
6678 else if (!text
.empty() && text
.Last() == wxT('\n'))
6681 action
->SetPosition(pos
);
6683 // Set the range we'll need to delete in Undo
6684 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6686 buffer
->SubmitAction(action
);
6691 /// Submit command to insert the given text
6692 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6694 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(pos
, ctrl
, this, flags
);
6697 /// Submit command to insert the given text
6698 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6700 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6702 wxRichTextAttr
* p
= NULL
;
6703 wxRichTextAttr paraAttr
;
6704 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6706 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6707 if (!paraAttr
.IsDefault())
6711 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6713 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6714 action
->GetNewParagraphs().AppendChild(newPara
);
6715 action
->GetNewParagraphs().UpdateRanges();
6716 action
->GetNewParagraphs().SetPartialParagraph(false);
6717 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6721 newPara
->SetAttributes(*p
);
6723 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6725 if (para
&& para
->GetRange().GetEnd() == pos
)
6728 // Now see if we need to number the paragraph.
6729 if (newPara
->GetAttributes().HasBulletNumber())
6731 wxRichTextAttr numberingAttr
;
6732 if (FindNextParagraphNumber(para
, numberingAttr
))
6733 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6737 action
->SetPosition(pos
);
6739 // Use the default character style
6740 // Use the default character style
6741 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6743 // Check whether the default style merely reflects the paragraph/basic style,
6744 // in which case don't apply it.
6745 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6746 wxRichTextAttr toApply
;
6749 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6750 wxRichTextAttr newAttr
;
6751 // This filters out attributes that are accounted for by the current
6752 // paragraph/basic style
6753 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6756 toApply
= defaultStyle
;
6758 if (!toApply
.IsDefault())
6759 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6762 // Set the range we'll need to delete in Undo
6763 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6765 buffer
->SubmitAction(action
);
6770 /// Submit command to insert the given image
6771 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6772 const wxRichTextAttr
& textAttr
)
6774 return ctrl
->GetFocusObject()->InsertImageWithUndo(pos
, imageBlock
, ctrl
, this, flags
, textAttr
);
6777 /// Submit command to insert the given image
6778 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
,
6779 wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
,
6780 const wxRichTextAttr
& textAttr
)
6782 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6784 wxRichTextAttr
* p
= NULL
;
6785 wxRichTextAttr paraAttr
;
6786 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6788 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6789 if (!paraAttr
.IsDefault())
6793 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6795 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6797 newPara
->SetAttributes(*p
);
6799 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6800 newPara
->AppendChild(imageObject
);
6801 imageObject
->SetAttributes(textAttr
);
6802 action
->GetNewParagraphs().AppendChild(newPara
);
6803 action
->GetNewParagraphs().UpdateRanges();
6805 action
->GetNewParagraphs().SetPartialParagraph(true);
6807 action
->SetPosition(pos
);
6809 // Set the range we'll need to delete in Undo
6810 action
->SetRange(wxRichTextRange(pos
, pos
));
6812 buffer
->SubmitAction(action
);
6817 // Insert an object with no change of it
6818 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6820 return ctrl
->GetFocusObject()->InsertObjectWithUndo(pos
, object
, ctrl
, this, flags
);
6823 // Insert an object with no change of it
6824 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6826 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6828 wxRichTextAttr
* p
= NULL
;
6829 wxRichTextAttr paraAttr
;
6830 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6832 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6833 if (!paraAttr
.IsDefault())
6837 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6839 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6841 newPara
->SetAttributes(*p
);
6843 newPara
->AppendChild(object
);
6844 action
->GetNewParagraphs().AppendChild(newPara
);
6845 action
->GetNewParagraphs().UpdateRanges();
6847 action
->GetNewParagraphs().SetPartialParagraph(true);
6849 action
->SetPosition(pos
);
6851 // Set the range we'll need to delete in Undo
6852 action
->SetRange(wxRichTextRange(pos
, pos
));
6854 buffer
->SubmitAction(action
);
6856 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
6860 /// Get the style that is appropriate for a new paragraph at this position.
6861 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6863 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
6865 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
6868 wxRichTextAttr attr
;
6869 bool foundAttributes
= false;
6871 // Look for a matching paragraph style
6872 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
6874 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
6877 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6878 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
6880 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
6883 foundAttributes
= true;
6884 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6888 // If we didn't find the 'next style', use this style instead.
6889 if (!foundAttributes
)
6891 foundAttributes
= true;
6892 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6897 // Also apply list style if present
6898 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
6900 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
6903 int thisIndent
= para
->GetAttributes().GetLeftIndent();
6904 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
6906 // Apply the overall list style, and item style for this level
6907 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
6908 wxRichTextApplyStyle(attr
, listStyle
);
6909 attr
.SetOutlineLevel(thisLevel
);
6910 if (para
->GetAttributes().HasBulletNumber())
6911 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
6915 if (!foundAttributes
)
6917 attr
= para
->GetAttributes();
6918 int flags
= attr
.GetFlags();
6920 // Eliminate character styles
6921 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
6922 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
6923 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
6924 attr
.SetFlags(flags
);
6930 return wxRichTextAttr();
6933 /// Submit command to delete this range
6934 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
6936 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
6939 /// Submit command to delete this range
6940 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
6942 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
6944 action
->SetPosition(ctrl
->GetCaretPosition());
6946 // Set the range to delete
6947 action
->SetRange(range
);
6949 // Copy the fragment that we'll need to restore in Undo
6950 CopyFragment(range
, action
->GetOldParagraphs());
6952 // See if we're deleting a paragraph marker, in which case we need to
6953 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6954 if (range
.GetStart() == range
.GetEnd())
6956 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
6957 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
6959 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
6960 if (nextPara
&& nextPara
!= para
)
6962 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
6963 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
6968 buffer
->SubmitAction(action
);
6973 /// Collapse undo/redo commands
6974 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
6976 if (m_batchedCommandDepth
== 0)
6978 wxASSERT(m_batchedCommand
== NULL
);
6979 if (m_batchedCommand
)
6981 GetCommandProcessor()->Store(m_batchedCommand
);
6983 m_batchedCommand
= new wxRichTextCommand(cmdName
);
6986 m_batchedCommandDepth
++;
6991 /// Collapse undo/redo commands
6992 bool wxRichTextBuffer::EndBatchUndo()
6994 m_batchedCommandDepth
--;
6996 wxASSERT(m_batchedCommandDepth
>= 0);
6997 wxASSERT(m_batchedCommand
!= NULL
);
6999 if (m_batchedCommandDepth
== 0)
7001 GetCommandProcessor()->Store(m_batchedCommand
);
7002 m_batchedCommand
= NULL
;
7008 /// Submit immediately, or delay according to whether collapsing is on
7009 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7011 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7013 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7014 cmd
->AddAction(action
);
7016 cmd
->GetActions().Clear();
7019 m_batchedCommand
->AddAction(action
);
7023 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7024 cmd
->AddAction(action
);
7026 // Only store it if we're not suppressing undo.
7027 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7033 /// Begin suppressing undo/redo commands.
7034 bool wxRichTextBuffer::BeginSuppressUndo()
7041 /// End suppressing undo/redo commands.
7042 bool wxRichTextBuffer::EndSuppressUndo()
7049 /// Begin using a style
7050 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7052 wxRichTextAttr
newStyle(GetDefaultStyle());
7054 // Save the old default style
7055 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7057 wxRichTextApplyStyle(newStyle
, style
);
7058 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7060 SetDefaultStyle(newStyle
);
7066 bool wxRichTextBuffer::EndStyle()
7068 if (!m_attributeStack
.GetFirst())
7070 wxLogDebug(_("Too many EndStyle calls!"));
7074 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7075 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7076 m_attributeStack
.Erase(node
);
7078 SetDefaultStyle(*attr
);
7085 bool wxRichTextBuffer::EndAllStyles()
7087 while (m_attributeStack
.GetCount() != 0)
7092 /// Clear the style stack
7093 void wxRichTextBuffer::ClearStyleStack()
7095 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7096 delete (wxRichTextAttr
*) node
->GetData();
7097 m_attributeStack
.Clear();
7100 /// Begin using bold
7101 bool wxRichTextBuffer::BeginBold()
7103 wxRichTextAttr attr
;
7104 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7106 return BeginStyle(attr
);
7109 /// Begin using italic
7110 bool wxRichTextBuffer::BeginItalic()
7112 wxRichTextAttr attr
;
7113 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7115 return BeginStyle(attr
);
7118 /// Begin using underline
7119 bool wxRichTextBuffer::BeginUnderline()
7121 wxRichTextAttr attr
;
7122 attr
.SetFontUnderlined(true);
7124 return BeginStyle(attr
);
7127 /// Begin using point size
7128 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7130 wxRichTextAttr attr
;
7131 attr
.SetFontSize(pointSize
);
7133 return BeginStyle(attr
);
7136 /// Begin using this font
7137 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7139 wxRichTextAttr attr
;
7142 return BeginStyle(attr
);
7145 /// Begin using this colour
7146 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7148 wxRichTextAttr attr
;
7149 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7150 attr
.SetTextColour(colour
);
7152 return BeginStyle(attr
);
7155 /// Begin using alignment
7156 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7158 wxRichTextAttr attr
;
7159 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7160 attr
.SetAlignment(alignment
);
7162 return BeginStyle(attr
);
7165 /// Begin left indent
7166 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7168 wxRichTextAttr attr
;
7169 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7170 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7172 return BeginStyle(attr
);
7175 /// Begin right indent
7176 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7178 wxRichTextAttr attr
;
7179 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7180 attr
.SetRightIndent(rightIndent
);
7182 return BeginStyle(attr
);
7185 /// Begin paragraph spacing
7186 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7190 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7192 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7194 wxRichTextAttr attr
;
7195 attr
.SetFlags(flags
);
7196 attr
.SetParagraphSpacingBefore(before
);
7197 attr
.SetParagraphSpacingAfter(after
);
7199 return BeginStyle(attr
);
7202 /// Begin line spacing
7203 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7205 wxRichTextAttr attr
;
7206 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7207 attr
.SetLineSpacing(lineSpacing
);
7209 return BeginStyle(attr
);
7212 /// Begin numbered bullet
7213 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7215 wxRichTextAttr attr
;
7216 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7217 attr
.SetBulletStyle(bulletStyle
);
7218 attr
.SetBulletNumber(bulletNumber
);
7219 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7221 return BeginStyle(attr
);
7224 /// Begin symbol bullet
7225 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7227 wxRichTextAttr attr
;
7228 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7229 attr
.SetBulletStyle(bulletStyle
);
7230 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7231 attr
.SetBulletText(symbol
);
7233 return BeginStyle(attr
);
7236 /// Begin standard bullet
7237 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7239 wxRichTextAttr attr
;
7240 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7241 attr
.SetBulletStyle(bulletStyle
);
7242 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7243 attr
.SetBulletName(bulletName
);
7245 return BeginStyle(attr
);
7248 /// Begin named character style
7249 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7251 if (GetStyleSheet())
7253 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7256 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7257 return BeginStyle(attr
);
7263 /// Begin named paragraph style
7264 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7266 if (GetStyleSheet())
7268 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7271 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7272 return BeginStyle(attr
);
7278 /// Begin named list style
7279 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7281 if (GetStyleSheet())
7283 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7286 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7288 attr
.SetBulletNumber(number
);
7290 return BeginStyle(attr
);
7297 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7299 wxRichTextAttr attr
;
7301 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7303 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7306 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7311 return BeginStyle(attr
);
7314 /// Adds a handler to the end
7315 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7317 sm_handlers
.Append(handler
);
7320 /// Inserts a handler at the front
7321 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7323 sm_handlers
.Insert( handler
);
7326 /// Removes a handler
7327 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7329 wxRichTextFileHandler
*handler
= FindHandler(name
);
7332 sm_handlers
.DeleteObject(handler
);
7340 /// Finds a handler by filename or, if supplied, type
7341 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7342 wxRichTextFileType imageType
)
7344 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7345 return FindHandler(imageType
);
7346 else if (!filename
.IsEmpty())
7348 wxString path
, file
, ext
;
7349 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7350 return FindHandler(ext
, imageType
);
7357 /// Finds a handler by name
7358 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7360 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7363 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7364 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7366 node
= node
->GetNext();
7371 /// Finds a handler by extension and type
7372 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7374 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7377 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7378 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7379 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7381 node
= node
->GetNext();
7386 /// Finds a handler by type
7387 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7389 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7392 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7393 if (handler
->GetType() == type
) return handler
;
7394 node
= node
->GetNext();
7399 void wxRichTextBuffer::InitStandardHandlers()
7401 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7402 AddHandler(new wxRichTextPlainTextHandler
);
7405 void wxRichTextBuffer::CleanUpHandlers()
7407 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7410 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7411 wxList::compatibility_iterator next
= node
->GetNext();
7416 sm_handlers
.Clear();
7419 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7426 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7430 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7431 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7436 wildcard
+= wxT(";");
7437 wildcard
+= wxT("*.") + handler
->GetExtension();
7442 wildcard
+= wxT("|");
7443 wildcard
+= handler
->GetName();
7444 wildcard
+= wxT(" ");
7445 wildcard
+= _("files");
7446 wildcard
+= wxT(" (*.");
7447 wildcard
+= handler
->GetExtension();
7448 wildcard
+= wxT(")|*.");
7449 wildcard
+= handler
->GetExtension();
7451 types
->Add(handler
->GetType());
7456 node
= node
->GetNext();
7460 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7465 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7467 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7470 SetDefaultStyle(wxRichTextAttr());
7471 handler
->SetFlags(GetHandlerFlags());
7472 bool success
= handler
->LoadFile(this, filename
);
7473 Invalidate(wxRICHTEXT_ALL
);
7481 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7483 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7486 handler
->SetFlags(GetHandlerFlags());
7487 return handler
->SaveFile(this, filename
);
7493 /// Load from a stream
7494 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7496 wxRichTextFileHandler
* handler
= FindHandler(type
);
7499 SetDefaultStyle(wxRichTextAttr());
7500 handler
->SetFlags(GetHandlerFlags());
7501 bool success
= handler
->LoadFile(this, stream
);
7502 Invalidate(wxRICHTEXT_ALL
);
7509 /// Save to a stream
7510 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7512 wxRichTextFileHandler
* handler
= FindHandler(type
);
7515 handler
->SetFlags(GetHandlerFlags());
7516 return handler
->SaveFile(this, stream
);
7522 /// Copy the range to the clipboard
7523 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7525 bool success
= false;
7526 wxRichTextParagraphLayoutBox
* container
= this;
7527 if (GetRichTextCtrl())
7528 container
= GetRichTextCtrl()->GetFocusObject();
7530 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7532 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7534 wxTheClipboard
->Clear();
7536 // Add composite object
7538 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7541 wxString text
= container
->GetTextForRange(range
);
7544 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7547 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7550 // Add rich text buffer data object. This needs the XML handler to be present.
7552 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7554 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7555 container
->CopyFragment(range
, *richTextBuf
);
7557 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7560 if (wxTheClipboard
->SetData(compositeObject
))
7563 wxTheClipboard
->Close();
7572 /// Paste the clipboard content to the buffer
7573 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7575 bool success
= false;
7576 wxRichTextParagraphLayoutBox
* container
= this;
7577 if (GetRichTextCtrl())
7578 container
= GetRichTextCtrl()->GetFocusObject();
7580 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7581 if (CanPasteFromClipboard())
7583 if (wxTheClipboard
->Open())
7585 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7587 wxRichTextBufferDataObject data
;
7588 wxTheClipboard
->GetData(data
);
7589 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7592 container
->InsertParagraphsWithUndo(position
+1, *richTextBuffer
, GetRichTextCtrl(), this, 0);
7593 if (GetRichTextCtrl())
7594 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7595 delete richTextBuffer
;
7598 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7600 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7604 wxTextDataObject data
;
7605 wxTheClipboard
->GetData(data
);
7606 wxString
text(data
.GetText());
7609 text2
.Alloc(text
.Length()+1);
7611 for (i
= 0; i
< text
.Length(); i
++)
7613 wxChar ch
= text
[i
];
7614 if (ch
!= wxT('\r'))
7618 wxString text2
= text
;
7620 container
->InsertTextWithUndo(position
+1, text2
, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7622 if (GetRichTextCtrl())
7623 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7627 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7629 wxBitmapDataObject data
;
7630 wxTheClipboard
->GetData(data
);
7631 wxBitmap
bitmap(data
.GetBitmap());
7632 wxImage
image(bitmap
.ConvertToImage());
7634 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7636 action
->GetNewParagraphs().AddImage(image
);
7638 if (action
->GetNewParagraphs().GetChildCount() == 1)
7639 action
->GetNewParagraphs().SetPartialParagraph(true);
7641 action
->SetPosition(position
+1);
7643 // Set the range we'll need to delete in Undo
7644 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7646 SubmitAction(action
);
7650 wxTheClipboard
->Close();
7654 wxUnusedVar(position
);
7659 /// Can we paste from the clipboard?
7660 bool wxRichTextBuffer::CanPasteFromClipboard() const
7662 bool canPaste
= false;
7663 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7664 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7666 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7668 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7670 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7671 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7675 wxTheClipboard
->Close();
7681 /// Dumps contents of buffer for debugging purposes
7682 void wxRichTextBuffer::Dump()
7686 wxStringOutputStream
stream(& text
);
7687 wxTextOutputStream
textStream(stream
);
7694 /// Add an event handler
7695 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7697 m_eventHandlers
.Append(handler
);
7701 /// Remove an event handler
7702 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7704 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7707 m_eventHandlers
.Erase(node
);
7717 /// Clear event handlers
7718 void wxRichTextBuffer::ClearEventHandlers()
7720 m_eventHandlers
.Clear();
7723 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7724 /// otherwise will stop at the first successful one.
7725 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7727 bool success
= false;
7728 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7730 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7731 if (handler
->ProcessEvent(event
))
7741 /// Set style sheet and notify of the change
7742 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7744 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7746 wxWindowID id
= wxID_ANY
;
7747 if (GetRichTextCtrl())
7748 id
= GetRichTextCtrl()->GetId();
7750 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, id
);
7751 event
.SetEventObject(GetRichTextCtrl());
7752 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7753 event
.SetOldStyleSheet(oldSheet
);
7754 event
.SetNewStyleSheet(sheet
);
7757 if (SendEvent(event
) && !event
.IsAllowed())
7759 if (sheet
!= oldSheet
)
7765 if (oldSheet
&& oldSheet
!= sheet
)
7768 SetStyleSheet(sheet
);
7770 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7771 event
.SetOldStyleSheet(NULL
);
7774 return SendEvent(event
);
7777 /// Set renderer, deleting old one
7778 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7782 sm_renderer
= renderer
;
7785 /// Hit-testing: returns a flag indicating hit test details, plus
7786 /// information about position
7787 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7789 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7790 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7796 textPosition
= m_ownRange
.GetEnd()-1;
7799 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7803 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7805 if (bulletAttr
.GetTextColour().Ok())
7807 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7808 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7812 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7813 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7817 if (bulletAttr
.HasFont())
7819 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
7822 font
= (*wxNORMAL_FONT
);
7824 wxCheckSetFont(dc
, font
);
7826 int charHeight
= dc
.GetCharHeight();
7828 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
7829 int bulletHeight
= bulletWidth
;
7833 // Calculate the top position of the character (as opposed to the whole line height)
7834 int y
= rect
.y
+ (rect
.height
- charHeight
);
7836 // Calculate where the bullet should be positioned
7837 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
7839 // The margin between a bullet and text.
7840 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7842 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7843 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
7844 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7845 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
7847 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
7849 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
7851 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
7854 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
7855 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
7856 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
7857 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
7859 dc
.DrawPolygon(4, pts
);
7861 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
7864 pts
[0].x
= x
; pts
[0].y
= y
;
7865 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
7866 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
7868 dc
.DrawPolygon(3, pts
);
7870 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
7872 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
7873 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7875 else // "standard/circle", and catch-all
7877 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7883 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
7888 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
7890 wxRichTextAttr fontAttr
;
7891 fontAttr
.SetFontSize(attr
.GetFontSize());
7892 fontAttr
.SetFontStyle(attr
.GetFontStyle());
7893 fontAttr
.SetFontWeight(attr
.GetFontWeight());
7894 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
7895 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
7896 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
7898 else if (attr
.HasFont())
7899 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
7901 font
= (*wxNORMAL_FONT
);
7903 wxCheckSetFont(dc
, font
);
7905 if (attr
.GetTextColour().Ok())
7906 dc
.SetTextForeground(attr
.GetTextColour());
7908 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
7910 int charHeight
= dc
.GetCharHeight();
7912 dc
.GetTextExtent(text
, & tw
, & th
);
7916 // Calculate the top position of the character (as opposed to the whole line height)
7917 int y
= rect
.y
+ (rect
.height
- charHeight
);
7919 // The margin between a bullet and text.
7920 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7922 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7923 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
7924 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7925 x
= x
+ (rect
.width
)/2 - tw
/2;
7927 dc
.DrawText(text
, x
, y
);
7935 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
7937 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7938 // with the buffer. The store will allow retrieval from memory, disk or other means.
7942 /// Enumerate the standard bullet names currently supported
7943 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
7945 bulletNames
.Add(wxTRANSLATE("standard/circle"));
7946 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
7947 bulletNames
.Add(wxTRANSLATE("standard/square"));
7948 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
7949 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
7958 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
7960 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
7961 wxRichTextParagraphLayoutBox(parent
)
7966 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
7971 // TODO: if the active object in the control, draw an indication.
7972 // We need to add the concept of active object, and not just focus object,
7973 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
7974 // Ultimately we would like to be able to interactively resize an active object
7975 // using drag handles.
7976 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
7980 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
7982 wxRichTextParagraphLayoutBox::Copy(obj
);
7985 // Edit properties via a GUI
7986 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
7988 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
7989 boxDlg
.SetAttributes(GetAttributes());
7991 if (boxDlg
.ShowModal() == wxID_OK
)
7993 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
7994 // indeterminate in the object.
7995 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8002 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8004 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8005 wxRichTextBox(parent
)
8010 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8012 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8016 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8018 wxRichTextBox::Copy(obj
);
8021 // Edit properties via a GUI
8022 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8024 // We need to gather common attributes for all selected cells.
8026 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8027 bool multipleCells
= false;
8028 wxRichTextAttr attr
;
8030 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8031 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8033 wxRichTextAttr clashingAttr
, absentAttr
;
8034 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8036 int selectedCellCount
= 0;
8037 for (i
= 0; i
< sel
.GetCount(); i
++)
8039 const wxRichTextRange
& range
= sel
[i
];
8040 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8043 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8045 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8047 selectedCellCount
++;
8050 multipleCells
= selectedCellCount
> 1;
8054 attr
= GetAttributes();
8059 caption
= _("Multiple Cell Properties");
8061 caption
= _("Cell Properties");
8063 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8064 cellDlg
.SetAttributes(attr
);
8066 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8069 // We don't want position and floating controls for a cell.
8070 sizePage
->ShowPositionControls(false);
8071 sizePage
->ShowFloatingControls(false);
8074 if (cellDlg
.ShowModal() == wxID_OK
)
8078 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8079 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8080 // since it may represent clashing attributes across multiple objects.
8081 table
->SetCellStyle(sel
, attr
);
8084 // For a single object, indeterminate attributes set by the user should be reflected in the
8085 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8086 // the style directly instead of applying (which ignores indeterminate attributes,
8087 // leaving them as they were).
8088 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8095 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8097 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8099 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8105 // Draws the object.
8106 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8108 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8111 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8112 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8114 // Lays the object out. rect is the space available for layout. Often it will
8115 // be the specified overall space for this object, if trying to constrain
8116 // layout to a particular size, or it could be the total space available in the
8117 // parent. rect is the overall size, so we must subtract margins and padding.
8118 // to get the actual available space.
8119 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
8121 SetPosition(rect
.GetPosition());
8123 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8124 // minimum size if within alloted size, then divide up remaining size
8125 // between rows/cols.
8128 wxRichTextBuffer
* buffer
= GetBuffer();
8129 if (buffer
) scale
= buffer
->GetScale();
8131 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8132 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8134 // If we have no fixed table size, and assuming we're not pushed for
8135 // space, then we don't have to try to stretch the table to fit the contents.
8136 bool stretchToFitTableWidth
= false;
8138 int tableWidth
= rect
.width
;
8139 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8141 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8143 // Fixed table width, so we do want to stretch columns out if necessary.
8144 stretchToFitTableWidth
= true;
8146 // Shouldn't be able to exceed the size passed to this function
8147 tableWidth
= wxMin(rect
.width
, tableWidth
);
8150 // Get internal padding
8151 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
8152 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8153 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8154 if (GetAttributes().GetTextBoxAttr().GetPadding().GetRight().IsValid())
8155 paddingRight
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetRight());
8156 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8157 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8158 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8159 paddingBottom
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetBottom());
8161 // Assume that left and top padding are also used for inter-cell padding.
8162 int paddingX
= paddingLeft
;
8163 int paddingY
= paddingTop
;
8165 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8166 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8168 // Internal table width - the area for content
8169 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8171 int rowCount
= m_cells
.GetCount();
8172 if (m_colCount
== 0 || rowCount
== 0)
8174 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8175 SetCachedSize(overallRect
.GetSize());
8177 // Zero content size
8178 SetMinSize(overallRect
.GetSize());
8179 SetMaxSize(GetMinSize());
8183 // The final calculated widths
8184 wxArrayInt
colWidths(m_colCount
);
8186 wxArrayInt
absoluteColWidths(m_colCount
);
8187 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8188 wxArrayInt
percentageColWidths(m_colCount
);
8189 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8190 // These are only relevant when the first column contains spanning information.
8191 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8192 wxArrayInt
maxColWidths(m_colCount
);
8193 wxArrayInt
minColWidths(m_colCount
);
8195 wxSize
tableSize(tableWidth
, 0);
8199 for (i
= 0; i
< m_colCount
; i
++)
8201 absoluteColWidths
[i
] = 0;
8202 // absoluteColWidthsSpanning[i] = 0;
8203 percentageColWidths
[i
] = -1;
8204 // percentageColWidthsSpanning[i] = -1;
8206 maxColWidths
[i
] = 0;
8207 minColWidths
[i
] = 0;
8208 // columnSpans[i] = 1;
8211 // (0) Determine which cells are visible according to spans
8213 // __________________
8218 // |------------------|
8219 // |__________________| 4
8221 // To calculate cell visibility:
8222 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8223 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8224 // that cell, hide the cell.
8226 // We can also use this array to match the size of spanning cells to the grid. Or just do
8227 // this when we iterate through all cells.
8229 // 0.1: add spanning cells to an array
8230 wxRichTextRectArray rectArray
;
8231 for (j
= 0; j
< m_rowCount
; j
++)
8233 for (i
= 0; i
< m_colCount
; i
++)
8235 wxRichTextBox
* cell
= GetCell(j
, i
);
8236 int colSpan
= 1, rowSpan
= 1;
8237 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8238 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8239 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8240 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8241 if (colSpan
> 1 || rowSpan
> 1)
8243 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8247 // 0.2: find which cells are subsumed by a spanning cell
8248 for (j
= 0; j
< m_rowCount
; j
++)
8250 for (i
= 0; i
< m_colCount
; i
++)
8252 wxRichTextBox
* cell
= GetCell(j
, i
);
8253 if (rectArray
.GetCount() == 0)
8259 int colSpan
= 1, rowSpan
= 1;
8260 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8261 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8262 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8263 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8264 if (colSpan
> 1 || rowSpan
> 1)
8266 // Assume all spanning cells are shown
8272 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8274 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8286 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8287 // overlap with a spanned cell starting at a previous column position.
8288 // This means we need to keep an array of rects so we can check. However
8289 // it does also mean that some spans simply may not be taken into account
8290 // where there are different spans happening on different rows. In these cases,
8291 // they will simply be as wide as their constituent columns.
8293 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8294 // the absolute or percentage width of each column.
8296 for (j
= 0; j
< m_rowCount
; j
++)
8298 // First get the overall margins so we can calculate percentage widths based on
8299 // the available content space for all cells on the row
8301 int overallRowContentMargin
= 0;
8302 int visibleCellCount
= 0;
8304 for (i
= 0; i
< m_colCount
; i
++)
8306 wxRichTextBox
* cell
= GetCell(j
, i
);
8307 if (cell
->IsShown())
8309 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8310 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8312 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8313 visibleCellCount
++;
8317 // Add in inter-cell padding
8318 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8320 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8321 wxSize
rowTableSize(rowContentWidth
, 0);
8322 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8324 for (i
= 0; i
< m_colCount
; i
++)
8326 wxRichTextBox
* cell
= GetCell(j
, i
);
8327 if (cell
->IsShown())
8330 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8331 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8333 // Lay out cell to find min/max widths
8334 cell
->Invalidate(wxRICHTEXT_ALL
);
8335 cell
->Layout(dc
, availableSpace
, style
);
8339 int absoluteCellWidth
= -1;
8340 int percentageCellWidth
= -1;
8342 // I think we need to calculate percentages from the internal table size,
8343 // minus the padding between cells which we'll need to calculate from the
8344 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8345 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8346 // so if we want to conform to that we'll need to add in the overall cell margins.
8347 // However, this will make it difficult to specify percentages that add up to
8348 // 100% and still fit within the table width.
8349 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8350 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8351 // If we're using internal content size for the width, we would calculate the
8352 // the overall cell width for n cells as:
8353 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8354 // + thisOverallCellMargin
8355 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8356 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8358 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8360 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8361 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8363 percentageCellWidth
= w
;
8367 absoluteCellWidth
= w
;
8369 // Override absolute width with minimum width if necessary
8370 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8371 absoluteCellWidth
= cell
->GetMinSize().x
;
8374 if (absoluteCellWidth
!= -1)
8376 if (absoluteCellWidth
> absoluteColWidths
[i
])
8377 absoluteColWidths
[i
] = absoluteCellWidth
;
8380 if (percentageCellWidth
!= -1)
8382 if (percentageCellWidth
> percentageColWidths
[i
])
8383 percentageColWidths
[i
] = percentageCellWidth
;
8386 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8387 minColWidths
[i
] = cell
->GetMinSize().x
;
8388 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8389 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8395 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8396 // TODO: simply merge this into (1).
8397 for (i
= 0; i
< m_colCount
; i
++)
8399 if (absoluteColWidths
[i
] > 0)
8401 colWidths
[i
] = absoluteColWidths
[i
];
8403 else if (percentageColWidths
[i
] > 0)
8405 colWidths
[i
] = percentageColWidths
[i
];
8407 // This is rubbish - we calculated the absolute widths from percentages, so
8408 // we can't do it again here.
8409 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8413 // (3) Process absolute or proportional widths of spanning columns,
8414 // now that we know what our fixed column widths are going to be.
8415 // Spanned cells will try to adjust columns so the span will fit.
8416 // Even existing fixed column widths can be expanded if necessary.
8417 // Actually, currently fixed columns widths aren't adjusted; instead,
8418 // the algorithm favours earlier rows and adjusts unspecified column widths
8419 // the first time only. After that, we can't know whether the column has been
8420 // specified explicitly or not. (We could make a note if necessary.)
8421 for (j
= 0; j
< m_rowCount
; j
++)
8423 // First get the overall margins so we can calculate percentage widths based on
8424 // the available content space for all cells on the row
8426 int overallRowContentMargin
= 0;
8427 int visibleCellCount
= 0;
8429 for (i
= 0; i
< m_colCount
; i
++)
8431 wxRichTextBox
* cell
= GetCell(j
, i
);
8432 if (cell
->IsShown())
8434 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8435 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8437 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8438 visibleCellCount
++;
8442 // Add in inter-cell padding
8443 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8445 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8446 wxSize
rowTableSize(rowContentWidth
, 0);
8447 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8449 for (i
= 0; i
< m_colCount
; i
++)
8451 wxRichTextBox
* cell
= GetCell(j
, i
);
8452 if (cell
->IsShown())
8455 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8456 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8460 int spans
= wxMin(colSpan
, m_colCount
- i
);
8464 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8466 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8467 // Override absolute width with minimum width if necessary
8468 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8469 cellWidth
= cell
->GetMinSize().x
;
8473 // Do we want to do this? It's the only chance we get to
8474 // use the cell's min/max sizes, so we need to work out
8475 // how we're going to balance the unspecified spanning cell
8476 // width with the possibility more-constrained constituent cell widths.
8477 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8478 // don't want to constraint all the spanned columns to fit into this cell.
8479 // OK, let's say that if any of the constituent columns don't fit,
8480 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8481 // cells to the columns later.
8482 cellWidth
= cell
->GetMinSize().x
;
8483 if (cell
->GetMaxSize().x
> cellWidth
)
8484 cellWidth
= cell
->GetMaxSize().x
;
8487 // Subtract the padding between cells
8488 int spanningWidth
= cellWidth
;
8489 spanningWidth
-= paddingX
* (spans
-1);
8491 if (spanningWidth
> 0)
8493 // Now share the spanning width between columns within that span
8494 // TODO: take into account min widths of columns within the span
8495 int spanningWidthLeft
= spanningWidth
;
8496 int stretchColCount
= 0;
8497 for (k
= i
; k
< (i
+spans
); k
++)
8499 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8500 spanningWidthLeft
-= colWidths
[k
];
8504 // Now divide what's left between the remaining columns
8506 if (stretchColCount
> 0)
8507 colShare
= spanningWidthLeft
/ stretchColCount
;
8508 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8510 // If fixed-width columns are currently too big, then we'll later
8511 // stretch the spanned cell to fit.
8513 if (spanningWidthLeft
> 0)
8515 for (k
= i
; k
< (i
+spans
); k
++)
8517 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8519 int newWidth
= colShare
;
8520 if (k
== (i
+spans
-1))
8521 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8522 colWidths
[k
] = newWidth
;
8533 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8534 // TODO: take into account min widths of columns within the span
8535 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8536 int widthLeft
= tableWidthMinusPadding
;
8537 int stretchColCount
= 0;
8538 for (i
= 0; i
< m_colCount
; i
++)
8540 // TODO: we need to take into account min widths.
8541 // Subtract min width from width left, then
8542 // add the colShare to the min width
8543 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8544 widthLeft
-= colWidths
[i
];
8547 if (minColWidths
[i
] > 0)
8548 widthLeft
-= minColWidths
[i
];
8554 // Now divide what's left between the remaining columns
8556 if (stretchColCount
> 0)
8557 colShare
= widthLeft
/ stretchColCount
;
8558 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8560 // Check we don't have enough space, in which case shrink all columns, overriding
8561 // any absolute/proportional widths
8562 // TODO: actually we would like to divide up the shrinkage according to size.
8563 // How do we calculate the proportions that will achieve this?
8564 // Could first choose an arbitrary value for stretching cells, and then calculate
8565 // factors to multiply each width by.
8566 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8567 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8569 colShare
= tableWidthMinusPadding
/ m_colCount
;
8570 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8571 for (i
= 0; i
< m_colCount
; i
++)
8574 minColWidths
[i
] = 0;
8578 // We have to adjust the columns if either we need to shrink the
8579 // table to fit the parent/table width, or we explicitly set the
8580 // table width and need to stretch out the table.
8581 if (widthLeft
< 0 || stretchToFitTableWidth
)
8583 for (i
= 0; i
< m_colCount
; i
++)
8585 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8587 if (minColWidths
[i
] > 0)
8588 colWidths
[i
] = minColWidths
[i
] + colShare
;
8590 colWidths
[i
] = colShare
;
8591 if (i
== (m_colCount
-1))
8592 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8597 // TODO: if spanned cells have no specified or max width, make them the
8598 // as big as the columns they span. Do this for all spanned cells in all
8599 // rows, of course. Size any spanned cells left over at the end - even if they
8600 // have width > 0, make sure they're limited to the appropriate column edge.
8604 Sort out confusion between content width
8605 and overall width later. For now, assume we specify overall width.
8607 So, now we've laid out the table to fit into the given space
8608 and have used specified widths and minimum widths.
8610 Now we need to consider how we will try to take maximum width into account.
8614 // (??) TODO: take max width into account
8616 // (6) Lay out all cells again with the current values
8619 int y
= availableSpace
.y
;
8620 for (j
= 0; j
< m_rowCount
; j
++)
8622 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8623 int maxCellHeight
= 0;
8624 int maxSpecifiedCellHeight
= 0;
8626 wxArrayInt
actualWidths(m_colCount
);
8628 wxTextAttrDimensionConverter
converter(dc
, scale
);
8629 for (i
= 0; i
< m_colCount
; i
++)
8631 wxRichTextCell
* cell
= GetCell(j
, i
);
8632 if (cell
->IsShown())
8634 // Get max specified cell height
8635 // Don't handle percentages for height
8636 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8638 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8639 if (h
> maxSpecifiedCellHeight
)
8640 maxSpecifiedCellHeight
= h
;
8643 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8646 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8647 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8649 wxRect availableCellSpace
;
8651 // TODO: take into acount spans
8654 // Calculate the size of this spanning cell from its constituent columns
8656 int spans
= wxMin(colSpan
, m_colCount
- i
);
8657 for (k
= i
; k
< spans
; k
++)
8663 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8666 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8668 // Store actual width so we can force cell to be the appropriate width on the final loop
8669 actualWidths
[i
] = availableCellSpace
.GetWidth();
8672 cell
->Invalidate(wxRICHTEXT_ALL
);
8673 cell
->Layout(dc
, availableCellSpace
, style
);
8675 // TODO: use GetCachedSize().x to compute 'natural' size
8677 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8678 if (cell
->GetCachedSize().y
> maxCellHeight
)
8679 maxCellHeight
= cell
->GetCachedSize().y
;
8684 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8686 for (i
= 0; i
< m_colCount
; i
++)
8688 wxRichTextCell
* cell
= GetCell(j
, i
);
8689 if (cell
->IsShown())
8691 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8692 // Lay out cell with new height
8693 cell
->Invalidate(wxRICHTEXT_ALL
);
8694 cell
->Layout(dc
, availableCellSpace
, style
);
8696 // Make sure the cell size really is the appropriate size,
8697 // not the calculated box size
8698 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8700 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8705 if (j
< (m_rowCount
-1))
8709 // We need to add back the margins etc.
8711 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8712 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8713 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8714 SetCachedSize(marginRect
.GetSize());
8717 // TODO: calculate max size
8719 SetMaxSize(GetCachedSize());
8722 // TODO: calculate min size
8724 SetMinSize(GetCachedSize());
8727 // TODO: currently we use either a fixed table width or the parent's size.
8728 // We also want to be able to calculate the table width from its content,
8729 // whether using fixed column widths or cell content min/max width.
8730 // Probably need a boolean flag to say whether we need to stretch cells
8731 // to fit the table width, or to simply use min/max cell widths. The
8732 // trouble with this is that if cell widths are not specified, they
8733 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8734 // Anyway, ignoring that problem, we probably need to factor layout into a function
8735 // that can can calculate the maximum unconstrained layout in case table size is
8736 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8737 // constrain Layout(), or the previously-calculated max size to constraint layout.
8742 // Finds the absolute position and row height for the given character position
8743 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8745 wxRichTextCell
* child
= GetCell(index
+1);
8748 // Find the position at the start of the child cell, since the table doesn't
8749 // have any caret position of its own.
8750 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8756 // Get the cell at the given character position (in the range of the table).
8757 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8759 int row
= 0, col
= 0;
8760 if (GetCellRowColumnPosition(pos
, row
, col
))
8762 return GetCell(row
, col
);
8768 // Get the row/column for a given character position
8769 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8771 if (m_colCount
== 0 || m_rowCount
== 0)
8774 row
= (int) (pos
/ m_colCount
);
8775 col
= pos
- (row
* m_colCount
);
8777 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8779 if (row
< m_rowCount
&& col
< m_colCount
)
8785 // Calculate range, taking row/cell ordering into account instead of relying
8786 // on list ordering.
8787 void wxRichTextTable::CalculateRange(long start
, long& end
)
8789 long current
= start
;
8790 long lastEnd
= current
;
8799 for (i
= 0; i
< m_rowCount
; i
++)
8801 for (j
= 0; j
< m_colCount
; j
++)
8803 wxRichTextCell
* child
= GetCell(i
, j
);
8808 child
->CalculateRange(current
, childEnd
);
8811 current
= childEnd
+ 1;
8816 // A top-level object always has a range of size 1,
8817 // because its children don't count at this level.
8819 m_range
.SetRange(start
, start
);
8821 // An object with no children has zero length
8822 if (m_children
.GetCount() == 0)
8824 m_ownRange
.SetRange(0, lastEnd
);
8827 // Gets the range size.
8828 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8830 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
8833 // Deletes content in the given range.
8834 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
8836 // TODO: implement deletion of cells
8840 // Gets any text in this object for the given range.
8841 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
8843 return wxRichTextBox::GetTextForRange(range
);
8846 // Copies this object.
8847 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
8849 wxRichTextBox::Copy(obj
);
8853 m_rowCount
= obj
.m_rowCount
;
8854 m_colCount
= obj
.m_colCount
;
8856 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
8859 for (i
= 0; i
< m_rowCount
; i
++)
8861 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8862 for (j
= 0; j
< m_colCount
; j
++)
8864 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
8872 void wxRichTextTable::ClearTable()
8878 bool wxRichTextTable::CreateTable(int rows
, int cols
)
8885 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
8888 for (i
= 0; i
< rows
; i
++)
8890 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8891 for (j
= 0; j
< cols
; j
++)
8893 wxRichTextCell
* cell
= new wxRichTextCell
;
8895 cell
->AddParagraph(wxEmptyString
);
8904 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
8906 wxASSERT(row
< m_rowCount
);
8907 wxASSERT(col
< m_colCount
);
8909 if (row
< m_rowCount
&& col
< m_colCount
)
8911 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
8912 wxRichTextObject
* obj
= colArray
[col
];
8913 return wxDynamicCast(obj
, wxRichTextCell
);
8919 // Returns a selection object specifying the selections between start and end character positions.
8920 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8921 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
8923 wxRichTextSelection selection
;
8924 selection
.SetContainer((wxRichTextTable
*) this);
8933 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
8935 if (end
>= (m_colCount
* m_rowCount
))
8938 // We need to find the rectangle of cells that is described by the rectangle
8939 // with start, end as the diagonal. Make sure we don't add cells that are
8940 // not currenty visible because they are overlapped by spanning cells.
8942 --------------------------
8943 | 0 | 1 | 2 | 3 | 4 |
8944 --------------------------
8945 | 5 | 6 | 7 | 8 | 9 |
8946 --------------------------
8947 | 10 | 11 | 12 | 13 | 14 |
8948 --------------------------
8949 | 15 | 16 | 17 | 18 | 19 |
8950 --------------------------
8952 Let's say we select 6 -> 18.
8954 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8955 which is left and which is right.
8957 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8959 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8965 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
8966 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
8968 int topRow
= int(start
/m_colCount
);
8969 int bottomRow
= int(end
/m_colCount
);
8971 if (leftCol
> rightCol
)
8978 if (topRow
> bottomRow
)
8980 int tmp
= bottomRow
;
8986 for (i
= topRow
; i
<= bottomRow
; i
++)
8988 for (j
= leftCol
; j
<= rightCol
; j
++)
8990 wxRichTextCell
* cell
= GetCell(i
, j
);
8991 if (cell
&& cell
->IsShown())
8992 selection
.Add(cell
->GetRange());
8999 // Sets the attributes for the cells specified by the selection.
9000 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9002 if (selection
.GetContainer() != this)
9005 wxRichTextBuffer
* buffer
= GetBuffer();
9006 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9007 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9010 buffer
->BeginBatchUndo(_("Set Cell Style"));
9012 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9015 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9016 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9017 SetStyle(cell
, style
, flags
);
9018 node
= node
->GetNext();
9021 // Do action, or delay it until end of batch.
9023 buffer
->EndBatchUndo();
9028 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9030 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9031 if ((startRow
+ noRows
) >= m_rowCount
)
9035 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9037 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9038 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9040 wxRichTextObject
* cell
= colArray
[j
];
9041 RemoveChild(cell
, true);
9044 // Keep deleting at the same position, since we move all
9046 m_cells
.RemoveAt(startRow
);
9049 m_rowCount
= m_rowCount
- noRows
;
9054 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9056 wxASSERT((startCol
+ noCols
) < m_colCount
);
9057 if ((startCol
+ noCols
) >= m_colCount
)
9060 bool deleteRows
= (noCols
== m_colCount
);
9063 for (i
= 0; i
< m_rowCount
; i
++)
9065 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9066 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9068 wxRichTextObject
* cell
= colArray
[j
];
9069 RemoveChild(cell
, true);
9073 m_cells
.RemoveAt(0);
9078 m_colCount
= m_colCount
- noCols
;
9083 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9085 wxASSERT(startRow
<= m_rowCount
);
9086 if (startRow
> m_rowCount
)
9090 for (i
= 0; i
< noRows
; i
++)
9093 if (startRow
== m_rowCount
)
9095 m_cells
.Add(wxRichTextObjectPtrArray());
9096 idx
= m_cells
.GetCount() - 1;
9100 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9104 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9105 for (j
= 0; j
< m_colCount
; j
++)
9107 wxRichTextCell
* cell
= new wxRichTextCell
;
9108 cell
->GetAttributes() = attr
;
9115 m_rowCount
= m_rowCount
+ noRows
;
9119 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9121 wxASSERT(startCol
<= m_colCount
);
9122 if (startCol
> m_colCount
)
9126 for (i
= 0; i
< m_rowCount
; i
++)
9128 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9129 for (j
= 0; j
< noCols
; j
++)
9131 wxRichTextCell
* cell
= new wxRichTextCell
;
9132 cell
->GetAttributes() = attr
;
9136 if (startCol
== m_colCount
)
9139 colArray
.Insert(cell
, startCol
+j
);
9143 m_colCount
= m_colCount
+ noCols
;
9148 // Edit properties via a GUI
9149 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9151 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9152 boxDlg
.SetAttributes(GetAttributes());
9154 if (boxDlg
.ShowModal() == wxID_OK
)
9156 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9164 * Module to initialise and clean up handlers
9167 class wxRichTextModule
: public wxModule
9169 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9171 wxRichTextModule() {}
9174 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9175 wxRichTextBuffer::InitStandardHandlers();
9176 wxRichTextParagraph::InitDefaultTabs();
9181 wxRichTextBuffer::CleanUpHandlers();
9182 wxRichTextDecimalToRoman(-1);
9183 wxRichTextParagraph::ClearDefaultTabs();
9184 wxRichTextCtrl::ClearAvailableFontNames();
9185 wxRichTextBuffer::SetRenderer(NULL
);
9189 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9192 // If the richtext lib is dynamically loaded after the app has already started
9193 // (such as from wxPython) then the built-in module system will not init this
9194 // module. Provide this function to do it manually.
9195 void wxRichTextModuleInit()
9197 wxModule
* module = new wxRichTextModule
;
9199 wxModule::RegisterModule(module);
9204 * Commands for undo/redo
9208 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9209 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9211 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9214 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9218 wxRichTextCommand::~wxRichTextCommand()
9223 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9225 if (!m_actions
.Member(action
))
9226 m_actions
.Append(action
);
9229 bool wxRichTextCommand::Do()
9231 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9233 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9240 bool wxRichTextCommand::Undo()
9242 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9244 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9251 void wxRichTextCommand::ClearActions()
9253 WX_CLEAR_LIST(wxList
, m_actions
);
9261 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9262 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9263 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9267 m_containerAddress
.Create(buffer
, container
);
9268 m_ignoreThis
= ignoreFirstTime
;
9273 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9274 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9276 cmd
->AddAction(this);
9279 wxRichTextAction::~wxRichTextAction()
9285 // Returns the container that this action refers to, using the container address and top-level buffer.
9286 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9288 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9293 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9295 // Store a list of line start character and y positions so we can figure out which area
9296 // we need to refresh
9298 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9299 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9300 wxASSERT(container
!= NULL
);
9304 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9305 // If we had several actions, which only invalidate and leave layout until the
9306 // paint handler is called, then this might not be true. So we may need to switch
9307 // optimisation on only when we're simply adding text and not simultaneously
9308 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9309 // first, but of course this means we'll be doing it twice.
9310 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9312 wxSize clientSize
= m_ctrl
->GetClientSize();
9313 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9314 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9316 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9317 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9320 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9321 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9324 wxRichTextLine
* line
= node2
->GetData();
9325 wxPoint pt
= line
->GetAbsolutePosition();
9326 wxRichTextRange range
= line
->GetAbsoluteRange();
9330 node2
= wxRichTextLineList::compatibility_iterator();
9331 node
= wxRichTextObjectList::compatibility_iterator();
9333 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9335 optimizationLineCharPositions
.Add(range
.GetStart());
9336 optimizationLineYPositions
.Add(pt
.y
);
9340 node2
= node2
->GetNext();
9344 node
= node
->GetNext();
9350 bool wxRichTextAction::Do()
9352 m_buffer
->Modify(true);
9354 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9355 wxASSERT(container
!= NULL
);
9361 case wxRICHTEXT_INSERT
:
9363 // Store a list of line start character and y positions so we can figure out which area
9364 // we need to refresh
9365 wxArrayInt optimizationLineCharPositions
;
9366 wxArrayInt optimizationLineYPositions
;
9368 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9369 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9372 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9373 container
->UpdateRanges();
9375 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9376 // Layout() would stop prematurely at the top level.
9377 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9379 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9381 // Character position to caret position
9382 newCaretPosition
--;
9384 // Don't take into account the last newline
9385 if (m_newParagraphs
.GetPartialParagraph())
9386 newCaretPosition
--;
9388 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9390 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9391 if (p
->GetRange().GetLength() == 1)
9392 newCaretPosition
--;
9395 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9397 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9399 wxRichTextEvent
cmdEvent(
9400 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9401 m_ctrl
? m_ctrl
->GetId() : -1);
9402 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9403 cmdEvent
.SetRange(GetRange());
9404 cmdEvent
.SetPosition(GetRange().GetStart());
9405 cmdEvent
.SetContainer(container
);
9407 m_buffer
->SendEvent(cmdEvent
);
9411 case wxRICHTEXT_DELETE
:
9413 wxArrayInt optimizationLineCharPositions
;
9414 wxArrayInt optimizationLineYPositions
;
9416 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9417 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9420 container
->DeleteRange(GetRange());
9421 container
->UpdateRanges();
9422 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9423 // Layout() would stop prematurely at the top level.
9424 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9426 long caretPos
= GetRange().GetStart()-1;
9427 if (caretPos
>= container
->GetOwnRange().GetEnd())
9430 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9432 wxRichTextEvent
cmdEvent(
9433 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9434 m_ctrl
? m_ctrl
->GetId() : -1);
9435 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9436 cmdEvent
.SetRange(GetRange());
9437 cmdEvent
.SetPosition(GetRange().GetStart());
9438 cmdEvent
.SetContainer(container
);
9440 m_buffer
->SendEvent(cmdEvent
);
9444 case wxRICHTEXT_CHANGE_STYLE
:
9446 ApplyParagraphs(GetNewParagraphs());
9448 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9449 // Layout() would stop prematurely at the top level.
9450 container
->InvalidateHierarchy(GetRange());
9452 UpdateAppearance(GetPosition());
9454 wxRichTextEvent
cmdEvent(
9455 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9456 m_ctrl
? m_ctrl
->GetId() : -1);
9457 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9458 cmdEvent
.SetRange(GetRange());
9459 cmdEvent
.SetPosition(GetRange().GetStart());
9460 cmdEvent
.SetContainer(container
);
9462 m_buffer
->SendEvent(cmdEvent
);
9466 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9468 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9471 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9472 obj
->GetAttributes() = m_attributes
;
9473 m_attributes
= oldAttr
;
9476 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9477 // Layout() would stop prematurely at the top level.
9478 container
->InvalidateHierarchy(GetRange());
9480 UpdateAppearance(GetPosition());
9482 wxRichTextEvent
cmdEvent(
9483 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9484 m_ctrl
? m_ctrl
->GetId() : -1);
9485 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9486 cmdEvent
.SetRange(GetRange());
9487 cmdEvent
.SetPosition(GetRange().GetStart());
9488 cmdEvent
.SetContainer(container
);
9490 m_buffer
->SendEvent(cmdEvent
);
9494 case wxRICHTEXT_CHANGE_OBJECT
:
9496 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9497 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9498 if (obj
&& m_object
)
9500 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9503 wxRichTextObject
* obj
= node
->GetData();
9504 node
->SetData(m_object
);
9509 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9510 // Layout() would stop prematurely at the top level.
9511 container
->InvalidateHierarchy(GetRange());
9513 UpdateAppearance(GetPosition());
9515 // TODO: send new kind of modification event
9526 bool wxRichTextAction::Undo()
9528 m_buffer
->Modify(true);
9530 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9531 wxASSERT(container
!= NULL
);
9537 case wxRICHTEXT_INSERT
:
9539 wxArrayInt optimizationLineCharPositions
;
9540 wxArrayInt optimizationLineYPositions
;
9542 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9543 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9546 container
->DeleteRange(GetRange());
9547 container
->UpdateRanges();
9548 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9549 // Layout() would stop prematurely at the top level.
9550 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9552 long newCaretPosition
= GetPosition() - 1;
9554 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9556 wxRichTextEvent
cmdEvent(
9557 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9558 m_ctrl
? m_ctrl
->GetId() : -1);
9559 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9560 cmdEvent
.SetRange(GetRange());
9561 cmdEvent
.SetPosition(GetRange().GetStart());
9562 cmdEvent
.SetContainer(container
);
9564 m_buffer
->SendEvent(cmdEvent
);
9568 case wxRICHTEXT_DELETE
:
9570 wxArrayInt optimizationLineCharPositions
;
9571 wxArrayInt optimizationLineYPositions
;
9573 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9574 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9577 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9578 container
->UpdateRanges();
9579 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9580 // Layout() would stop prematurely at the top level.
9581 container
->InvalidateHierarchy(GetRange());
9583 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9585 wxRichTextEvent
cmdEvent(
9586 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9587 m_ctrl
? m_ctrl
->GetId() : -1);
9588 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9589 cmdEvent
.SetRange(GetRange());
9590 cmdEvent
.SetPosition(GetRange().GetStart());
9591 cmdEvent
.SetContainer(container
);
9593 m_buffer
->SendEvent(cmdEvent
);
9597 case wxRICHTEXT_CHANGE_STYLE
:
9599 ApplyParagraphs(GetOldParagraphs());
9600 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9601 // Layout() would stop prematurely at the top level.
9602 container
->InvalidateHierarchy(GetRange());
9604 UpdateAppearance(GetPosition());
9606 wxRichTextEvent
cmdEvent(
9607 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9608 m_ctrl
? m_ctrl
->GetId() : -1);
9609 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9610 cmdEvent
.SetRange(GetRange());
9611 cmdEvent
.SetPosition(GetRange().GetStart());
9612 cmdEvent
.SetContainer(container
);
9614 m_buffer
->SendEvent(cmdEvent
);
9618 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9619 case wxRICHTEXT_CHANGE_OBJECT
:
9630 /// Update the control appearance
9631 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9633 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9634 wxASSERT(container
!= NULL
);
9640 m_ctrl
->SetFocusObject(container
);
9641 m_ctrl
->SetCaretPosition(caretPosition
);
9643 if (!m_ctrl
->IsFrozen())
9645 wxRect containerRect
= container
->GetRect();
9647 m_ctrl
->LayoutContent();
9649 // Refresh everything if there were floating objects or the container changed size
9650 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9651 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9653 m_ctrl
->Refresh(false);
9657 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9658 // Find refresh rectangle if we are in a position to optimise refresh
9659 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9663 wxSize clientSize
= m_ctrl
->GetClientSize();
9664 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9666 // Start/end positions
9668 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9670 bool foundEnd
= false;
9672 // position offset - how many characters were inserted
9673 int positionOffset
= GetRange().GetLength();
9675 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9676 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9677 positionOffset
= - positionOffset
;
9679 // find the first line which is being drawn at the same position as it was
9680 // before. Since we're talking about a simple insertion, we can assume
9681 // that the rest of the window does not need to be redrawn.
9683 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9684 // Since we support floating layout, we should redraw the whole para instead of just
9685 // the first line touching the invalid range.
9688 firstY
= para
->GetPosition().y
;
9691 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9694 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9695 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9698 wxRichTextLine
* line
= node2
->GetData();
9699 wxPoint pt
= line
->GetAbsolutePosition();
9700 wxRichTextRange range
= line
->GetAbsoluteRange();
9702 // we want to find the first line that is in the same position
9703 // as before. This will mean we're at the end of the changed text.
9705 if (pt
.y
> lastY
) // going past the end of the window, no more info
9707 node2
= wxRichTextLineList::compatibility_iterator();
9708 node
= wxRichTextObjectList::compatibility_iterator();
9710 // Detect last line in the buffer
9711 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9713 // If deleting text, make sure we refresh below as well as above
9714 if (positionOffset
>= 0)
9717 lastY
= pt
.y
+ line
->GetSize().y
;
9720 node2
= wxRichTextLineList::compatibility_iterator();
9721 node
= wxRichTextObjectList::compatibility_iterator();
9727 // search for this line being at the same position as before
9728 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9730 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9731 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9733 // Stop, we're now the same as we were
9738 node2
= wxRichTextLineList::compatibility_iterator();
9739 node
= wxRichTextObjectList::compatibility_iterator();
9747 node2
= node2
->GetNext();
9751 node
= node
->GetNext();
9754 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9756 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9758 // Convert to device coordinates
9759 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9760 m_ctrl
->RefreshRect(rect
);
9764 m_ctrl
->Refresh(false);
9766 m_ctrl
->PositionCaret();
9768 // This causes styles to persist when doing programmatic
9769 // content creation except when Freeze/Thaw is used, so
9770 // disable this and check for the consequences.
9771 // m_ctrl->SetDefaultStyleToCursorStyle();
9773 if (sendUpdateEvent
)
9774 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9779 /// Replace the buffer paragraphs with the new ones.
9780 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9782 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9783 wxASSERT(container
!= NULL
);
9787 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9790 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9791 wxASSERT (para
!= NULL
);
9793 // We'll replace the existing paragraph by finding the paragraph at this position,
9794 // delete its node data, and setting a copy as the new node data.
9795 // TODO: make more efficient by simply swapping old and new paragraph objects.
9797 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9800 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9803 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9804 newPara
->SetParent(container
);
9806 bufferParaNode
->SetData(newPara
);
9808 delete existingPara
;
9812 node
= node
->GetNext();
9819 * This stores beginning and end positions for a range of data.
9822 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
9824 /// Limit this range to be within 'range'
9825 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
9827 if (m_start
< range
.m_start
)
9828 m_start
= range
.m_start
;
9830 if (m_end
> range
.m_end
)
9831 m_end
= range
.m_end
;
9837 * wxRichTextImage implementation
9838 * This object represents an image.
9841 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
9843 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9844 wxRichTextObject(parent
)
9846 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
9848 SetAttributes(*charStyle
);
9851 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9852 wxRichTextObject(parent
)
9854 m_imageBlock
= imageBlock
;
9856 SetAttributes(*charStyle
);
9859 /// Create a cached image at the required size
9860 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
9862 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9864 if (!m_imageBlock
.IsOk())
9868 m_imageBlock
.Load(image
);
9872 int width
= image
.GetWidth();
9873 int height
= image
.GetHeight();
9875 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9877 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9878 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9880 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9882 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9884 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9885 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9887 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9890 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
9891 m_imageCache
= wxBitmap(image
);
9894 // If the original width and height is small, e.g. 400 or below,
9895 // scale up and then down to improve image quality. This can make
9896 // a big difference, with not much performance hit.
9897 int upscaleThreshold
= 400;
9899 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
9901 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
9902 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9905 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9906 m_imageCache
= wxBitmap(img
);
9910 return m_imageCache
.IsOk();
9914 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
9919 // Don't need cached size AFAIK
9920 // wxSize size = GetCachedSize();
9921 if (!LoadImageCache(dc
))
9924 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9927 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
9929 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
9932 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9933 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9934 marginRect
= rect
; // outer rectangle, will calculate contentRect
9935 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9937 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
9939 if (selection
.WithinSelection(range
.GetStart(), this))
9941 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9942 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9943 dc
.SetLogicalFunction(wxINVERT
);
9944 dc
.DrawRectangle(contentRect
);
9945 dc
.SetLogicalFunction(wxCOPY
);
9951 /// Lay the item out
9952 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(style
))
9954 if (!LoadImageCache(dc
))
9957 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9958 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9959 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9960 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9962 wxSize overallSize
= marginRect
.GetSize();
9964 SetCachedSize(overallSize
);
9965 SetMaxSize(overallSize
);
9966 SetMinSize(overallSize
);
9967 SetPosition(rect
.GetPosition());
9972 /// Get/set the object size for the given range. Returns false if the range
9973 /// is invalid for this object.
9974 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
9976 if (!range
.IsWithin(GetRange()))
9979 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
9981 size
.x
= 0; size
.y
= 0;
9983 partialExtents
->Add(0);
9987 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9988 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9989 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9990 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9992 wxSize overallSize
= marginRect
.GetSize();
9995 partialExtents
->Add(overallSize
.x
);
10002 // Get the 'natural' size for an object. For an image, it would be the
10004 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10006 wxTextAttrSize size
;
10007 if (GetImageCache().IsOk())
10009 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10010 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10017 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10019 wxRichTextObject::Copy(obj
);
10021 m_imageBlock
= obj
.m_imageBlock
;
10024 /// Edit properties via a GUI
10025 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10027 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10028 imageDlg
.SetAttributes(GetAttributes());
10030 if (imageDlg
.ShowModal() == wxID_OK
)
10032 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10033 // indeterminate in the object.
10034 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10046 /// Compare two attribute objects
10047 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10049 return (attr1
== attr2
);
10052 // Partial equality test taking flags into account
10053 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10055 return attr1
.EqPartial(attr2
);
10059 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10061 if (tabs1
.GetCount() != tabs2
.GetCount())
10065 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10067 if (tabs1
[i
] != tabs2
[i
])
10073 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10075 return destStyle
.Apply(style
, compareWith
);
10078 // Remove attributes
10079 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10081 return destStyle
.RemoveStyle(style
);
10084 /// Combine two bitlists, specifying the bits of interest with separate flags.
10085 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10087 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10090 /// Compare two bitlists
10091 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10093 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10096 /// Split into paragraph and character styles
10097 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10099 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10102 /// Convert a decimal to Roman numerals
10103 wxString
wxRichTextDecimalToRoman(long n
)
10105 static wxArrayInt decimalNumbers
;
10106 static wxArrayString romanNumbers
;
10111 decimalNumbers
.Clear();
10112 romanNumbers
.Clear();
10113 return wxEmptyString
;
10116 if (decimalNumbers
.GetCount() == 0)
10118 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10120 wxRichTextAddDecRom(1000, wxT("M"));
10121 wxRichTextAddDecRom(900, wxT("CM"));
10122 wxRichTextAddDecRom(500, wxT("D"));
10123 wxRichTextAddDecRom(400, wxT("CD"));
10124 wxRichTextAddDecRom(100, wxT("C"));
10125 wxRichTextAddDecRom(90, wxT("XC"));
10126 wxRichTextAddDecRom(50, wxT("L"));
10127 wxRichTextAddDecRom(40, wxT("XL"));
10128 wxRichTextAddDecRom(10, wxT("X"));
10129 wxRichTextAddDecRom(9, wxT("IX"));
10130 wxRichTextAddDecRom(5, wxT("V"));
10131 wxRichTextAddDecRom(4, wxT("IV"));
10132 wxRichTextAddDecRom(1, wxT("I"));
10138 while (n
> 0 && i
< 13)
10140 if (n
>= decimalNumbers
[i
])
10142 n
-= decimalNumbers
[i
];
10143 roman
+= romanNumbers
[i
];
10150 if (roman
.IsEmpty())
10156 * wxRichTextFileHandler
10157 * Base class for file handlers
10160 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10162 #if wxUSE_FFILE && wxUSE_STREAMS
10163 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10165 wxFFileInputStream
stream(filename
);
10167 return LoadFile(buffer
, stream
);
10172 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10174 wxFFileOutputStream
stream(filename
);
10176 return SaveFile(buffer
, stream
);
10180 #endif // wxUSE_FFILE && wxUSE_STREAMS
10182 /// Can we handle this filename (if using files)? By default, checks the extension.
10183 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10185 wxString path
, file
, ext
;
10186 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10188 return (ext
.Lower() == GetExtension());
10192 * wxRichTextTextHandler
10193 * Plain text handler
10196 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10199 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10201 if (!stream
.IsOk())
10207 while (!stream
.Eof())
10209 int ch
= stream
.GetC();
10213 if (ch
== 10 && lastCh
!= 13)
10216 if (ch
> 0 && ch
!= 10)
10223 buffer
->ResetAndClearCommands();
10225 buffer
->AddParagraphs(str
);
10226 buffer
->UpdateRanges();
10231 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10233 if (!stream
.IsOk())
10236 wxString text
= buffer
->GetText();
10238 wxString newLine
= wxRichTextLineBreakChar
;
10239 text
.Replace(newLine
, wxT("\n"));
10241 wxCharBuffer buf
= text
.ToAscii();
10243 stream
.Write((const char*) buf
, text
.length());
10246 #endif // wxUSE_STREAMS
10249 * Stores information about an image, in binary in-memory form
10252 wxRichTextImageBlock::wxRichTextImageBlock()
10257 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10263 wxRichTextImageBlock::~wxRichTextImageBlock()
10268 void wxRichTextImageBlock::Init()
10272 m_imageType
= wxBITMAP_TYPE_INVALID
;
10275 void wxRichTextImageBlock::Clear()
10279 m_imageType
= wxBITMAP_TYPE_INVALID
;
10283 // Load the original image into a memory block.
10284 // If the image is not a JPEG, we must convert it into a JPEG
10285 // to conserve space.
10286 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10287 // load the image a 2nd time.
10289 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10290 wxImage
& image
, bool convertToJPEG
)
10292 m_imageType
= imageType
;
10294 wxString
filenameToRead(filename
);
10295 bool removeFile
= false;
10297 if (imageType
== wxBITMAP_TYPE_INVALID
)
10298 return false; // Could not determine image type
10300 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10302 wxString tempFile
=
10303 wxFileName::CreateTempFileName(_("image"));
10305 wxASSERT(!tempFile
.IsEmpty());
10307 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10308 filenameToRead
= tempFile
;
10311 m_imageType
= wxBITMAP_TYPE_JPEG
;
10314 if (!file
.Open(filenameToRead
))
10317 m_dataSize
= (size_t) file
.Length();
10322 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10325 wxRemoveFile(filenameToRead
);
10327 return (m_data
!= NULL
);
10330 // Make an image block from the wxImage in the given
10332 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10334 image
.SetOption(wxT("quality"), quality
);
10336 if (imageType
== wxBITMAP_TYPE_INVALID
)
10337 return false; // Could not determine image type
10339 return DoMakeImageBlock(image
, imageType
);
10342 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10343 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10345 if (imageType
== wxBITMAP_TYPE_INVALID
)
10346 return false; // Could not determine image type
10348 return DoMakeImageBlock(image
, imageType
);
10351 // Makes the image block
10352 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10354 wxMemoryOutputStream memStream
;
10355 if (!image
.SaveFile(memStream
, imageType
))
10360 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10368 m_imageType
= imageType
;
10369 m_dataSize
= memStream
.GetSize();
10371 memStream
.CopyTo(m_data
, m_dataSize
);
10373 return (m_data
!= NULL
);
10377 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10379 return WriteBlock(filename
, m_data
, m_dataSize
);
10382 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10384 m_imageType
= block
.m_imageType
;
10386 m_dataSize
= block
.m_dataSize
;
10387 if (m_dataSize
== 0)
10390 m_data
= new unsigned char[m_dataSize
];
10392 for (i
= 0; i
< m_dataSize
; i
++)
10393 m_data
[i
] = block
.m_data
[i
];
10397 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10402 // Load a wxImage from the block
10403 bool wxRichTextImageBlock::Load(wxImage
& image
)
10408 // Read in the image.
10410 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10411 bool success
= image
.LoadFile(mstream
, GetImageType());
10413 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10414 wxASSERT(!tempFile
.IsEmpty());
10416 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10420 success
= image
.LoadFile(tempFile
, GetImageType());
10421 wxRemoveFile(tempFile
);
10427 // Write data in hex to a stream
10428 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10430 const int bufSize
= 512;
10431 char buf
[bufSize
+1];
10433 int left
= m_dataSize
;
10438 if (left
*2 > bufSize
)
10440 n
= bufSize
; left
-= (bufSize
/2);
10444 n
= left
*2; left
= 0;
10448 for (i
= 0; i
< (n
/2); i
++)
10450 wxDecToHex(m_data
[j
], b
, b
+1);
10455 stream
.Write((const char*) buf
, n
);
10460 // Read data in hex from a stream
10461 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10463 int dataSize
= length
/2;
10468 // create a null terminated temporary string:
10472 m_data
= new unsigned char[dataSize
];
10474 for (i
= 0; i
< dataSize
; i
++)
10476 str
[0] = (char)stream
.GetC();
10477 str
[1] = (char)stream
.GetC();
10479 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10482 m_dataSize
= dataSize
;
10483 m_imageType
= imageType
;
10488 // Allocate and read from stream as a block of memory
10489 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10491 unsigned char* block
= new unsigned char[size
];
10495 stream
.Read(block
, size
);
10500 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10502 wxFileInputStream
stream(filename
);
10506 return ReadBlock(stream
, size
);
10509 // Write memory block to stream
10510 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10512 stream
.Write((void*) block
, size
);
10513 return stream
.IsOk();
10517 // Write memory block to file
10518 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10520 wxFileOutputStream
outStream(filename
);
10521 if (!outStream
.Ok())
10524 return WriteBlock(outStream
, block
, size
);
10527 // Gets the extension for the block's type
10528 wxString
wxRichTextImageBlock::GetExtension() const
10530 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10532 return handler
->GetExtension();
10534 return wxEmptyString
;
10540 * The data object for a wxRichTextBuffer
10543 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10545 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10547 m_richTextBuffer
= richTextBuffer
;
10549 // this string should uniquely identify our format, but is otherwise
10551 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10553 SetFormat(m_formatRichTextBuffer
);
10556 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10558 delete m_richTextBuffer
;
10561 // after a call to this function, the richTextBuffer is owned by the caller and it
10562 // is responsible for deleting it!
10563 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10565 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10566 m_richTextBuffer
= NULL
;
10568 return richTextBuffer
;
10571 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10573 return m_formatRichTextBuffer
;
10576 size_t wxRichTextBufferDataObject::GetDataSize() const
10578 if (!m_richTextBuffer
)
10584 wxStringOutputStream
stream(& bufXML
);
10585 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10587 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10593 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10594 return strlen(buffer
) + 1;
10596 return bufXML
.Length()+1;
10600 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10602 if (!pBuf
|| !m_richTextBuffer
)
10608 wxStringOutputStream
stream(& bufXML
);
10609 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10611 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10617 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10618 size_t len
= strlen(buffer
);
10619 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10620 ((char*) pBuf
)[len
] = 0;
10622 size_t len
= bufXML
.Length();
10623 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10624 ((char*) pBuf
)[len
] = 0;
10630 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10632 wxDELETE(m_richTextBuffer
);
10634 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10636 m_richTextBuffer
= new wxRichTextBuffer
;
10638 wxStringInputStream
stream(bufXML
);
10639 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10641 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10643 wxDELETE(m_richTextBuffer
);
10655 * wxRichTextFontTable
10656 * Manages quick access to a pool of fonts for rendering rich text
10659 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10661 class wxRichTextFontTableData
: public wxObjectRefData
10664 wxRichTextFontTableData() {}
10666 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10668 wxRichTextFontTableHashMap m_hashMap
;
10671 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10673 wxString
facename(fontSpec
.GetFontFaceName());
10674 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()));
10675 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10677 if ( entry
== m_hashMap
.end() )
10679 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10680 m_hashMap
[spec
] = font
;
10685 return entry
->second
;
10689 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10691 wxRichTextFontTable::wxRichTextFontTable()
10693 m_refData
= new wxRichTextFontTableData
;
10696 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10702 wxRichTextFontTable::~wxRichTextFontTable()
10707 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10709 return (m_refData
== table
.m_refData
);
10712 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10717 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10719 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10721 return data
->FindFont(fontSpec
);
10726 void wxRichTextFontTable::Clear()
10728 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10730 data
->m_hashMap
.clear();
10736 void wxTextBoxAttr::Reset()
10739 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10740 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10741 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10742 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10746 m_position
.Reset();
10755 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10758 m_flags
== attr
.m_flags
&&
10759 m_floatMode
== attr
.m_floatMode
&&
10760 m_clearMode
== attr
.m_clearMode
&&
10761 m_collapseMode
== attr
.m_collapseMode
&&
10762 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10764 m_margins
== attr
.m_margins
&&
10765 m_padding
== attr
.m_padding
&&
10766 m_position
== attr
.m_position
&&
10768 m_size
== attr
.m_size
&&
10770 m_border
== attr
.m_border
&&
10771 m_outline
== attr
.m_outline
10775 // Partial equality test
10776 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10778 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10781 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10784 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10787 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10792 if (!m_position
.EqPartial(attr
.m_position
))
10797 if (!m_margins
.EqPartial(attr
.m_margins
))
10802 if (!m_padding
.EqPartial(attr
.m_padding
))
10807 if (!GetBorder().EqPartial(attr
.GetBorder()))
10812 if (!GetOutline().EqPartial(attr
.GetOutline()))
10818 // Merges the given attributes. If compareWith
10819 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10820 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10821 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
10823 if (attr
.HasFloatMode())
10825 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
10826 SetFloatMode(attr
.GetFloatMode());
10829 if (attr
.HasClearMode())
10831 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
10832 SetClearMode(attr
.GetClearMode());
10835 if (attr
.HasCollapseBorders())
10837 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
10838 SetCollapseBorders(attr
.GetCollapseBorders());
10841 if (attr
.HasVerticalAlignment())
10843 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
10844 SetVerticalAlignment(attr
.GetVerticalAlignment());
10847 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
10848 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
10849 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
10851 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
10853 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
10854 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
10859 // Remove specified attributes from this object
10860 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
10862 if (attr
.HasFloatMode())
10863 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10865 if (attr
.HasClearMode())
10866 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10868 if (attr
.HasCollapseBorders())
10869 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10871 if (attr
.HasVerticalAlignment())
10872 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10874 m_margins
.RemoveStyle(attr
.m_margins
);
10875 m_padding
.RemoveStyle(attr
.m_padding
);
10876 m_position
.RemoveStyle(attr
.m_position
);
10878 m_size
.RemoveStyle(attr
.m_size
);
10880 m_border
.RemoveStyle(attr
.m_border
);
10881 m_outline
.RemoveStyle(attr
.m_outline
);
10886 // Collects the attributes that are common to a range of content, building up a note of
10887 // which attributes are absent in some objects and which clash in some objects.
10888 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
10890 if (attr
.HasFloatMode())
10892 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
10894 if (HasFloatMode())
10896 if (GetFloatMode() != attr
.GetFloatMode())
10898 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10899 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10903 SetFloatMode(attr
.GetFloatMode());
10907 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10909 if (attr
.HasClearMode())
10911 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
10913 if (HasClearMode())
10915 if (GetClearMode() != attr
.GetClearMode())
10917 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10918 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10922 SetClearMode(attr
.GetClearMode());
10926 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10928 if (attr
.HasCollapseBorders())
10930 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
10932 if (HasCollapseBorders())
10934 if (GetCollapseBorders() != attr
.GetCollapseBorders())
10936 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10937 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10941 SetCollapseBorders(attr
.GetCollapseBorders());
10945 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10947 if (attr
.HasVerticalAlignment())
10949 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
10951 if (HasVerticalAlignment())
10953 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
10955 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10956 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10960 SetVerticalAlignment(attr
.GetVerticalAlignment());
10964 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10966 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
10967 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
10968 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
10970 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
10972 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
10973 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
10978 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
10980 wxTextAttr::Copy(attr
);
10982 m_textBoxAttr
= attr
.m_textBoxAttr
;
10985 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
10987 if (!(wxTextAttr::operator==(attr
)))
10990 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
10993 // Partial equality test taking comparison object into account
10994 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
10996 if (!(wxTextAttr::EqPartial(attr
)))
10999 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11002 // Merges the given attributes. If compareWith
11003 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11004 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11005 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11007 wxTextAttr::Apply(style
, compareWith
);
11009 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11012 // Remove specified attributes from this object
11013 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11015 wxTextAttr::RemoveStyle(*this, attr
);
11017 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11020 // Collects the attributes that are common to a range of content, building up a note of
11021 // which attributes are absent in some objects and which clash in some objects.
11022 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11024 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11026 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11029 // Partial equality test
11030 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11032 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11035 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11038 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11044 // Apply border to 'this', but not if the same as compareWith
11045 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11047 if (border
.HasStyle())
11049 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11050 SetStyle(border
.GetStyle());
11052 if (border
.HasColour())
11054 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11055 SetColour(border
.GetColourLong());
11057 if (border
.HasWidth())
11059 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11060 SetWidth(border
.GetWidth());
11066 // Remove specified attributes from this object
11067 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11069 if (attr
.HasStyle() && HasStyle())
11070 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11071 if (attr
.HasColour() && HasColour())
11072 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11073 if (attr
.HasWidth() && HasWidth())
11074 m_borderWidth
.Reset();
11079 // Collects the attributes that are common to a range of content, building up a note of
11080 // which attributes are absent in some objects and which clash in some objects.
11081 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11083 if (attr
.HasStyle())
11085 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11089 if (GetStyle() != attr
.GetStyle())
11091 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11092 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11096 SetStyle(attr
.GetStyle());
11100 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11102 if (attr
.HasColour())
11104 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11108 if (GetColour() != attr
.GetColour())
11110 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11111 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11115 SetColour(attr
.GetColourLong());
11119 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11121 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11124 // Partial equality test
11125 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11127 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11128 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11131 // Apply border to 'this', but not if the same as compareWith
11132 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11134 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11135 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11136 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11137 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11141 // Remove specified attributes from this object
11142 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11144 m_left
.RemoveStyle(attr
.m_left
);
11145 m_right
.RemoveStyle(attr
.m_right
);
11146 m_top
.RemoveStyle(attr
.m_top
);
11147 m_bottom
.RemoveStyle(attr
.m_bottom
);
11151 // Collects the attributes that are common to a range of content, building up a note of
11152 // which attributes are absent in some objects and which clash in some objects.
11153 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11155 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11156 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11157 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11158 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11161 // Set style of all borders
11162 void wxTextAttrBorders::SetStyle(int style
)
11164 m_left
.SetStyle(style
);
11165 m_right
.SetStyle(style
);
11166 m_top
.SetStyle(style
);
11167 m_bottom
.SetStyle(style
);
11170 // Set colour of all borders
11171 void wxTextAttrBorders::SetColour(unsigned long colour
)
11173 m_left
.SetColour(colour
);
11174 m_right
.SetColour(colour
);
11175 m_top
.SetColour(colour
);
11176 m_bottom
.SetColour(colour
);
11179 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11181 m_left
.SetColour(colour
);
11182 m_right
.SetColour(colour
);
11183 m_top
.SetColour(colour
);
11184 m_bottom
.SetColour(colour
);
11187 // Set width of all borders
11188 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11190 m_left
.SetWidth(width
);
11191 m_right
.SetWidth(width
);
11192 m_top
.SetWidth(width
);
11193 m_bottom
.SetWidth(width
);
11196 // Partial equality test
11197 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11199 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11205 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11209 if (!(compareWith
&& dim
== (*compareWith
)))
11216 // Collects the attributes that are common to a range of content, building up a note of
11217 // which attributes are absent in some objects and which clash in some objects.
11218 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11220 if (attr
.IsValid())
11222 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11226 if (!((*this) == attr
))
11228 clashingAttr
.SetValid(true);
11237 absentAttr
.SetValid(true);
11240 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11242 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11245 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11247 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11250 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11252 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11255 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11257 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11260 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11262 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11263 return ConvertTenthsMMToPixels(dim
.GetValue());
11264 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11265 return dim
.GetValue();
11266 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11268 wxASSERT(m_parentSize
!= wxDefaultSize
);
11269 if (direction
== wxHORIZONTAL
)
11270 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11272 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11281 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11283 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11284 return dim
.GetValue();
11285 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11286 return ConvertPixelsToTenthsMM(dim
.GetValue());
11294 // Partial equality test
11295 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11297 if (!m_left
.EqPartial(dims
.m_left
))
11300 if (!m_right
.EqPartial(dims
.m_right
))
11303 if (!m_top
.EqPartial(dims
.m_top
))
11306 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11312 // Apply border to 'this', but not if the same as compareWith
11313 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11315 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11316 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11317 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11318 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11323 // Remove specified attributes from this object
11324 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11326 if (attr
.m_left
.IsValid())
11328 if (attr
.m_right
.IsValid())
11330 if (attr
.m_top
.IsValid())
11332 if (attr
.m_bottom
.IsValid())
11338 // Collects the attributes that are common to a range of content, building up a note of
11339 // which attributes are absent in some objects and which clash in some objects.
11340 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11342 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11343 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11344 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11345 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11348 // Partial equality test
11349 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11351 if (!m_width
.EqPartial(size
.m_width
))
11354 if (!m_height
.EqPartial(size
.m_height
))
11360 // Apply border to 'this', but not if the same as compareWith
11361 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11363 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11364 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11369 // Remove specified attributes from this object
11370 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11372 if (attr
.m_width
.IsValid())
11374 if (attr
.m_height
.IsValid())
11380 // Collects the attributes that are common to a range of content, building up a note of
11381 // which attributes are absent in some objects and which clash in some objects.
11382 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11384 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11385 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11388 // Collects the attributes that are common to a range of content, building up a note of
11389 // which attributes are absent in some objects and which clash in some objects.
11390 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11392 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11393 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11395 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11397 if (attr
.HasFont())
11399 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11401 if (currentStyle
.HasFontSize())
11403 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11405 // Clash of attr - mark as such
11406 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11407 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11411 currentStyle
.SetFontSize(attr
.GetFontSize());
11414 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11416 if (currentStyle
.HasFontItalic())
11418 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11420 // Clash of attr - mark as such
11421 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11422 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11426 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11429 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11431 if (currentStyle
.HasFontFamily())
11433 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11435 // Clash of attr - mark as such
11436 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11437 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11441 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11444 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11446 if (currentStyle
.HasFontWeight())
11448 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11450 // Clash of attr - mark as such
11451 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11452 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11456 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11459 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11461 if (currentStyle
.HasFontFaceName())
11463 wxString
faceName1(currentStyle
.GetFontFaceName());
11464 wxString
faceName2(attr
.GetFontFaceName());
11466 if (faceName1
!= faceName2
)
11468 // Clash of attr - mark as such
11469 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11470 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11474 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11477 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11479 if (currentStyle
.HasFontUnderlined())
11481 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11483 // Clash of attr - mark as such
11484 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11485 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11489 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11493 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11495 if (currentStyle
.HasTextColour())
11497 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11499 // Clash of attr - mark as such
11500 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11501 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11505 currentStyle
.SetTextColour(attr
.GetTextColour());
11508 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11510 if (currentStyle
.HasBackgroundColour())
11512 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11514 // Clash of attr - mark as such
11515 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11516 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11520 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11523 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11525 if (currentStyle
.HasAlignment())
11527 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11529 // Clash of attr - mark as such
11530 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11531 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11535 currentStyle
.SetAlignment(attr
.GetAlignment());
11538 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11540 if (currentStyle
.HasTabs())
11542 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11544 // Clash of attr - mark as such
11545 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11546 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11550 currentStyle
.SetTabs(attr
.GetTabs());
11553 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11555 if (currentStyle
.HasLeftIndent())
11557 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11559 // Clash of attr - mark as such
11560 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11561 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11565 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11568 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11570 if (currentStyle
.HasRightIndent())
11572 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11574 // Clash of attr - mark as such
11575 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11576 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11580 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11583 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11585 if (currentStyle
.HasParagraphSpacingAfter())
11587 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11589 // Clash of attr - mark as such
11590 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11591 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11595 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11598 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11600 if (currentStyle
.HasParagraphSpacingBefore())
11602 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11604 // Clash of attr - mark as such
11605 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11606 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11610 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11613 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11615 if (currentStyle
.HasLineSpacing())
11617 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11619 // Clash of attr - mark as such
11620 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11621 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11625 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11628 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11630 if (currentStyle
.HasCharacterStyleName())
11632 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11634 // Clash of attr - mark as such
11635 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11636 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11640 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11643 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11645 if (currentStyle
.HasParagraphStyleName())
11647 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11649 // Clash of attr - mark as such
11650 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11651 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11655 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11658 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11660 if (currentStyle
.HasListStyleName())
11662 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11664 // Clash of attr - mark as such
11665 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11666 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11670 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11673 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11675 if (currentStyle
.HasBulletStyle())
11677 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11679 // Clash of attr - mark as such
11680 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11681 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11685 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11688 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11690 if (currentStyle
.HasBulletNumber())
11692 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11694 // Clash of attr - mark as such
11695 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11696 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11700 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11703 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11705 if (currentStyle
.HasBulletText())
11707 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11709 // Clash of attr - mark as such
11710 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11711 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11716 currentStyle
.SetBulletText(attr
.GetBulletText());
11717 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11721 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11723 if (currentStyle
.HasBulletName())
11725 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11727 // Clash of attr - mark as such
11728 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11729 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11734 currentStyle
.SetBulletName(attr
.GetBulletName());
11738 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11740 if (currentStyle
.HasURL())
11742 if (currentStyle
.GetURL() != attr
.GetURL())
11744 // Clash of attr - mark as such
11745 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11746 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
11751 currentStyle
.SetURL(attr
.GetURL());
11755 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
11757 if (currentStyle
.HasTextEffects())
11759 // We need to find the bits in the new attr that are different:
11760 // just look at those bits that are specified by the new attr.
11762 // We need to remove the bits and flags that are not common between current attr
11763 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11764 // previous styles.
11766 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
11767 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
11769 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
11771 // Find the text effects that were different, using XOR
11772 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
11774 // Clash of attr - mark as such
11775 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
11776 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
11781 currentStyle
.SetTextEffects(attr
.GetTextEffects());
11782 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
11785 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11786 // that we've looked at so far
11787 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
11788 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
11790 if (currentStyle
.GetTextEffectFlags() == 0)
11791 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
11794 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
11796 if (currentStyle
.HasOutlineLevel())
11798 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
11800 // Clash of attr - mark as such
11801 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11802 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11806 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
11810 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
11812 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
11814 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
11816 if (m_properties
.GetCount() != props
.GetCount())
11820 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11822 const wxVariant
& var1
= m_properties
[i
];
11823 int idx
= props
.Find(var1
.GetName());
11826 const wxVariant
& var2
= props
.m_properties
[idx
];
11827 if (!(var1
== var2
))
11834 wxArrayString
wxRichTextProperties::GetPropertyNames() const
11838 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11840 arr
.Add(m_properties
[i
].GetName());
11845 int wxRichTextProperties::Find(const wxString
& name
) const
11848 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11850 if (m_properties
[i
].GetName() == name
)
11856 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
11858 int idx
= Find(name
);
11859 if (idx
== wxNOT_FOUND
)
11860 SetProperty(name
, wxString());
11862 if (idx
!= wxNOT_FOUND
)
11864 return & (*this)[idx
];
11870 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
11872 static const wxVariant nullVariant
;
11873 int idx
= Find(name
);
11875 return m_properties
[idx
];
11877 return nullVariant
;
11880 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
11882 return GetProperty(name
).GetString();
11885 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
11887 return GetProperty(name
).GetLong();
11890 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
11892 return GetProperty(name
).GetBool();
11895 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
11897 return GetProperty(name
).GetDouble();
11900 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
11902 wxASSERT(!variant
.GetName().IsEmpty());
11904 int idx
= Find(variant
.GetName());
11907 m_properties
.Add(variant
);
11909 m_properties
[idx
] = variant
;
11912 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
11914 int idx
= Find(name
);
11915 wxVariant
var(variant
);
11919 m_properties
.Add(var
);
11921 m_properties
[idx
] = var
;
11924 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
11926 SetProperty(name
, wxVariant(value
, name
));
11929 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
11931 SetProperty(name
, wxVariant(value
, name
));
11934 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
11936 SetProperty(name
, wxVariant(value
, name
));
11939 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
11941 SetProperty(name
, wxVariant(value
, name
));
11944 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
11946 if (m_address
.GetCount() == 0)
11947 return topLevelContainer
;
11949 wxRichTextCompositeObject
* p
= topLevelContainer
;
11951 while (p
&& i
< m_address
.GetCount())
11953 int pos
= m_address
[i
];
11954 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
11955 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
11958 wxRichTextObject
* p1
= p
->GetChild(pos
);
11959 if (i
== (m_address
.GetCount()-1))
11962 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
11968 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
11972 if (topLevelContainer
== obj
)
11975 wxRichTextObject
* o
= obj
;
11978 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
11982 int pos
= p
->GetChildren().IndexOf(o
);
11986 m_address
.Insert(pos
, 0);
11988 if (p
== topLevelContainer
)
11997 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
11999 if (m_container
!= sel
.m_container
)
12001 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12004 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12005 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12010 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12011 // or none at the level of the object's container.
12012 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12016 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12018 if (container
== m_container
)
12021 container
= obj
->GetContainer();
12024 if (container
->GetParent())
12026 // If we found that our object's container is within the range of
12027 // a selection higher up, then assume the whole original object
12028 // is also selected.
12029 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12030 if (parentContainer
== m_container
)
12032 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12034 wxRichTextRangeArray ranges
;
12035 ranges
.Add(obj
->GetRange());
12040 container
= parentContainer
;
12049 return wxRichTextRangeArray();
12052 // Is the given position within the selection?
12053 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12059 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12060 return WithinSelection(pos
, selectionRanges
);
12064 // Is the given position within the selection range?
12065 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12068 for (i
= 0; i
< ranges
.GetCount(); i
++)
12070 const wxRichTextRange
& range
= ranges
[i
];
12071 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12077 // Is the given range completely within the selection range?
12078 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12081 for (i
= 0; i
< ranges
.GetCount(); i
++)
12083 const wxRichTextRange
& eachRange
= ranges
[i
];
12084 if (range
.IsWithin(eachRange
))