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(borderRect
);
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
+ rect
.width
- 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().GetBottom().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().GetBottom().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 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
957 // E.g. a cell that's 50% of its parent.
958 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
960 wxRect rect
= availableParentSpace
;
963 scale
= buffer
->GetScale();
965 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
967 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
968 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
970 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
971 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
973 // Can specify either left or right for the position (we're assuming we can't
974 // set the left and right edges to effectively set the size. Would we want to do that?)
975 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
977 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
979 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
981 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
982 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
983 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
988 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
990 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
992 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
994 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
995 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
996 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1001 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1002 rect
.SetWidth(availableParentSpace
.GetWidth());
1007 // Dump to output stream for debugging
1008 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1010 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1011 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");
1012 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");
1015 // Gets the containing buffer
1016 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1018 const wxRichTextObject
* obj
= this;
1019 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1020 obj
= obj
->GetParent();
1021 return wxDynamicCast(obj
, wxRichTextBuffer
);
1024 // Get the absolute object position, by traversing up the child/parent hierarchy
1025 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1027 wxPoint pt
= GetPosition();
1029 wxRichTextObject
* p
= GetParent();
1032 pt
= pt
+ p
->GetPosition();
1039 // Hit-testing: returns a flag indicating hit test details, plus
1040 // information about position
1041 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1044 return wxRICHTEXT_HITTEST_NONE
;
1046 wxRect rect
= GetRect();
1047 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1048 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1051 *contextObj
= GetParentContainer();
1052 textPosition
= GetRange().GetStart();
1053 return wxRICHTEXT_HITTEST_ON
;
1056 return wxRICHTEXT_HITTEST_NONE
;
1059 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1060 // lays out the object again using the maximum ('best') size
1061 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextBuffer
* buffer
,
1062 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1063 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1066 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1067 wxRect originalAvailableRect
= availableChildRect
;
1068 Layout(dc
, availableChildRect
, availableContainerSpace
, style
);
1070 wxSize maxSize
= GetMaxSize();
1072 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1074 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1076 // Redo the layout with a fixed, minimum size this time.
1077 Invalidate(wxRICHTEXT_ALL
);
1078 wxRichTextAttr
newAttr(attr
);
1079 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1080 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1082 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1084 // If a paragraph, align the whole paragraph.
1085 // Problem with this: if we're limited by a floating object, a line may be centered
1086 // w.r.t. the smaller resulting box rather than the actual available width.
1087 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1089 // centering, right-justification
1090 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1092 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1094 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1096 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1100 Layout(dc
, availableChildRect
, availableContainerSpace
, style
);
1114 // Move the object recursively, by adding the offset from old to new
1115 void wxRichTextObject::Move(const wxPoint
& pt
)
1122 * wxRichTextCompositeObject
1123 * This is the base for drawable objects.
1126 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1128 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1129 wxRichTextObject(parent
)
1133 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1138 /// Get the nth child
1139 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1141 wxASSERT ( n
< m_children
.GetCount() );
1143 return m_children
.Item(n
)->GetData();
1146 /// Append a child, returning the position
1147 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1149 m_children
.Append(child
);
1150 child
->SetParent(this);
1151 return m_children
.GetCount() - 1;
1154 /// Insert the child in front of the given object, or at the beginning
1155 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1159 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1160 m_children
.Insert(node
, child
);
1163 m_children
.Insert(child
);
1164 child
->SetParent(this);
1169 /// Delete the child
1170 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1172 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1175 wxRichTextObject
* obj
= node
->GetData();
1176 m_children
.Erase(node
);
1185 /// Delete all children
1186 bool wxRichTextCompositeObject::DeleteChildren()
1188 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1191 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1193 wxRichTextObject
* child
= node
->GetData();
1194 child
->Dereference(); // Only delete if reference count is zero
1196 node
= node
->GetNext();
1197 m_children
.Erase(oldNode
);
1203 /// Get the child count
1204 size_t wxRichTextCompositeObject::GetChildCount() const
1206 return m_children
.GetCount();
1210 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1212 wxRichTextObject::Copy(obj
);
1216 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1219 wxRichTextObject
* child
= node
->GetData();
1220 wxRichTextObject
* newChild
= child
->Clone();
1221 newChild
->SetParent(this);
1222 m_children
.Append(newChild
);
1224 node
= node
->GetNext();
1228 /// Hit-testing: returns a flag indicating hit test details, plus
1229 /// information about position
1230 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1233 return wxRICHTEXT_HITTEST_NONE
;
1235 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1238 wxRichTextObject
* child
= node
->GetData();
1240 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1242 // Just check if we hit the overall object
1243 int ret
= child
->wxRichTextObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1244 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 else if (child
->IsShown())
1249 int ret
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1250 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1254 node
= node
->GetNext();
1257 return wxRICHTEXT_HITTEST_NONE
;
1260 /// Finds the absolute position and row height for the given character position
1261 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1263 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1266 wxRichTextObject
* child
= node
->GetData();
1268 // Don't recurse if the child is a top-level object,
1269 // such as a text box, because the character position will no longer
1270 // apply. By definition, a top-level object has its own range of
1271 // character positions.
1272 if (!child
->IsTopLevel() && child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
))
1275 node
= node
->GetNext();
1282 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1284 long current
= start
;
1285 long lastEnd
= current
;
1293 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1296 wxRichTextObject
* child
= node
->GetData();
1299 child
->CalculateRange(current
, childEnd
);
1302 current
= childEnd
+ 1;
1304 node
= node
->GetNext();
1309 // A top-level object always has a range of size 1,
1310 // because its children don't count at this level.
1312 m_range
.SetRange(start
, start
);
1314 // An object with no children has zero length
1315 if (m_children
.GetCount() == 0)
1317 m_ownRange
.SetRange(0, lastEnd
);
1323 // An object with no children has zero length
1324 if (m_children
.GetCount() == 0)
1327 m_range
.SetRange(start
, end
);
1331 /// Delete range from layout.
1332 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1334 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1338 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1339 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1341 // Delete the range in each paragraph
1343 // When a chunk has been deleted, internally the content does not
1344 // now match the ranges.
1345 // However, so long as deletion is not done on the same object twice this is OK.
1346 // If you may delete content from the same object twice, recalculate
1347 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1348 // adjust the range you're deleting accordingly.
1350 if (!obj
->GetRange().IsOutside(range
))
1352 // No need to delete within a top-level object; just removing this object will do fine
1353 if (!obj
->IsTopLevel())
1354 obj
->DeleteRange(range
);
1356 // Delete an empty object, or paragraph within this range.
1357 if (obj
->IsEmpty() ||
1358 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1360 // An empty paragraph has length 1, so won't be deleted unless the
1361 // whole range is deleted.
1362 RemoveChild(obj
, true);
1372 /// Get any text in this object for the given range
1373 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1376 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1379 wxRichTextObject
* child
= node
->GetData();
1380 wxRichTextRange childRange
= range
;
1381 if (!child
->GetRange().IsOutside(range
))
1383 childRange
.LimitTo(child
->GetRange());
1385 wxString childText
= child
->GetTextForRange(childRange
);
1389 node
= node
->GetNext();
1395 /// Get the child object at the given character position
1396 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1398 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1401 wxRichTextObject
* child
= node
->GetData();
1402 if (child
->GetRange().GetStart() == pos
)
1404 node
= node
->GetNext();
1409 /// Recursively merge all pieces that can be merged.
1410 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1412 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1415 wxRichTextObject
* child
= node
->GetData();
1416 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1418 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1420 composite
->Defragment();
1422 if (node
->GetNext())
1424 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1425 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1427 nextChild
->Dereference();
1428 m_children
.Erase(node
->GetNext());
1430 // Don't set node -- we'll see if we can merge again with the next
1434 node
= node
->GetNext();
1437 node
= node
->GetNext();
1440 node
= node
->GetNext();
1443 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1444 if (GetChildCount() > 1)
1446 node
= m_children
.GetFirst();
1449 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1450 wxRichTextObject
* child
= node
->GetData();
1451 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1453 if (child
->IsEmpty())
1455 child
->Dereference();
1456 m_children
.Erase(node
);
1461 node
= node
->GetNext();
1468 /// Dump to output stream for debugging
1469 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1471 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1474 wxRichTextObject
* child
= node
->GetData();
1475 child
->Dump(stream
);
1476 node
= node
->GetNext();
1480 /// Get/set the object size for the given range. Returns false if the range
1481 /// is invalid for this object.
1482 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1484 if (!range
.IsWithin(GetRange()))
1489 wxArrayInt childExtents
;
1496 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1499 wxRichTextObject
* child
= node
->GetData();
1500 if (!child
->GetRange().IsOutside(range
))
1502 // Floating objects have a zero size within the paragraph.
1503 if (child
->IsFloating())
1508 if (partialExtents
->GetCount() > 0)
1509 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1513 partialExtents
->Add(0 /* zero size */ + lastSize
);
1520 wxRichTextRange rangeToUse
= range
;
1521 rangeToUse
.LimitTo(child
->GetRange());
1522 if (child
->IsTopLevel())
1523 rangeToUse
= child
->GetOwnRange();
1525 int childDescent
= 0;
1527 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1528 // but it's only going to be used after caching has taken place.
1529 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1531 childDescent
= child
->GetDescent();
1532 childSize
= child
->GetCachedSize();
1534 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1535 sz
.x
+= childSize
.x
;
1536 descent
= wxMax(descent
, childDescent
);
1538 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1540 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1541 sz
.x
+= childSize
.x
;
1542 descent
= wxMax(descent
, childDescent
);
1544 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1546 child
->SetCachedSize(childSize
);
1547 child
->SetDescent(childDescent
);
1553 if (partialExtents
->GetCount() > 0)
1554 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1559 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1561 partialExtents
->Add(childExtents
[i
] + lastSize
);
1571 node
= node
->GetNext();
1577 // Invalidate the buffer. With no argument, invalidates whole buffer.
1578 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1580 wxRichTextObject::Invalidate(invalidRange
);
1582 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1585 wxRichTextObject
* child
= node
->GetData();
1586 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1590 else if (child
->IsTopLevel())
1592 if (invalidRange
== wxRICHTEXT_NONE
)
1593 child
->Invalidate(wxRICHTEXT_NONE
);
1595 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1598 child
->Invalidate(invalidRange
);
1599 node
= node
->GetNext();
1603 // Move the object recursively, by adding the offset from old to new
1604 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1606 wxPoint oldPos
= GetPosition();
1608 wxPoint offset
= pt
- oldPos
;
1610 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1613 wxRichTextObject
* child
= node
->GetData();
1614 wxPoint childPos
= child
->GetPosition() + offset
;
1615 child
->Move(childPos
);
1616 node
= node
->GetNext();
1622 * wxRichTextParagraphLayoutBox
1623 * This box knows how to lay out paragraphs.
1626 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1628 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1629 wxRichTextCompositeObject(parent
)
1634 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1636 if (m_floatCollector
)
1638 delete m_floatCollector
;
1639 m_floatCollector
= NULL
;
1643 /// Initialize the object.
1644 void wxRichTextParagraphLayoutBox::Init()
1648 // For now, assume is the only box and has no initial size.
1649 m_range
= wxRichTextRange(0, -1);
1650 m_ownRange
= wxRichTextRange(0, -1);
1652 m_invalidRange
= wxRICHTEXT_ALL
;
1655 m_partialParagraph
= false;
1656 m_floatCollector
= NULL
;
1659 void wxRichTextParagraphLayoutBox::Clear()
1663 if (m_floatCollector
)
1664 delete m_floatCollector
;
1665 m_floatCollector
= NULL
;
1666 m_partialParagraph
= false;
1670 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1674 wxRichTextCompositeObject::Copy(obj
);
1676 m_partialParagraph
= obj
.m_partialParagraph
;
1677 m_defaultAttributes
= obj
.m_defaultAttributes
;
1680 // Gather information about floating objects; only gather floats for those paragraphs that
1681 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1683 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1685 if (m_floatCollector
!= NULL
)
1686 delete m_floatCollector
;
1687 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1688 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1689 // Only gather floats up to the point we'll start formatting paragraphs.
1690 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1692 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1693 wxASSERT (child
!= NULL
);
1695 m_floatCollector
->CollectFloat(child
);
1696 node
= node
->GetNext();
1702 // Returns the style sheet associated with the overall buffer.
1703 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1705 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1708 // Get the number of floating objects at this level
1709 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1711 if (m_floatCollector
)
1712 return m_floatCollector
->GetFloatingObjectCount();
1717 // Get a list of floating objects
1718 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1720 if (m_floatCollector
)
1722 return m_floatCollector
->GetFloatingObjects(objects
);
1729 void wxRichTextParagraphLayoutBox::UpdateRanges()
1733 start
= GetRange().GetStart();
1735 CalculateRange(start
, end
);
1739 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1742 return wxRICHTEXT_HITTEST_NONE
;
1744 int ret
= wxRICHTEXT_HITTEST_NONE
;
1745 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1746 ret
= m_floatCollector
->HitTest(dc
, pt
, textPosition
, obj
, flags
);
1748 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1749 return wxRichTextCompositeObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1757 /// Draw the floating objects
1758 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1760 if (m_floatCollector
)
1761 m_floatCollector
->Draw(dc
, range
, selection
, rect
, descent
, style
);
1764 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1769 from
->RemoveChild(obj
);
1770 to
->AppendChild(obj
);
1774 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1779 wxRect
thisRect(GetPosition(), GetCachedSize());
1782 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1783 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1785 // Don't draw guidelines if at top level
1786 int theseFlags
= flags
;
1788 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1789 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), thisRect
, theseFlags
);
1791 DrawFloats(dc
, range
, selection
, rect
, descent
, style
);
1792 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1795 wxRichTextObject
* child
= node
->GetData();
1797 if (child
&& !child
->GetRange().IsOutside(range
))
1799 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1800 wxRichTextRange childRange
= range
;
1801 if (child
->IsTopLevel())
1803 childRange
= child
->GetOwnRange();
1806 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1811 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1816 child
->Draw(dc
, childRange
, selection
, rect
, descent
, style
);
1819 node
= node
->GetNext();
1824 /// Lay the item out
1825 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1827 SetPosition(rect
.GetPosition());
1832 wxRect availableSpace
;
1833 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1835 // If only laying out a specific area, the passed rect has a different meaning:
1836 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1837 // so that during a size, only the visible part will be relaid out, or
1838 // it would take too long causing flicker. As an approximation, we assume that
1839 // everything up to the start of the visible area is laid out correctly.
1842 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1843 availableSpace
= GetAvailableContentArea(dc
, rect2
);
1845 // Invalidate the part of the buffer from the first visible line
1846 // to the end. If other parts of the buffer are currently invalid,
1847 // then they too will be taken into account if they are above
1848 // the visible point.
1850 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1852 startPos
= line
->GetAbsoluteRange().GetStart();
1854 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1858 availableSpace
= GetAvailableContentArea(dc
, rect
);
1861 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1862 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), GetAttributes(), leftMargin
, rightMargin
,
1863 topMargin
, bottomMargin
);
1868 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1869 int maxMaxWidth
= 0;
1871 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1872 int maxMinWidth
= 0;
1874 // If we have vertical alignment, we must recalculate everything.
1875 bool hasVerticalAlignment
= (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1876 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1878 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1880 bool layoutAll
= true;
1882 // Get invalid range, rounding to paragraph start/end.
1883 wxRichTextRange invalidRange
= GetInvalidRange(true);
1885 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1888 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1890 else // If we know what range is affected, start laying out from that point on.
1891 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1893 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1896 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1897 wxRichTextObjectList::compatibility_iterator previousNode
;
1899 previousNode
= firstNode
->GetPrevious();
1904 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1905 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1908 // Now we're going to start iterating from the first affected paragraph.
1916 // Gather information about only those floating objects that will not be formatted,
1917 // after which floats will be gathered per-paragraph during layout.
1918 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1920 // A way to force speedy rest-of-buffer layout (the 'else' below)
1921 bool forceQuickLayout
= false;
1923 // First get the size of the paragraphs we won't be laying out
1924 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1925 while (n
&& n
!= node
)
1927 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1930 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1931 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1932 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1939 // Assume this box only contains paragraphs
1941 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1942 // Unsure if this is needed
1943 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1945 if (child
&& child
->IsShown())
1947 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1948 if ( !forceQuickLayout
&&
1950 child
->GetLines().IsEmpty() ||
1951 !child
->GetRange().IsOutside(invalidRange
)) )
1953 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1954 // lays out the object again using the minimum size
1955 child
->LayoutToBestSize(dc
, GetBuffer(),
1956 GetAttributes(), child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1958 // Layout must set the cached size
1959 availableSpace
.y
+= child
->GetCachedSize().y
;
1960 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1961 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1962 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1964 // If we're just formatting the visible part of the buffer,
1965 // and we're now past the bottom of the window, and we don't have any
1966 // floating objects (since they may cause wrapping to change for the rest of the
1967 // the buffer), start quick layout.
1968 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1969 forceQuickLayout
= true;
1973 // We're outside the immediately affected range, so now let's just
1974 // move everything up or down. This assumes that all the children have previously
1975 // been laid out and have wrapped line lists associated with them.
1976 // TODO: check all paragraphs before the affected range.
1978 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1982 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1985 if (child
->GetLines().GetCount() == 0)
1987 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1988 // lays out the object again using the minimum size
1989 child
->LayoutToBestSize(dc
, GetBuffer(),
1990 GetAttributes(), child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1992 //child->Layout(dc, availableChildRect, style);
1995 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1997 availableSpace
.y
+= child
->GetCachedSize().y
;
1998 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1999 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2000 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2003 node
= node
->GetNext();
2009 node
= node
->GetNext();
2012 node
= m_children
.GetLast();
2013 if (node
&& node
->GetData()->IsShown())
2015 wxRichTextObject
* child
= node
->GetData();
2016 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2019 maxHeight
= 0; // topMargin + bottomMargin;
2021 if (GetAttributes().GetTextBoxAttr().GetSize().GetWidth().IsValid())
2023 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, GetAttributes(), parentRect
, parentRect
);
2024 int w
= r
.GetWidth();
2026 // Convert external to content rect
2027 w
= w
- leftMargin
- rightMargin
;
2028 maxWidth
= wxMax(maxWidth
, w
);
2029 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2032 // TODO: (also in para layout) should set the
2033 // object's size to an absolute one if specified,
2034 // but if not specified, calculate it from content.
2036 // We need to add back the margins etc.
2038 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2039 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2040 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2041 SetCachedSize(marginRect
.GetSize());
2044 // The maximum size is the greatest of all maximum widths for all paragraphs.
2046 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2047 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2048 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2049 SetMaxSize(marginRect
.GetSize());
2052 // The minimum size is the greatest of all minimum widths for all paragraphs.
2054 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2055 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2056 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2057 SetMinSize(marginRect
.GetSize());
2060 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2061 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2064 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2065 if (leftOverSpace
> 0)
2067 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2069 yOffset
= (leftOverSpace
/2);
2071 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2073 yOffset
= leftOverSpace
;
2077 // Move all the children to vertically align the content
2078 // This doesn't take into account floating objects, unfortunately.
2081 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2084 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2086 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2088 node
= node
->GetNext();
2093 m_invalidRange
= wxRICHTEXT_NONE
;
2098 /// Get/set the size for the given range.
2099 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2103 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2104 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2106 // First find the first paragraph whose starting position is within the range.
2107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2110 // child is a paragraph
2111 wxRichTextObject
* child
= node
->GetData();
2112 const wxRichTextRange
& r
= child
->GetRange();
2114 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2120 node
= node
->GetNext();
2123 // Next find the last paragraph containing part of the range
2124 node
= m_children
.GetFirst();
2127 // child is a paragraph
2128 wxRichTextObject
* child
= node
->GetData();
2129 const wxRichTextRange
& r
= child
->GetRange();
2131 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2137 node
= node
->GetNext();
2140 if (!startPara
|| !endPara
)
2143 // Now we can add up the sizes
2144 for (node
= startPara
; node
; node
= node
->GetNext())
2146 // child is a paragraph
2147 wxRichTextObject
* child
= node
->GetData();
2148 const wxRichTextRange
& childRange
= child
->GetRange();
2149 wxRichTextRange rangeToFind
= range
;
2150 rangeToFind
.LimitTo(childRange
);
2152 if (child
->IsTopLevel())
2153 rangeToFind
= child
->GetOwnRange();
2157 int childDescent
= 0;
2158 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2160 descent
= wxMax(childDescent
, descent
);
2162 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2163 sz
.y
+= childSize
.y
;
2165 if (node
== endPara
)
2174 /// Get the paragraph at the given position
2175 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2180 // First find the first paragraph whose starting position is within the range.
2181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2184 // child is a paragraph
2185 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2186 // wxASSERT (child != NULL);
2190 // Return first child in buffer if position is -1
2194 if (child
->GetRange().Contains(pos
))
2198 node
= node
->GetNext();
2203 /// Get the line at the given position
2204 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2209 // First find the first paragraph whose starting position is within the range.
2210 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2213 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2214 if (obj
->GetRange().Contains(pos
))
2216 // child is a paragraph
2217 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2218 // wxASSERT (child != NULL);
2222 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2225 wxRichTextLine
* line
= node2
->GetData();
2227 wxRichTextRange range
= line
->GetAbsoluteRange();
2229 if (range
.Contains(pos
) ||
2231 // If the position is end-of-paragraph, then return the last line of
2232 // of the paragraph.
2233 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2236 node2
= node2
->GetNext();
2241 node
= node
->GetNext();
2244 int lineCount
= GetLineCount();
2246 return GetLineForVisibleLineNumber(lineCount
-1);
2251 /// Get the line at the given y pixel position, or the last line.
2252 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2254 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2257 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2258 // wxASSERT (child != NULL);
2262 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2265 wxRichTextLine
* line
= node2
->GetData();
2267 wxRect
rect(line
->GetRect());
2269 if (y
<= rect
.GetBottom())
2272 node2
= node2
->GetNext();
2276 node
= node
->GetNext();
2280 int lineCount
= GetLineCount();
2282 return GetLineForVisibleLineNumber(lineCount
-1);
2287 /// Get the number of visible lines
2288 int wxRichTextParagraphLayoutBox::GetLineCount() const
2292 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2295 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2296 // wxASSERT (child != NULL);
2299 count
+= child
->GetLines().GetCount();
2301 node
= node
->GetNext();
2307 /// Get the paragraph for a given line
2308 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2310 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2313 /// Get the line size at the given position
2314 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2316 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2319 return line
->GetSize();
2322 return wxSize(0, 0);
2326 /// Convenience function to add a paragraph of text
2327 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2329 // Don't use the base style, just the default style, and the base style will
2330 // be combined at display time.
2331 // Divide into paragraph and character styles.
2333 wxRichTextAttr defaultCharStyle
;
2334 wxRichTextAttr defaultParaStyle
;
2336 // If the default style is a named paragraph style, don't apply any character formatting
2337 // to the initial text string.
2338 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2340 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2342 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2345 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2347 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2348 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2350 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2356 return para
->GetRange();
2359 /// Adds multiple paragraphs, based on newlines.
2360 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2362 // Don't use the base style, just the default style, and the base style will
2363 // be combined at display time.
2364 // Divide into paragraph and character styles.
2366 wxRichTextAttr defaultCharStyle
;
2367 wxRichTextAttr defaultParaStyle
;
2369 // If the default style is a named paragraph style, don't apply any character formatting
2370 // to the initial text string.
2371 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2373 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2375 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2378 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2380 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2381 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2383 wxRichTextParagraph
* firstPara
= NULL
;
2384 wxRichTextParagraph
* lastPara
= NULL
;
2386 wxRichTextRange
range(-1, -1);
2389 size_t len
= text
.length();
2391 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2400 wxChar ch
= text
[i
];
2401 if (ch
== wxT('\n') || ch
== wxT('\r'))
2405 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2406 plainText
->SetText(line
);
2408 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2413 line
= wxEmptyString
;
2424 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2425 plainText
->SetText(line
);
2430 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2433 /// Convenience function to add an image
2434 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2436 // Don't use the base style, just the default style, and the base style will
2437 // be combined at display time.
2438 // Divide into paragraph and character styles.
2440 wxRichTextAttr defaultCharStyle
;
2441 wxRichTextAttr defaultParaStyle
;
2443 // If the default style is a named paragraph style, don't apply any character formatting
2444 // to the initial text string.
2445 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2447 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2449 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2452 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2454 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2455 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2457 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2459 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2463 return para
->GetRange();
2467 /// Insert fragment into this box at the given position. If partialParagraph is true,
2468 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2471 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2473 // First, find the first paragraph whose starting position is within the range.
2474 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2477 wxRichTextAttr originalAttr
= para
->GetAttributes();
2479 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2481 // Now split at this position, returning the object to insert the new
2482 // ones in front of.
2483 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2485 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2486 // text, for example, so let's optimize.
2488 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2490 // Add the first para to this para...
2491 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2495 // Iterate through the fragment paragraph inserting the content into this paragraph.
2496 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2497 wxASSERT (firstPara
!= NULL
);
2499 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2502 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2507 para
->AppendChild(newObj
);
2511 // Insert before nextObject
2512 para
->InsertChild(newObj
, nextObject
);
2515 objectNode
= objectNode
->GetNext();
2522 // Procedure for inserting a fragment consisting of a number of
2525 // 1. Remove and save the content that's after the insertion point, for adding
2526 // back once we've added the fragment.
2527 // 2. Add the content from the first fragment paragraph to the current
2529 // 3. Add remaining fragment paragraphs after the current paragraph.
2530 // 4. Add back the saved content from the first paragraph. If partialParagraph
2531 // is true, add it to the last paragraph added and not a new one.
2533 // 1. Remove and save objects after split point.
2534 wxList savedObjects
;
2536 para
->MoveToList(nextObject
, savedObjects
);
2538 // 2. Add the content from the 1st fragment paragraph.
2539 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2543 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2544 wxASSERT(firstPara
!= NULL
);
2546 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2547 para
->SetAttributes(firstPara
->GetAttributes());
2549 // Save empty paragraph attributes for appending later
2550 // These are character attributes deliberately set for a new paragraph. Without this,
2551 // we couldn't pass default attributes when appending a new paragraph.
2552 wxRichTextAttr emptyParagraphAttributes
;
2554 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2556 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2557 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2561 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2564 para
->AppendChild(newObj
);
2566 objectNode
= objectNode
->GetNext();
2569 // 3. Add remaining fragment paragraphs after the current paragraph.
2570 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2571 wxRichTextObject
* nextParagraph
= NULL
;
2572 if (nextParagraphNode
)
2573 nextParagraph
= nextParagraphNode
->GetData();
2575 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2576 wxRichTextParagraph
* finalPara
= para
;
2578 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2580 // If there was only one paragraph, we need to insert a new one.
2583 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2584 wxASSERT( para
!= NULL
);
2586 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2589 InsertChild(finalPara
, nextParagraph
);
2591 AppendChild(finalPara
);
2596 // If there was only one paragraph, or we have full paragraphs in our fragment,
2597 // we need to insert a new one.
2600 finalPara
= new wxRichTextParagraph
;
2603 InsertChild(finalPara
, nextParagraph
);
2605 AppendChild(finalPara
);
2608 // 4. Add back the remaining content.
2612 finalPara
->MoveFromList(savedObjects
);
2614 // Ensure there's at least one object
2615 if (finalPara
->GetChildCount() == 0)
2617 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2618 text
->SetAttributes(emptyParagraphAttributes
);
2620 finalPara
->AppendChild(text
);
2624 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2625 finalPara
->SetAttributes(firstPara
->GetAttributes());
2626 else if (finalPara
&& finalPara
!= para
)
2627 finalPara
->SetAttributes(originalAttr
);
2635 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2638 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2639 wxASSERT( para
!= NULL
);
2641 AppendChild(para
->Clone());
2650 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2651 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2652 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2654 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2657 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2658 wxASSERT( para
!= NULL
);
2660 if (!para
->GetRange().IsOutside(range
))
2662 fragment
.AppendChild(para
->Clone());
2667 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2668 if (!fragment
.IsEmpty())
2670 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2671 wxASSERT( firstPara
!= NULL
);
2673 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2674 wxASSERT( lastPara
!= NULL
);
2676 if (!firstPara
|| !lastPara
)
2679 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2681 long firstPos
= firstPara
->GetRange().GetStart();
2683 // Adjust for renumbering from zero
2684 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2687 fragment
.CalculateRange(0, end
);
2689 // Chop off the start of the paragraph
2690 if (topTailRange
.GetStart() > 0)
2692 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2693 firstPara
->DeleteRange(r
);
2695 // Make sure the numbering is correct
2696 fragment
.CalculateRange(0, end
);
2698 // Now, we've deleted some positions, so adjust the range
2700 topTailRange
.SetStart(range
.GetLength());
2701 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2705 topTailRange
.SetStart(range
.GetLength());
2706 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2709 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2711 lastPara
->DeleteRange(topTailRange
);
2713 // Make sure the numbering is correct
2715 fragment
.CalculateRange(0, end
);
2717 // We only have part of a paragraph at the end
2718 fragment
.SetPartialParagraph(true);
2722 // We have a partial paragraph (don't save last new paragraph marker)
2723 // or complete paragraph
2724 fragment
.SetPartialParagraph(isFragment
);
2731 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2732 /// starting from zero at the start of the buffer.
2733 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2740 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2743 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2744 // wxASSERT( child != NULL );
2748 if (child
->GetRange().Contains(pos
))
2750 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2753 wxRichTextLine
* line
= node2
->GetData();
2754 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2756 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2758 // If the caret is displayed at the end of the previous wrapped line,
2759 // we want to return the line it's _displayed_ at (not the actual line
2760 // containing the position).
2761 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2762 return lineCount
- 1;
2769 node2
= node2
->GetNext();
2771 // If we didn't find it in the lines, it must be
2772 // the last position of the paragraph. So return the last line.
2776 lineCount
+= child
->GetLines().GetCount();
2779 node
= node
->GetNext();
2786 /// Given a line number, get the corresponding wxRichTextLine object.
2787 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2791 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2794 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2795 // wxASSERT(child != NULL);
2799 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2801 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2804 wxRichTextLine
* line
= node2
->GetData();
2806 if (lineCount
== lineNumber
)
2811 node2
= node2
->GetNext();
2815 lineCount
+= child
->GetLines().GetCount();
2818 node
= node
->GetNext();
2825 /// Delete range from layout.
2826 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2828 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2830 wxRichTextParagraph
* firstPara
= NULL
;
2833 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2834 // wxASSERT (obj != NULL);
2836 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2840 // Delete the range in each paragraph
2842 if (!obj
->GetRange().IsOutside(range
))
2844 // Deletes the content of this object within the given range
2845 obj
->DeleteRange(range
);
2847 wxRichTextRange thisRange
= obj
->GetRange();
2848 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2850 // If the whole paragraph is within the range to delete,
2851 // delete the whole thing.
2852 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2854 // Delete the whole object
2855 RemoveChild(obj
, true);
2858 else if (!firstPara
)
2861 // If the range includes the paragraph end, we need to join this
2862 // and the next paragraph.
2863 if (range
.GetEnd() <= thisRange
.GetEnd())
2865 // We need to move the objects from the next paragraph
2866 // to this paragraph
2868 wxRichTextParagraph
* nextParagraph
= NULL
;
2869 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2870 nextParagraph
= obj
;
2873 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2875 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2878 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2880 wxRichTextAttr nextParaAttr
;
2881 if (applyFinalParagraphStyle
)
2883 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2884 // not the next one.
2885 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2886 nextParaAttr
= thisAttr
;
2888 nextParaAttr
= nextParagraph
->GetAttributes();
2891 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2893 // Move the objects to the previous para
2894 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2898 wxRichTextObject
* obj1
= node1
->GetData();
2900 firstPara
->AppendChild(obj1
);
2902 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2903 nextParagraph
->GetChildren().Erase(node1
);
2908 // Delete the paragraph
2909 RemoveChild(nextParagraph
, true);
2912 // Avoid empty paragraphs
2913 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2915 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2916 firstPara
->AppendChild(text
);
2919 if (applyFinalParagraphStyle
)
2920 firstPara
->SetAttributes(nextParaAttr
);
2933 /// Get any text in this object for the given range
2934 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2938 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2941 wxRichTextObject
* child
= node
->GetData();
2942 if (!child
->GetRange().IsOutside(range
))
2944 wxRichTextRange childRange
= range
;
2945 childRange
.LimitTo(child
->GetRange());
2947 wxString childText
= child
->GetTextForRange(childRange
);
2951 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2956 node
= node
->GetNext();
2962 /// Get all the text
2963 wxString
wxRichTextParagraphLayoutBox::GetText() const
2965 return GetTextForRange(GetOwnRange());
2968 /// Get the paragraph by number
2969 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2971 if ((size_t) paragraphNumber
>= GetChildCount())
2974 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2977 /// Get the length of the paragraph
2978 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2980 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2982 return para
->GetRange().GetLength() - 1; // don't include newline
2987 /// Get the text of the paragraph
2988 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2990 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2992 return para
->GetTextForRange(para
->GetRange());
2994 return wxEmptyString
;
2997 /// Convert zero-based line column and paragraph number to a position.
2998 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3000 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3003 return para
->GetRange().GetStart() + x
;
3009 /// Convert zero-based position to line column and paragraph number
3010 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3012 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3016 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3019 wxRichTextObject
* child
= node
->GetData();
3023 node
= node
->GetNext();
3027 *x
= pos
- para
->GetRange().GetStart();
3035 /// Get the leaf object in a paragraph at this position.
3036 /// Given a line number, get the corresponding wxRichTextLine object.
3037 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3039 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3042 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3046 wxRichTextObject
* child
= node
->GetData();
3047 if (child
->GetRange().Contains(position
))
3050 node
= node
->GetNext();
3052 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3053 return para
->GetChildren().GetLast()->GetData();
3058 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3059 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3061 bool characterStyle
= false;
3062 bool paragraphStyle
= false;
3064 if (style
.IsCharacterStyle())
3065 characterStyle
= true;
3066 if (style
.IsParagraphStyle())
3067 paragraphStyle
= true;
3069 wxRichTextBuffer
* buffer
= GetBuffer();
3071 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3072 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3073 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3074 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3075 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3076 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3078 // Apply paragraph style first, if any
3079 wxRichTextAttr
wholeStyle(style
);
3081 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3083 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3085 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3088 // Limit the attributes to be set to the content to only character attributes.
3089 wxRichTextAttr
characterAttributes(wholeStyle
);
3090 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3092 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3094 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3096 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3099 // If we are associated with a control, make undoable; otherwise, apply immediately
3102 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3104 wxRichTextAction
* action
= NULL
;
3106 if (haveControl
&& withUndo
)
3108 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3109 action
->SetRange(range
);
3110 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3113 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3116 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3117 // wxASSERT (para != NULL);
3119 if (para
&& para
->GetChildCount() > 0)
3121 // Stop searching if we're beyond the range of interest
3122 if (para
->GetRange().GetStart() > range
.GetEnd())
3125 if (!para
->GetRange().IsOutside(range
))
3127 // We'll be using a copy of the paragraph to make style changes,
3128 // not updating the buffer directly.
3129 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3131 if (haveControl
&& withUndo
)
3133 newPara
= new wxRichTextParagraph(*para
);
3134 action
->GetNewParagraphs().AppendChild(newPara
);
3136 // Also store the old ones for Undo
3137 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3142 // If we're specifying paragraphs only, then we really mean character formatting
3143 // to be included in the paragraph style
3144 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3148 // Removes the given style from the paragraph
3149 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3151 else if (resetExistingStyle
)
3152 newPara
->GetAttributes() = wholeStyle
;
3157 // Only apply attributes that will make a difference to the combined
3158 // style as seen on the display
3159 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3160 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3163 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3167 // When applying paragraph styles dynamically, don't change the text objects' attributes
3168 // since they will computed as needed. Only apply the character styling if it's _only_
3169 // character styling. This policy is subject to change and might be put under user control.
3171 // Hm. we might well be applying a mix of paragraph and character styles, in which
3172 // case we _do_ want to apply character styles regardless of what para styles are set.
3173 // But if we're applying a paragraph style, which has some character attributes, but
3174 // we only want the paragraphs to hold this character style, then we _don't_ want to
3175 // apply the character style. So we need to be able to choose.
3177 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3179 wxRichTextRange
childRange(range
);
3180 childRange
.LimitTo(newPara
->GetRange());
3182 // Find the starting position and if necessary split it so
3183 // we can start applying a different style.
3184 // TODO: check that the style actually changes or is different
3185 // from style outside of range
3186 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3187 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3189 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3190 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3192 firstObject
= newPara
->SplitAt(range
.GetStart());
3194 // Increment by 1 because we're apply the style one _after_ the split point
3195 long splitPoint
= childRange
.GetEnd();
3196 if (splitPoint
!= newPara
->GetRange().GetEnd())
3200 if (splitPoint
== newPara
->GetRange().GetEnd())
3201 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3203 // lastObject is set as a side-effect of splitting. It's
3204 // returned as the object before the new object.
3205 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3207 wxASSERT(firstObject
!= NULL
);
3208 wxASSERT(lastObject
!= NULL
);
3210 if (!firstObject
|| !lastObject
)
3213 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3214 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3216 wxASSERT(firstNode
);
3219 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3223 wxRichTextObject
* child
= node2
->GetData();
3227 // Removes the given style from the paragraph
3228 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3230 else if (resetExistingStyle
)
3231 child
->GetAttributes() = characterAttributes
;
3236 // Only apply attributes that will make a difference to the combined
3237 // style as seen on the display
3238 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3239 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3242 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3245 if (node2
== lastNode
)
3248 node2
= node2
->GetNext();
3254 node
= node
->GetNext();
3257 // Do action, or delay it until end of batch.
3258 if (haveControl
&& withUndo
)
3259 buffer
->SubmitAction(action
);
3264 // Just change the attributes for this single object.
3265 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3267 wxRichTextBuffer
* buffer
= GetBuffer();
3268 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3269 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3270 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3272 wxRichTextAction
*action
= NULL
;
3273 wxRichTextAttr newAttr
= obj
->GetAttributes();
3274 if (resetExistingStyle
)
3277 newAttr
.Apply(textAttr
);
3279 if (haveControl
&& withUndo
)
3281 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3282 action
->SetRange(obj
->GetRange().FromInternal());
3283 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3284 action
->MakeObject(obj
);
3286 action
->GetAttributes() = newAttr
;
3289 obj
->GetAttributes() = newAttr
;
3291 if (haveControl
&& withUndo
)
3292 buffer
->SubmitAction(action
);
3295 /// Get the text attributes for this position.
3296 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3298 return DoGetStyle(position
, style
, true);
3301 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3303 return DoGetStyle(position
, style
, false);
3306 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3307 /// context attributes.
3308 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3310 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3312 if (style
.IsParagraphStyle())
3314 obj
= GetParagraphAtPosition(position
);
3319 // Start with the base style
3320 style
= GetAttributes();
3322 // Apply the paragraph style
3323 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3326 style
= obj
->GetAttributes();
3333 obj
= GetLeafObjectAtPosition(position
);
3338 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3339 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3342 style
= obj
->GetAttributes();
3350 static bool wxHasStyle(long flags
, long style
)
3352 return (flags
& style
) != 0;
3355 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3357 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3359 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3364 /// Get the combined style for a range - if any attribute is different within the range,
3365 /// that attribute is not present within the flags.
3366 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3368 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3370 style
= wxRichTextAttr();
3372 wxRichTextAttr clashingAttr
;
3373 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3375 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3378 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3379 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3381 if (para
->GetChildren().GetCount() == 0)
3383 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3385 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3389 wxRichTextRange
paraRange(para
->GetRange());
3390 paraRange
.LimitTo(range
);
3392 // First collect paragraph attributes only
3393 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3394 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3395 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3397 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3401 wxRichTextObject
* child
= childNode
->GetData();
3402 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3404 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3406 // Now collect character attributes only
3407 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3409 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3412 childNode
= childNode
->GetNext();
3416 node
= node
->GetNext();
3421 /// Set default style
3422 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3424 m_defaultAttributes
= style
;
3428 /// Test if this whole range has character attributes of the specified kind. If any
3429 /// of the attributes are different within the range, the test fails. You
3430 /// can use this to implement, for example, bold button updating. style must have
3431 /// flags indicating which attributes are of interest.
3432 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3435 int matchingCount
= 0;
3437 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3440 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3441 // wxASSERT (para != NULL);
3445 // Stop searching if we're beyond the range of interest
3446 if (para
->GetRange().GetStart() > range
.GetEnd())
3447 return foundCount
== matchingCount
&& foundCount
!= 0;
3449 if (!para
->GetRange().IsOutside(range
))
3451 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3455 wxRichTextObject
* child
= node2
->GetData();
3456 // Allow for empty string if no buffer
3457 wxRichTextRange childRange
= child
->GetRange();
3458 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3459 childRange
.SetEnd(childRange
.GetEnd()+1);
3461 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3464 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3466 if (wxTextAttrEqPartial(textAttr
, style
))
3470 node2
= node2
->GetNext();
3475 node
= node
->GetNext();
3478 return foundCount
== matchingCount
&& foundCount
!= 0;
3481 /// Test if this whole range has paragraph attributes of the specified kind. If any
3482 /// of the attributes are different within the range, the test fails. You
3483 /// can use this to implement, for example, centering button updating. style must have
3484 /// flags indicating which attributes are of interest.
3485 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3488 int matchingCount
= 0;
3490 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3493 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3494 // wxASSERT (para != NULL);
3498 // Stop searching if we're beyond the range of interest
3499 if (para
->GetRange().GetStart() > range
.GetEnd())
3500 return foundCount
== matchingCount
&& foundCount
!= 0;
3502 if (!para
->GetRange().IsOutside(range
))
3504 wxRichTextAttr textAttr
= GetAttributes();
3505 // Apply the paragraph style
3506 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3509 if (wxTextAttrEqPartial(textAttr
, style
))
3514 node
= node
->GetNext();
3516 return foundCount
== matchingCount
&& foundCount
!= 0;
3519 void wxRichTextParagraphLayoutBox::Reset()
3523 wxRichTextBuffer
* buffer
= GetBuffer();
3524 if (buffer
&& buffer
->GetRichTextCtrl())
3526 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3527 event
.SetEventObject(buffer
->GetRichTextCtrl());
3528 event
.SetContainer(this);
3530 buffer
->SendEvent(event
, true);
3533 AddParagraph(wxEmptyString
);
3535 InvalidateHierarchy(wxRICHTEXT_ALL
);
3538 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3539 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3541 wxRichTextCompositeObject::Invalidate(invalidRange
);
3543 DoInvalidate(invalidRange
);
3546 // Do the (in)validation for this object only
3547 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3549 if (invalidRange
== wxRICHTEXT_ALL
)
3551 m_invalidRange
= wxRICHTEXT_ALL
;
3553 // Already invalidating everything
3554 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3559 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3560 m_invalidRange
.SetStart(invalidRange
.GetStart());
3561 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3562 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3566 // Do the (in)validation both up and down the hierarchy
3567 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3569 Invalidate(invalidRange
);
3571 if (invalidRange
!= wxRICHTEXT_NONE
)
3573 // Now go up the hierarchy
3574 wxRichTextObject
* thisObj
= this;
3575 wxRichTextObject
* p
= GetParent();
3578 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3580 l
->DoInvalidate(thisObj
->GetRange());
3588 /// Get invalid range, rounding to entire paragraphs if argument is true.
3589 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3591 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3592 return m_invalidRange
;
3594 wxRichTextRange range
= m_invalidRange
;
3596 if (wholeParagraphs
)
3598 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3600 range
.SetStart(para1
->GetRange().GetStart());
3601 // floating layout make all child should be relayout
3602 range
.SetEnd(GetOwnRange().GetEnd());
3607 /// Apply the style sheet to the buffer, for example if the styles have changed.
3608 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3610 wxASSERT(styleSheet
!= NULL
);
3616 wxRichTextAttr
attr(GetBasicStyle());
3617 if (GetBasicStyle().HasParagraphStyleName())
3619 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3622 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3623 SetBasicStyle(attr
);
3628 if (GetBasicStyle().HasCharacterStyleName())
3630 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3633 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3634 SetBasicStyle(attr
);
3639 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3642 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3643 // wxASSERT (para != NULL);
3647 // Combine paragraph and list styles. If there is a list style in the original attributes,
3648 // the current indentation overrides anything else and is used to find the item indentation.
3649 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3650 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3651 // exception as above).
3652 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3653 // So when changing a list style interactively, could retrieve level based on current style, then
3654 // set appropriate indent and apply new style.
3658 if (para
->GetAttributes().HasOutlineLevel())
3659 outline
= para
->GetAttributes().GetOutlineLevel();
3660 if (para
->GetAttributes().HasBulletNumber())
3661 num
= para
->GetAttributes().GetBulletNumber();
3663 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3665 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3667 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3668 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3669 if (paraDef
&& !listDef
)
3671 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3674 else if (listDef
&& !paraDef
)
3676 // Set overall style defined for the list style definition
3677 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3679 // Apply the style for this level
3680 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3683 else if (listDef
&& paraDef
)
3685 // Combines overall list style, style for level, and paragraph style
3686 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3690 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3692 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3694 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3696 // Overall list definition style
3697 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3699 // Style for this level
3700 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3704 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3706 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3709 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3715 para
->GetAttributes().SetOutlineLevel(outline
);
3717 para
->GetAttributes().SetBulletNumber(num
);
3720 node
= node
->GetNext();
3722 return foundCount
!= 0;
3726 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3728 wxRichTextBuffer
* buffer
= GetBuffer();
3729 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3731 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3732 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3733 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3734 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3736 // Current number, if numbering
3739 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3741 // If we are associated with a control, make undoable; otherwise, apply immediately
3744 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3746 wxRichTextAction
* action
= NULL
;
3748 if (haveControl
&& withUndo
)
3750 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3751 action
->SetRange(range
);
3752 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3755 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3758 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3759 // wxASSERT (para != NULL);
3761 if (para
&& para
->GetChildCount() > 0)
3763 // Stop searching if we're beyond the range of interest
3764 if (para
->GetRange().GetStart() > range
.GetEnd())
3767 if (!para
->GetRange().IsOutside(range
))
3769 // We'll be using a copy of the paragraph to make style changes,
3770 // not updating the buffer directly.
3771 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3773 if (haveControl
&& withUndo
)
3775 newPara
= new wxRichTextParagraph(*para
);
3776 action
->GetNewParagraphs().AppendChild(newPara
);
3778 // Also store the old ones for Undo
3779 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3786 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3787 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3789 // How is numbering going to work?
3790 // If we are renumbering, or numbering for the first time, we need to keep
3791 // track of the number for each level. But we might be simply applying a different
3793 // In Word, applying a style to several paragraphs, even if at different levels,
3794 // reverts the level back to the same one. So we could do the same here.
3795 // Renumbering will need to be done when we promote/demote a paragraph.
3797 // Apply the overall list style, and item style for this level
3798 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3799 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3801 // Now we need to do numbering
3804 newPara
->GetAttributes().SetBulletNumber(n
);
3809 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3811 // if def is NULL, remove list style, applying any associated paragraph style
3812 // to restore the attributes
3814 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3815 newPara
->GetAttributes().SetLeftIndent(0, 0);
3816 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3818 // Eliminate the main list-related attributes
3819 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
);
3821 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3823 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3826 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3833 node
= node
->GetNext();
3836 // Do action, or delay it until end of batch.
3837 if (haveControl
&& withUndo
)
3838 buffer
->SubmitAction(action
);
3843 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3845 wxRichTextBuffer
* buffer
= GetBuffer();
3846 if (buffer
&& buffer
->GetStyleSheet())
3848 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3850 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
3855 /// Clear list for given range
3856 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3858 return SetListStyle(range
, NULL
, flags
);
3861 /// Number/renumber any list elements in the given range
3862 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3864 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
3867 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3868 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
3869 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3871 wxRichTextBuffer
* buffer
= GetBuffer();
3872 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3874 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3875 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3877 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3880 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3882 // Max number of levels
3883 const int maxLevels
= 10;
3885 // The level we're looking at now
3886 int currentLevel
= -1;
3888 // The item number for each level
3889 int levels
[maxLevels
];
3892 // Reset all numbering
3893 for (i
= 0; i
< maxLevels
; i
++)
3895 if (startFrom
!= -1)
3896 levels
[i
] = startFrom
-1;
3897 else if (renumber
) // start again
3900 levels
[i
] = -1; // start from the number we found, if any
3904 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3907 // If we are associated with a control, make undoable; otherwise, apply immediately
3910 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3912 wxRichTextAction
* action
= NULL
;
3914 if (haveControl
&& withUndo
)
3916 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3917 action
->SetRange(range
);
3918 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3921 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3924 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3925 // wxASSERT (para != NULL);
3927 if (para
&& para
->GetChildCount() > 0)
3929 // Stop searching if we're beyond the range of interest
3930 if (para
->GetRange().GetStart() > range
.GetEnd())
3933 if (!para
->GetRange().IsOutside(range
))
3935 // We'll be using a copy of the paragraph to make style changes,
3936 // not updating the buffer directly.
3937 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3939 if (haveControl
&& withUndo
)
3941 newPara
= new wxRichTextParagraph(*para
);
3942 action
->GetNewParagraphs().AppendChild(newPara
);
3944 // Also store the old ones for Undo
3945 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3950 wxRichTextListStyleDefinition
* defToUse
= def
;
3953 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
3954 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
3959 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3960 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
3962 // If we've specified a level to apply to all, change the level.
3963 if (specifiedLevel
!= -1)
3964 thisLevel
= specifiedLevel
;
3966 // Do promotion if specified
3967 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
3969 thisLevel
= thisLevel
- promoteBy
;
3976 // Apply the overall list style, and item style for this level
3977 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3978 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3980 // OK, we've (re)applied the style, now let's get the numbering right.
3982 if (currentLevel
== -1)
3983 currentLevel
= thisLevel
;
3985 // Same level as before, do nothing except increment level's number afterwards
3986 if (currentLevel
== thisLevel
)
3989 // A deeper level: start renumbering all levels after current level
3990 else if (thisLevel
> currentLevel
)
3992 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
3996 currentLevel
= thisLevel
;
3998 else if (thisLevel
< currentLevel
)
4000 currentLevel
= thisLevel
;
4003 // Use the current numbering if -1 and we have a bullet number already
4004 if (levels
[currentLevel
] == -1)
4006 if (newPara
->GetAttributes().HasBulletNumber())
4007 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4009 levels
[currentLevel
] = 1;
4013 levels
[currentLevel
] ++;
4016 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4018 // Create the bullet text if an outline list
4019 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4022 for (i
= 0; i
<= currentLevel
; i
++)
4024 if (!text
.IsEmpty())
4026 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4028 newPara
->GetAttributes().SetBulletText(text
);
4034 node
= node
->GetNext();
4037 // Do action, or delay it until end of batch.
4038 if (haveControl
&& withUndo
)
4039 buffer
->SubmitAction(action
);
4044 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4046 wxRichTextBuffer
* buffer
= GetBuffer();
4047 if (buffer
->GetStyleSheet())
4049 wxRichTextListStyleDefinition
* def
= NULL
;
4050 if (!defName
.IsEmpty())
4051 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4052 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4057 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4058 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4061 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4062 // to NumberList with a flag indicating promotion is required within one of the ranges.
4063 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4064 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4065 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4066 // list position will start from 1.
4067 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4068 // We can end the renumbering at this point.
4070 // For now, only renumber within the promotion range.
4072 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4075 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4077 wxRichTextBuffer
* buffer
= GetBuffer();
4078 if (buffer
->GetStyleSheet())
4080 wxRichTextListStyleDefinition
* def
= NULL
;
4081 if (!defName
.IsEmpty())
4082 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4083 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4088 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4089 /// position of the paragraph that it had to start looking from.
4090 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4092 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4095 wxRichTextBuffer
* buffer
= GetBuffer();
4096 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4097 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4099 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4102 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4103 // int thisLevel = def->FindLevelForIndent(thisIndent);
4105 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4107 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4108 if (previousParagraph
->GetAttributes().HasBulletName())
4109 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4110 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4111 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4113 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4114 attr
.SetBulletNumber(nextNumber
);
4118 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4119 if (!text
.IsEmpty())
4121 int pos
= text
.Find(wxT('.'), true);
4122 if (pos
!= wxNOT_FOUND
)
4124 text
= text
.Mid(0, text
.Length() - pos
- 1);
4127 text
= wxEmptyString
;
4128 if (!text
.IsEmpty())
4130 text
+= wxString::Format(wxT("%d"), nextNumber
);
4131 attr
.SetBulletText(text
);
4145 * wxRichTextParagraph
4146 * This object represents a single paragraph (or in a straight text editor, a line).
4149 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4151 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4153 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4154 wxRichTextCompositeObject(parent
)
4157 SetAttributes(*style
);
4160 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4161 wxRichTextCompositeObject(parent
)
4164 SetAttributes(*paraStyle
);
4166 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4169 wxRichTextParagraph::~wxRichTextParagraph()
4175 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4180 // Currently we don't merge these attributes with the parent, but we
4181 // should consider whether we should (e.g. if we set a border colour
4182 // for all paragraphs). But generally box attributes are likely to be
4183 // different for different objects.
4184 wxRect paraRect
= GetRect();
4185 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4187 wxRichTextAttr attr
= GetCombinedAttributes();
4189 // Draw the bullet, if any
4190 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4192 if (attr
.GetLeftSubIndent() != 0)
4194 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4195 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4197 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4199 // Combine with the font of the first piece of content, if one is specified
4200 if (GetChildren().GetCount() > 0)
4202 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4203 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4205 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4209 // Get line height from first line, if any
4210 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4213 int lineHeight
wxDUMMY_INITIALIZE(0);
4216 lineHeight
= line
->GetSize().y
;
4217 linePos
= line
->GetPosition() + GetPosition();
4222 if (bulletAttr
.HasFont() && GetBuffer())
4223 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4225 font
= (*wxNORMAL_FONT
);
4227 wxCheckSetFont(dc
, font
);
4229 lineHeight
= dc
.GetCharHeight();
4230 linePos
= GetPosition();
4231 linePos
.y
+= spaceBeforePara
;
4234 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4236 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4238 if (wxRichTextBuffer::GetRenderer())
4239 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4241 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4243 if (wxRichTextBuffer::GetRenderer())
4244 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4248 wxString bulletText
= GetBulletText();
4250 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4251 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4256 // Draw the range for each line, one object at a time.
4258 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4261 wxRichTextLine
* line
= node
->GetData();
4262 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4264 // Lines are specified relative to the paragraph
4266 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4268 // Don't draw if off the screen
4269 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4271 wxPoint objectPosition
= linePosition
;
4272 int maxDescent
= line
->GetDescent();
4274 // Loop through objects until we get to the one within range
4275 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4280 wxRichTextObject
* child
= node2
->GetData();
4282 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4284 // Draw this part of the line at the correct position
4285 wxRichTextRange
objectRange(child
->GetRange());
4286 objectRange
.LimitTo(lineRange
);
4289 if (child
->IsTopLevel())
4291 objectSize
= child
->GetCachedSize();
4292 objectRange
= child
->GetOwnRange();
4296 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4297 if (i
< (int) line
->GetObjectSizes().GetCount())
4299 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4305 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4309 // Use the child object's width, but the whole line's height
4310 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4311 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4313 objectPosition
.x
+= objectSize
.x
;
4316 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4317 // Can break out of inner loop now since we've passed this line's range
4320 node2
= node2
->GetNext();
4324 node
= node
->GetNext();
4330 // Get the range width using partial extents calculated for the whole paragraph.
4331 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4333 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4335 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4338 int leftMostPos
= 0;
4339 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4340 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4342 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4344 int w
= rightMostPos
- leftMostPos
;
4349 /// Lay the item out
4350 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4352 // Deal with floating objects firstly before the normal layout
4353 wxRichTextBuffer
* buffer
= GetBuffer();
4355 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4356 wxASSERT(collector
);
4357 LayoutFloat(dc
, rect
, style
, collector
);
4359 wxRichTextAttr attr
= GetCombinedAttributes();
4363 // Increase the size of the paragraph due to spacing
4364 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4365 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4366 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4367 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4368 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4370 int lineSpacing
= 0;
4372 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4373 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4375 wxCheckSetFont(dc
, attr
.GetFont());
4376 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4379 // Start position for each line relative to the paragraph
4380 int startPositionFirstLine
= leftIndent
;
4381 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4383 // If we have a bullet in this paragraph, the start position for the first line's text
4384 // is actually leftIndent + leftSubIndent.
4385 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4386 startPositionFirstLine
= startPositionSubsequentLines
;
4388 long lastEndPos
= GetRange().GetStart()-1;
4389 long lastCompletedEndPos
= lastEndPos
;
4391 int currentWidth
= 0;
4392 SetPosition(rect
.GetPosition());
4394 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4397 int maxHeight
= currentPosition
.y
;
4402 int lineDescent
= 0;
4404 wxRichTextObjectList::compatibility_iterator node
;
4406 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4408 wxArrayInt partialExtents
;
4411 int paraDescent
= 0;
4413 // This calculates the partial text extents
4414 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4416 node
= m_children
.GetFirst();
4419 wxRichTextObject
* child
= node
->GetData();
4421 //child->SetCachedSize(wxDefaultSize);
4422 child
->Layout(dc
, rect
, style
);
4424 node
= node
->GetNext();
4431 // We may need to go back to a previous child, in which case create the new line,
4432 // find the child corresponding to the start position of the string, and
4435 wxRect availableRect
;
4437 node
= m_children
.GetFirst();
4440 wxRichTextObject
* child
= node
->GetData();
4442 // If floating, ignore. We already laid out floats.
4443 // Also ignore if empty object, except if we haven't got any
4445 if (child
->IsFloating() || !child
->IsShown() ||
4446 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4449 node
= node
->GetNext();
4453 // If this is e.g. a composite text box, it will need to be laid out itself.
4454 // But if just a text fragment or image, for example, this will
4455 // do nothing. NB: won't we need to set the position after layout?
4456 // since for example if position is dependent on vertical line size, we
4457 // can't tell the position until the size is determined. So possibly introduce
4458 // another layout phase.
4460 // We may only be looking at part of a child, if we searched back for wrapping
4461 // and found a suitable point some way into the child. So get the size for the fragment
4464 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4465 long lastPosToUse
= child
->GetRange().GetEnd();
4466 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4468 if (lineBreakInThisObject
)
4469 lastPosToUse
= nextBreakPos
;
4472 int childDescent
= 0;
4474 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4475 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4476 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4478 if (child
->IsTopLevel())
4480 wxSize oldSize
= child
->GetCachedSize();
4482 child
->Invalidate(wxRICHTEXT_ALL
);
4483 child
->SetPosition(wxPoint(0, 0));
4485 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4486 // lays out the object again using the minimum size
4487 // The position will be determined by its location in its line,
4488 // and not by the child's actual position.
4489 child
->LayoutToBestSize(dc
, buffer
,
4490 GetAttributes(), child
->GetAttributes(), availableRect
, parentRect
, style
);
4492 if (oldSize
!= child
->GetCachedSize())
4494 partialExtents
.Clear();
4496 // Recalculate the partial text extents since the child object changed size
4497 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4501 // Problem: we need to layout composites here for which we need the available width,
4502 // but we can't get the available width without using the float collector which
4503 // needs to know the object height.
4505 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4507 childSize
= child
->GetCachedSize();
4508 childDescent
= child
->GetDescent();
4512 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4513 // Get height only, then the width using the partial extents
4514 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4515 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4517 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4522 int loopIterations
= 0;
4524 // If there are nested objects that need to lay themselves out, we have to do this in a
4525 // loop because the height of the object may well depend on the available width.
4526 // And because of floating object positioning, the available width depends on the
4527 // height of the object and whether it will clash with the floating objects.
4528 // So, we see whether the available width changes due to the presence of floating images.
4529 // If it does, then we'll use the new restricted width to find the object height again.
4530 // If this causes another restriction in the available width, we'll try again, until
4531 // either we lose patience or the available width settles down.
4536 wxRect oldAvailableRect
= availableRect
;
4538 // Available width depends on the floating objects and the line height.
4539 // Note: the floating objects may be placed vertically along the two side of
4540 // buffer, so we may have different available line widths with different
4541 // [startY, endY]. So, we can't determine how wide the available
4542 // space is until we know the exact line height.
4543 lineDescent
= wxMax(childDescent
, maxDescent
);
4544 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4545 lineHeight
= lineDescent
+ lineAscent
;
4546 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4548 // Adjust availableRect to the space that is available when taking floating objects into account.
4550 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4552 int newX
= floatAvailableRect
.x
+ startOffset
;
4553 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4554 availableRect
.x
= newX
;
4555 availableRect
.width
= newW
;
4558 if (floatAvailableRect
.width
< availableRect
.width
)
4559 availableRect
.width
= floatAvailableRect
.width
;
4561 currentPosition
.x
= availableRect
.x
- rect
.x
;
4563 if (child
->IsTopLevel() && loopIterations
<= 20)
4565 if (availableRect
!= oldAvailableRect
)
4567 wxSize oldSize
= child
->GetCachedSize();
4569 //child->SetCachedSize(wxDefaultSize);
4570 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4571 // lays out the object again using the minimum size
4572 child
->Invalidate(wxRICHTEXT_ALL
);
4573 child
->LayoutToBestSize(dc
, buffer
,
4574 GetAttributes(), child
->GetAttributes(), availableRect
, parentRect
, style
);
4575 childSize
= child
->GetCachedSize();
4576 childDescent
= child
->GetDescent();
4577 //child->SetPosition(availableRect.GetPosition());
4579 if (oldSize
!= child
->GetCachedSize())
4581 partialExtents
.Clear();
4583 // Recalculate the partial text extents since the child object changed size
4584 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4587 // Go around the loop finding the available rect for the given floating objects
4598 // 1) There was a line break BEFORE the natural break
4599 // 2) There was a line break AFTER the natural break
4600 // 3) It's the last line
4601 // 4) The child still fits (carry on) - 'else' clause
4603 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4605 (childSize
.x
+ currentWidth
> availableRect
.width
)
4607 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4611 if (child
->IsTopLevel())
4613 // We can move it to the correct position at this point
4614 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4617 long wrapPosition
= 0;
4618 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4619 wrapPosition
= child
->GetRange().GetEnd();
4622 // Find a place to wrap. This may walk back to previous children,
4623 // for example if a word spans several objects.
4624 // Note: one object must contains only one wxTextAtrr, so the line height will not
4625 // change inside one object. Thus, we can pass the remain line width to the
4626 // FindWrapPosition function.
4627 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4629 // If the function failed, just cut it off at the end of this child.
4630 wrapPosition
= child
->GetRange().GetEnd();
4633 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4634 if (wrapPosition
<= lastCompletedEndPos
)
4635 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4637 // Line end position shouldn't be the same as the end, or greater.
4638 if (wrapPosition
>= GetRange().GetEnd())
4639 wrapPosition
= GetRange().GetEnd()-1;
4641 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4643 // Let's find the actual size of the current line now
4645 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4647 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4648 /// for the fragment we're about to add.
4649 childDescent
= maxDescent
;
4651 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4652 if (!child
->IsEmpty())
4654 // Get height only, then the width using the partial extents
4655 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4656 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4660 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4662 currentWidth
= actualSize
.x
;
4663 maxDescent
= wxMax(childDescent
, maxDescent
);
4664 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4665 lineHeight
= maxDescent
+ maxAscent
;
4667 if (lineHeight
== 0 && buffer
)
4669 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4670 wxCheckSetFont(dc
, font
);
4671 lineHeight
= dc
.GetCharHeight();
4674 if (maxDescent
== 0)
4677 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4681 wxRichTextLine
* line
= AllocateLine(lineCount
);
4683 // Set relative range so we won't have to change line ranges when paragraphs are moved
4684 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4685 line
->SetPosition(currentPosition
);
4686 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4687 line
->SetDescent(maxDescent
);
4689 maxHeight
= currentPosition
.y
+ lineHeight
;
4691 // Now move down a line. TODO: add margins, spacing
4692 currentPosition
.y
+= lineHeight
;
4693 currentPosition
.y
+= lineSpacing
;
4696 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4701 // TODO: account for zero-length objects, such as fields
4702 // wxASSERT(wrapPosition > lastCompletedEndPos);
4704 lastEndPos
= wrapPosition
;
4705 lastCompletedEndPos
= lastEndPos
;
4709 if (wrapPosition
< GetRange().GetEnd()-1)
4711 // May need to set the node back to a previous one, due to searching back in wrapping
4712 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4713 if (childAfterWrapPosition
)
4714 node
= m_children
.Find(childAfterWrapPosition
);
4716 node
= node
->GetNext();
4719 node
= node
->GetNext();
4721 // Apply paragraph styles such as alignment to the wrapped line
4722 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4726 // We still fit, so don't add a line, and keep going
4727 currentWidth
+= childSize
.x
;
4728 maxDescent
= wxMax(childDescent
, maxDescent
);
4729 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4730 lineHeight
= maxDescent
+ maxAscent
;
4732 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4733 lastEndPos
= child
->GetRange().GetEnd();
4735 node
= node
->GetNext();
4739 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4741 // Remove remaining unused line objects, if any
4742 ClearUnusedLines(lineCount
);
4744 // We need to add back the margins etc.
4746 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4747 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4748 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4749 SetCachedSize(marginRect
.GetSize());
4752 // The maximum size is the length of the paragraph stretched out into a line.
4753 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4754 // this size. TODO: take into account line breaks.
4756 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4757 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4758 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4759 SetMaxSize(marginRect
.GetSize());
4762 // Find the greatest minimum size. Currently we only look at non-text objects,
4763 // which isn't ideal but it would be slow to find the maximum word width to
4764 // use as the minimum.
4767 node
= m_children
.GetFirst();
4770 wxRichTextObject
* child
= node
->GetData();
4772 // If floating, ignore. We already laid out floats.
4773 // Also ignore if empty object, except if we haven't got any
4775 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4777 if (child
->GetCachedSize().x
> minWidth
)
4778 minWidth
= child
->GetMinSize().x
;
4780 node
= node
->GetNext();
4783 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4784 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4785 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4786 SetMinSize(marginRect
.GetSize());
4790 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4791 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4792 // Use the text extents to calculate the size of each fragment in each line
4793 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4796 wxRichTextLine
* line
= lineNode
->GetData();
4797 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4799 // Loop through objects until we get to the one within range
4800 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4804 wxRichTextObject
* child
= node2
->GetData();
4806 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4808 wxRichTextRange rangeToUse
= lineRange
;
4809 rangeToUse
.LimitTo(child
->GetRange());
4811 // Find the size of the child from the text extents, and store in an array
4812 // for drawing later
4814 if (rangeToUse
.GetStart() > GetRange().GetStart())
4815 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4816 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4817 int sz
= right
- left
;
4818 line
->GetObjectSizes().Add(sz
);
4820 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4821 // Can break out of inner loop now since we've passed this line's range
4824 node2
= node2
->GetNext();
4827 lineNode
= lineNode
->GetNext();
4835 /// Apply paragraph styles, such as centering, to wrapped lines
4836 /// TODO: take into account box attributes, possibly
4837 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4839 if (!attr
.HasAlignment())
4842 wxPoint pos
= line
->GetPosition();
4843 wxSize size
= line
->GetSize();
4845 // centering, right-justification
4846 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4848 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4849 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
4850 line
->SetPosition(pos
);
4852 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4854 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4855 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
4856 line
->SetPosition(pos
);
4860 /// Insert text at the given position
4861 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
4863 wxRichTextObject
* childToUse
= NULL
;
4864 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
4866 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4869 wxRichTextObject
* child
= node
->GetData();
4870 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
4877 node
= node
->GetNext();
4882 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
4885 int posInString
= pos
- textObject
->GetRange().GetStart();
4887 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
4888 text
+ textObject
->GetText().Mid(posInString
);
4889 textObject
->SetText(newText
);
4891 int textLength
= text
.length();
4893 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
4894 textObject
->GetRange().GetEnd() + textLength
));
4896 // Increment the end range of subsequent fragments in this paragraph.
4897 // We'll set the paragraph range itself at a higher level.
4899 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
4902 wxRichTextObject
* child
= node
->GetData();
4903 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
4904 textObject
->GetRange().GetEnd() + textLength
));
4906 node
= node
->GetNext();
4913 // TODO: if not a text object, insert at closest position, e.g. in front of it
4919 // Don't pass parent initially to suppress auto-setting of parent range.
4920 // We'll do that at a higher level.
4921 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
4923 AppendChild(textObject
);
4930 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
4932 wxRichTextCompositeObject::Copy(obj
);
4935 /// Clear the cached lines
4936 void wxRichTextParagraph::ClearLines()
4938 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
4941 /// Get/set the object size for the given range. Returns false if the range
4942 /// is invalid for this object.
4943 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
4945 if (!range
.IsWithin(GetRange()))
4948 if (flags
& wxRICHTEXT_UNFORMATTED
)
4950 // Just use unformatted data, assume no line breaks
4951 // TODO: take into account line breaks
4955 wxArrayInt childExtents
;
4962 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4966 wxRichTextObject
* child
= node
->GetData();
4967 if (!child
->GetRange().IsOutside(range
))
4969 // Floating objects have a zero size within the paragraph.
4970 if (child
->IsFloating())
4975 if (partialExtents
->GetCount() > 0)
4976 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4980 partialExtents
->Add(0 /* zero size */ + lastSize
);
4987 wxRichTextRange rangeToUse
= range
;
4988 rangeToUse
.LimitTo(child
->GetRange());
4990 if (child
->IsTopLevel())
4991 rangeToUse
= child
->GetOwnRange();
4993 int childDescent
= 0;
4995 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4996 // but it's only going to be used after caching has taken place.
4997 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
4999 childDescent
= child
->GetDescent();
5000 childSize
= child
->GetCachedSize();
5002 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5003 sz
.x
+= childSize
.x
;
5004 descent
= wxMax(descent
, childDescent
);
5006 else if (child
->IsTopLevel())
5008 childDescent
= child
->GetDescent();
5009 childSize
= child
->GetCachedSize();
5011 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5012 sz
.x
+= childSize
.x
;
5013 descent
= wxMax(descent
, childDescent
);
5014 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5016 child
->SetCachedSize(childSize
);
5017 child
->SetDescent(childDescent
);
5023 if (partialExtents
->GetCount() > 0)
5024 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5028 partialExtents
->Add(childSize
.x
+ lastSize
);
5031 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5033 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5034 sz
.x
+= childSize
.x
;
5035 descent
= wxMax(descent
, childDescent
);
5037 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5039 child
->SetCachedSize(childSize
);
5040 child
->SetDescent(childDescent
);
5046 if (partialExtents
->GetCount() > 0)
5047 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5052 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5054 partialExtents
->Add(childExtents
[i
] + lastSize
);
5064 node
= node
->GetNext();
5070 // Use formatted data, with line breaks
5073 // We're going to loop through each line, and then for each line,
5074 // call GetRangeSize for the fragment that comprises that line.
5075 // Only we have to do that multiple times within the line, because
5076 // the line may be broken into pieces. For now ignore line break commands
5077 // (so we can assume that getting the unformatted size for a fragment
5078 // within a line is the actual size)
5080 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5083 wxRichTextLine
* line
= node
->GetData();
5084 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5085 if (!lineRange
.IsOutside(range
))
5089 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5092 wxRichTextObject
* child
= node2
->GetData();
5094 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5096 wxRichTextRange rangeToUse
= lineRange
;
5097 rangeToUse
.LimitTo(child
->GetRange());
5098 if (child
->IsTopLevel())
5099 rangeToUse
= child
->GetOwnRange();
5102 int childDescent
= 0;
5103 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5105 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5106 lineSize
.x
+= childSize
.x
;
5108 descent
= wxMax(descent
, childDescent
);
5111 node2
= node2
->GetNext();
5114 // Increase size by a line (TODO: paragraph spacing)
5116 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5118 node
= node
->GetNext();
5125 /// Finds the absolute position and row height for the given character position
5126 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5130 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5132 *height
= line
->GetSize().y
;
5134 *height
= dc
.GetCharHeight();
5136 // -1 means 'the start of the buffer'.
5139 pt
= pt
+ line
->GetPosition();
5144 // The final position in a paragraph is taken to mean the position
5145 // at the start of the next paragraph.
5146 if (index
== GetRange().GetEnd())
5148 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5149 wxASSERT( parent
!= NULL
);
5151 // Find the height at the next paragraph, if any
5152 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5155 *height
= line
->GetSize().y
;
5156 pt
= line
->GetAbsolutePosition();
5160 *height
= dc
.GetCharHeight();
5161 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5162 pt
= wxPoint(indent
, GetCachedSize().y
);
5168 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5171 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5174 wxRichTextLine
* line
= node
->GetData();
5175 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5176 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5178 // If this is the last point in the line, and we're forcing the
5179 // returned value to be the start of the next line, do the required
5181 if (index
== lineRange
.GetEnd() && forceLineStart
)
5183 if (node
->GetNext())
5185 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5186 *height
= nextLine
->GetSize().y
;
5187 pt
= nextLine
->GetAbsolutePosition();
5192 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5194 wxRichTextRange
r(lineRange
.GetStart(), index
);
5198 // We find the size of the line up to this point,
5199 // then we can add this size to the line start position and
5200 // paragraph start position to find the actual position.
5202 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5204 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5205 *height
= line
->GetSize().y
;
5212 node
= node
->GetNext();
5218 /// Hit-testing: returns a flag indicating hit test details, plus
5219 /// information about position
5220 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5223 return wxRICHTEXT_HITTEST_NONE
;
5225 // If we're in the top-level container, then we can return
5226 // a suitable hit test code even if the point is outside the container area,
5227 // so that we can position the caret sensibly even if we don't
5228 // click on valid content. If we're not at the top-level, and the point
5229 // is not within this paragraph object, then we don't want to stop more
5230 // precise hit-testing from working prematurely, so return immediately.
5231 // NEW STRATEGY: use the parent boundary to test whether we're in the
5232 // right region, not the paragraph, since the paragraph may be positioned
5233 // some way in from where the user clicks.
5236 wxRichTextObject
* tempObj
, *tempContextObj
;
5237 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5238 return wxRICHTEXT_HITTEST_NONE
;
5241 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5244 wxRichTextObject
* child
= objNode
->GetData();
5245 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5248 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5249 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5254 objNode
= objNode
->GetNext();
5257 wxPoint paraPos
= GetPosition();
5259 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5262 wxRichTextLine
* line
= node
->GetData();
5263 wxPoint linePos
= paraPos
+ line
->GetPosition();
5264 wxSize lineSize
= line
->GetSize();
5265 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5267 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5269 if (pt
.x
< linePos
.x
)
5271 textPosition
= lineRange
.GetStart();
5272 *obj
= FindObjectAtPosition(textPosition
);
5273 *contextObj
= GetContainer();
5274 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5276 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5278 textPosition
= lineRange
.GetEnd();
5279 *obj
= FindObjectAtPosition(textPosition
);
5280 *contextObj
= GetContainer();
5281 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5285 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5286 wxArrayInt partialExtents
;
5291 // This calculates the partial text extents
5292 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5294 int lastX
= linePos
.x
;
5296 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5298 int nextX
= partialExtents
[i
] + linePos
.x
;
5300 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5302 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5304 *obj
= FindObjectAtPosition(textPosition
);
5305 *contextObj
= GetContainer();
5307 // So now we know it's between i-1 and i.
5308 // Let's see if we can be more precise about
5309 // which side of the position it's on.
5311 int midPoint
= (nextX
+ lastX
)/2;
5312 if (pt
.x
>= midPoint
)
5313 return wxRICHTEXT_HITTEST_AFTER
;
5315 return wxRICHTEXT_HITTEST_BEFORE
;
5322 int lastX
= linePos
.x
;
5323 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5328 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5330 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5332 int nextX
= childSize
.x
+ linePos
.x
;
5334 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5338 *obj
= FindObjectAtPosition(textPosition
);
5339 *contextObj
= GetContainer();
5341 // So now we know it's between i-1 and i.
5342 // Let's see if we can be more precise about
5343 // which side of the position it's on.
5345 int midPoint
= (nextX
+ lastX
)/2;
5346 if (pt
.x
>= midPoint
)
5347 return wxRICHTEXT_HITTEST_AFTER
;
5349 return wxRICHTEXT_HITTEST_BEFORE
;
5360 node
= node
->GetNext();
5363 return wxRICHTEXT_HITTEST_NONE
;
5366 /// Split an object at this position if necessary, and return
5367 /// the previous object, or NULL if inserting at beginning.
5368 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5370 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5373 wxRichTextObject
* child
= node
->GetData();
5375 if (pos
== child
->GetRange().GetStart())
5379 if (node
->GetPrevious())
5380 *previousObject
= node
->GetPrevious()->GetData();
5382 *previousObject
= NULL
;
5388 if (child
->GetRange().Contains(pos
))
5390 // This should create a new object, transferring part of
5391 // the content to the old object and the rest to the new object.
5392 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5394 // If we couldn't split this object, just insert in front of it.
5397 // Maybe this is an empty string, try the next one
5402 // Insert the new object after 'child'
5403 if (node
->GetNext())
5404 m_children
.Insert(node
->GetNext(), newObject
);
5406 m_children
.Append(newObject
);
5407 newObject
->SetParent(this);
5410 *previousObject
= child
;
5416 node
= node
->GetNext();
5419 *previousObject
= NULL
;
5423 /// Move content to a list from obj on
5424 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5426 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5429 wxRichTextObject
* child
= node
->GetData();
5432 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5434 node
= node
->GetNext();
5436 m_children
.DeleteNode(oldNode
);
5440 /// Add content back from list
5441 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5443 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5445 AppendChild((wxRichTextObject
*) node
->GetData());
5450 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5452 wxRichTextCompositeObject::CalculateRange(start
, end
);
5454 // Add one for end of paragraph
5457 m_range
.SetRange(start
, end
);
5460 /// Find the object at the given position
5461 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5463 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5466 wxRichTextObject
* obj
= node
->GetData();
5467 if (obj
->GetRange().Contains(position
) ||
5468 obj
->GetRange().GetStart() == position
||
5469 obj
->GetRange().GetEnd() == position
)
5472 node
= node
->GetNext();
5477 /// Get the plain text searching from the start or end of the range.
5478 /// The resulting string may be shorter than the range given.
5479 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5481 text
= wxEmptyString
;
5485 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5488 wxRichTextObject
* obj
= node
->GetData();
5489 if (!obj
->GetRange().IsOutside(range
))
5491 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5494 text
+= textObj
->GetTextForRange(range
);
5502 node
= node
->GetNext();
5507 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5510 wxRichTextObject
* obj
= node
->GetData();
5511 if (!obj
->GetRange().IsOutside(range
))
5513 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5516 text
= textObj
->GetTextForRange(range
) + text
;
5520 text
= wxT(" ") + text
;
5524 node
= node
->GetPrevious();
5531 /// Find a suitable wrap position.
5532 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5534 if (range
.GetLength() <= 0)
5537 // Find the first position where the line exceeds the available space.
5539 long breakPosition
= range
.GetEnd();
5541 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5542 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5546 if (range
.GetStart() > GetRange().GetStart())
5547 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5552 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5554 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5556 if (widthFromStartOfThisRange
> availableSpace
)
5558 breakPosition
= i
-1;
5566 // Binary chop for speed
5567 long minPos
= range
.GetStart();
5568 long maxPos
= range
.GetEnd();
5571 if (minPos
== maxPos
)
5574 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5576 if (sz
.x
> availableSpace
)
5577 breakPosition
= minPos
- 1;
5580 else if ((maxPos
- minPos
) == 1)
5583 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5585 if (sz
.x
> availableSpace
)
5586 breakPosition
= minPos
- 1;
5589 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5590 if (sz
.x
> availableSpace
)
5591 breakPosition
= maxPos
-1;
5597 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5600 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5602 if (sz
.x
> availableSpace
)
5614 // Now we know the last position on the line.
5615 // Let's try to find a word break.
5618 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5620 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5621 if (newLinePos
!= wxNOT_FOUND
)
5623 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5627 int spacePos
= plainText
.Find(wxT(' '), true);
5628 int tabPos
= plainText
.Find(wxT('\t'), true);
5629 int pos
= wxMax(spacePos
, tabPos
);
5630 if (pos
!= wxNOT_FOUND
)
5632 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5633 breakPosition
= breakPosition
- positionsFromEndOfString
;
5638 wrapPosition
= breakPosition
;
5643 /// Get the bullet text for this paragraph.
5644 wxString
wxRichTextParagraph::GetBulletText()
5646 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5647 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5648 return wxEmptyString
;
5650 int number
= GetAttributes().GetBulletNumber();
5653 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5655 text
.Printf(wxT("%d"), number
);
5657 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5659 // TODO: Unicode, and also check if number > 26
5660 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5662 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5664 // TODO: Unicode, and also check if number > 26
5665 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5667 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5669 text
= wxRichTextDecimalToRoman(number
);
5671 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5673 text
= wxRichTextDecimalToRoman(number
);
5676 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5678 text
= GetAttributes().GetBulletText();
5681 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5683 // The outline style relies on the text being computed statically,
5684 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5685 // should be stored in the attributes; if not, just use the number for this
5686 // level, as previously computed.
5687 if (!GetAttributes().GetBulletText().IsEmpty())
5688 text
= GetAttributes().GetBulletText();
5691 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5693 text
= wxT("(") + text
+ wxT(")");
5695 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5697 text
= text
+ wxT(")");
5700 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5708 /// Allocate or reuse a line object
5709 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5711 if (pos
< (int) m_cachedLines
.GetCount())
5713 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5719 wxRichTextLine
* line
= new wxRichTextLine(this);
5720 m_cachedLines
.Append(line
);
5725 /// Clear remaining unused line objects, if any
5726 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5728 int cachedLineCount
= m_cachedLines
.GetCount();
5729 if ((int) cachedLineCount
> lineCount
)
5731 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5733 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5734 wxRichTextLine
* line
= node
->GetData();
5735 m_cachedLines
.Erase(node
);
5742 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5743 /// retrieve the actual style.
5744 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5746 wxRichTextAttr attr
;
5747 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5750 attr
= buf
->GetBasicStyle();
5751 if (!includingBoxAttr
)
5753 attr
.GetTextBoxAttr().Reset();
5754 // The background colour will be painted by the container, and we don't
5755 // want to unnecessarily overwrite the background when we're drawing text
5756 // because this may erase the guideline (which appears just under the text
5757 // if there's no padding).
5758 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5760 wxRichTextApplyStyle(attr
, GetAttributes());
5763 attr
= GetAttributes();
5765 wxRichTextApplyStyle(attr
, contentStyle
);
5769 /// Get combined attributes of the base style and paragraph style.
5770 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5772 wxRichTextAttr attr
;
5773 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5776 attr
= buf
->GetBasicStyle();
5777 if (!includingBoxAttr
)
5778 attr
.GetTextBoxAttr().Reset();
5779 wxRichTextApplyStyle(attr
, GetAttributes());
5782 attr
= GetAttributes();
5787 // Create default tabstop array
5788 void wxRichTextParagraph::InitDefaultTabs()
5790 // create a default tab list at 10 mm each.
5791 for (int i
= 0; i
< 20; ++i
)
5793 sm_defaultTabs
.Add(i
*100);
5797 // Clear default tabstop array
5798 void wxRichTextParagraph::ClearDefaultTabs()
5800 sm_defaultTabs
.Clear();
5803 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5805 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5808 wxRichTextObject
* anchored
= node
->GetData();
5809 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5813 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5816 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5818 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5819 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5821 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5825 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5827 /* Update the offset */
5828 int newOffsetY
= pos
- rect
.y
;
5829 if (newOffsetY
!= offsetY
)
5831 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5832 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5833 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5836 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5838 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5839 x
= rect
.x
+ rect
.width
- size
.x
;
5841 anchored
->SetPosition(wxPoint(x
, pos
));
5842 anchored
->SetCachedSize(size
);
5843 floatCollector
->CollectFloat(this, anchored
);
5846 node
= node
->GetNext();
5850 // Get the first position from pos that has a line break character.
5851 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
5853 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5856 wxRichTextObject
* obj
= node
->GetData();
5857 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
5859 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5862 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
5867 node
= node
->GetNext();
5874 * This object represents a line in a paragraph, and stores
5875 * offsets from the start of the paragraph representing the
5876 * start and end positions of the line.
5879 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
5885 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
5888 m_range
.SetRange(-1, -1);
5889 m_pos
= wxPoint(0, 0);
5890 m_size
= wxSize(0, 0);
5892 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5893 m_objectSizes
.Clear();
5898 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
5900 m_range
= obj
.m_range
;
5901 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5902 m_objectSizes
= obj
.m_objectSizes
;
5906 /// Get the absolute object position
5907 wxPoint
wxRichTextLine::GetAbsolutePosition() const
5909 return m_parent
->GetPosition() + m_pos
;
5912 /// Get the absolute range
5913 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
5915 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
5916 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
5921 * wxRichTextPlainText
5922 * This object represents a single piece of text.
5925 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
5927 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
5928 wxRichTextObject(parent
)
5931 SetAttributes(*style
);
5936 #define USE_KERNING_FIX 1
5938 // If insufficient tabs are defined, this is the tab width used
5939 #define WIDTH_FOR_DEFAULT_TABS 50
5942 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
5944 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
5945 wxASSERT (para
!= NULL
);
5947 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5949 // Let's make the assumption for now that for content in a paragraph, including
5950 // text, we never have a discontinuous selection. So we only deal with a
5952 wxRichTextRange selectionRange
;
5953 if (selection
.IsValid())
5955 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
5956 if (selectionRanges
.GetCount() > 0)
5957 selectionRange
= selectionRanges
[0];
5959 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5962 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5964 int offset
= GetRange().GetStart();
5966 // Replace line break characters with spaces
5967 wxString str
= m_text
;
5968 wxString toRemove
= wxRichTextLineBreakChar
;
5969 str
.Replace(toRemove
, wxT(" "));
5970 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
5973 long len
= range
.GetLength();
5974 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
5976 // Test for the optimized situations where all is selected, or none
5979 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
5980 wxCheckSetFont(dc
, textFont
);
5981 int charHeight
= dc
.GetCharHeight();
5984 if ( textFont
.IsOk() )
5986 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
5988 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5989 textFont
.SetPointSize( static_cast<int>(size
) );
5992 wxCheckSetFont(dc
, textFont
);
5994 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
5996 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5997 textFont
.SetPointSize( static_cast<int>(size
) );
5999 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6000 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6001 wxCheckSetFont(dc
, textFont
);
6006 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6012 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6015 // TODO: new selection code
6017 // (a) All selected.
6018 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6020 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6022 // (b) None selected.
6023 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6025 // Draw all unselected
6026 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6030 // (c) Part selected, part not
6031 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6033 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6035 // 1. Initial unselected chunk, if any, up until start of selection.
6036 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6038 int r1
= range
.GetStart();
6039 int s1
= selectionRange
.GetStart()-1;
6040 int fragmentLen
= s1
- r1
+ 1;
6041 if (fragmentLen
< 0)
6043 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6045 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6047 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6050 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6052 // Compensate for kerning difference
6053 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6054 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6056 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6057 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6058 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6059 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6061 int kerningDiff
= (w1
+ w3
) - w2
;
6062 x
= x
- kerningDiff
;
6067 // 2. Selected chunk, if any.
6068 if (selectionRange
.GetEnd() >= range
.GetStart())
6070 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6071 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6073 int fragmentLen
= s2
- s1
+ 1;
6074 if (fragmentLen
< 0)
6076 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6078 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6080 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6083 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6085 // Compensate for kerning difference
6086 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6087 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6089 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6090 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6091 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6092 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6094 int kerningDiff
= (w1
+ w3
) - w2
;
6095 x
= x
- kerningDiff
;
6100 // 3. Remaining unselected chunk, if any
6101 if (selectionRange
.GetEnd() < range
.GetEnd())
6103 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6104 int r2
= range
.GetEnd();
6106 int fragmentLen
= r2
- s2
+ 1;
6107 if (fragmentLen
< 0)
6109 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6111 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6113 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6120 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6122 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6124 wxArrayInt tabArray
;
6128 if (attr
.GetTabs().IsEmpty())
6129 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6131 tabArray
= attr
.GetTabs();
6132 tabCount
= tabArray
.GetCount();
6134 for (int i
= 0; i
< tabCount
; ++i
)
6136 int pos
= tabArray
[i
];
6137 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6144 int nextTabPos
= -1;
6150 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6151 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6153 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6154 wxCheckSetPen(dc
, wxPen(highlightColour
));
6155 dc
.SetTextForeground(highlightTextColour
);
6156 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6160 dc
.SetTextForeground(attr
.GetTextColour());
6162 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6164 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6165 dc
.SetTextBackground(attr
.GetBackgroundColour());
6168 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6171 wxCoord x_orig
= GetParent()->GetPosition().x
;
6174 // the string has a tab
6175 // break up the string at the Tab
6176 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6177 str
= str
.AfterFirst(wxT('\t'));
6178 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6180 bool not_found
= true;
6181 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6183 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6185 // Find the next tab position.
6186 // Even if we're at the end of the tab array, we must still draw the chunk.
6188 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6190 if (nextTabPos
<= tabPos
)
6192 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6193 nextTabPos
= tabPos
+ defaultTabWidth
;
6200 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6201 dc
.DrawRectangle(selRect
);
6203 dc
.DrawText(stringChunk
, x
, y
);
6205 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6207 wxPen oldPen
= dc
.GetPen();
6208 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6209 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6210 wxCheckSetPen(dc
, oldPen
);
6216 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6221 dc
.GetTextExtent(str
, & w
, & h
);
6224 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6225 dc
.DrawRectangle(selRect
);
6227 dc
.DrawText(str
, x
, y
);
6229 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6231 wxPen oldPen
= dc
.GetPen();
6232 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6233 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6234 wxCheckSetPen(dc
, oldPen
);
6243 /// Lay the item out
6244 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6246 // Only lay out if we haven't already cached the size
6248 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6250 // Eventually we want to have a reasonable estimate of minimum size.
6251 m_minSize
= wxSize(0, 0);
6256 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6258 wxRichTextObject::Copy(obj
);
6260 m_text
= obj
.m_text
;
6263 /// Get/set the object size for the given range. Returns false if the range
6264 /// is invalid for this object.
6265 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6267 if (!range
.IsWithin(GetRange()))
6270 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6271 wxASSERT (para
!= NULL
);
6273 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6275 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6277 // Always assume unformatted text, since at this level we have no knowledge
6278 // of line breaks - and we don't need it, since we'll calculate size within
6279 // formatted text by doing it in chunks according to the line ranges
6281 bool bScript(false);
6282 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6285 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6286 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6288 wxFont textFont
= font
;
6289 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6290 textFont
.SetPointSize( static_cast<int>(size
) );
6291 wxCheckSetFont(dc
, textFont
);
6296 wxCheckSetFont(dc
, font
);
6300 bool haveDescent
= false;
6301 int startPos
= range
.GetStart() - GetRange().GetStart();
6302 long len
= range
.GetLength();
6304 wxString
str(m_text
);
6305 wxString toReplace
= wxRichTextLineBreakChar
;
6306 str
.Replace(toReplace
, wxT(" "));
6308 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6310 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6311 stringChunk
.MakeUpper();
6315 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6317 // the string has a tab
6318 wxArrayInt tabArray
;
6319 if (textAttr
.GetTabs().IsEmpty())
6320 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6322 tabArray
= textAttr
.GetTabs();
6324 int tabCount
= tabArray
.GetCount();
6326 for (int i
= 0; i
< tabCount
; ++i
)
6328 int pos
= tabArray
[i
];
6329 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6333 int nextTabPos
= -1;
6335 while (stringChunk
.Find(wxT('\t')) >= 0)
6337 int absoluteWidth
= 0;
6339 // the string has a tab
6340 // break up the string at the Tab
6341 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6342 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6347 if (partialExtents
->GetCount() > 0)
6348 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6352 // Add these partial extents
6354 dc
.GetPartialTextExtents(stringFragment
, p
);
6356 for (j
= 0; j
< p
.GetCount(); j
++)
6357 partialExtents
->Add(oldWidth
+ p
[j
]);
6359 if (partialExtents
->GetCount() > 0)
6360 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6362 absoluteWidth
= relativeX
;
6366 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6368 absoluteWidth
= width
+ relativeX
;
6372 bool notFound
= true;
6373 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6375 nextTabPos
= tabArray
.Item(i
);
6377 // Find the next tab position.
6378 // Even if we're at the end of the tab array, we must still process the chunk.
6380 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6382 if (nextTabPos
<= absoluteWidth
)
6384 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6385 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6389 width
= nextTabPos
- relativeX
;
6392 partialExtents
->Add(width
);
6398 if (!stringChunk
.IsEmpty())
6403 if (partialExtents
->GetCount() > 0)
6404 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6408 // Add these partial extents
6410 dc
.GetPartialTextExtents(stringChunk
, p
);
6412 for (j
= 0; j
< p
.GetCount(); j
++)
6413 partialExtents
->Add(oldWidth
+ p
[j
]);
6417 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6425 int charHeight
= dc
.GetCharHeight();
6426 if ((*partialExtents
).GetCount() > 0)
6427 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6430 size
= wxSize(w
, charHeight
);
6434 size
= wxSize(width
, dc
.GetCharHeight());
6438 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6446 /// Do a split, returning an object containing the second part, and setting
6447 /// the first part in 'this'.
6448 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6450 long index
= pos
- GetRange().GetStart();
6452 if (index
< 0 || index
>= (int) m_text
.length())
6455 wxString firstPart
= m_text
.Mid(0, index
);
6456 wxString secondPart
= m_text
.Mid(index
);
6460 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6461 newObject
->SetAttributes(GetAttributes());
6463 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6464 GetRange().SetEnd(pos
-1);
6470 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6472 end
= start
+ m_text
.length() - 1;
6473 m_range
.SetRange(start
, end
);
6477 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6479 wxRichTextRange r
= range
;
6481 r
.LimitTo(GetRange());
6483 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6489 long startIndex
= r
.GetStart() - GetRange().GetStart();
6490 long len
= r
.GetLength();
6492 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6496 /// Get text for the given range.
6497 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6499 wxRichTextRange r
= range
;
6501 r
.LimitTo(GetRange());
6503 long startIndex
= r
.GetStart() - GetRange().GetStart();
6504 long len
= r
.GetLength();
6506 return m_text
.Mid(startIndex
, len
);
6509 /// Returns true if this object can merge itself with the given one.
6510 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6512 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6513 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6516 /// Returns true if this object merged itself with the given one.
6517 /// The calling code will then delete the given object.
6518 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6520 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6521 wxASSERT( textObject
!= NULL
);
6525 m_text
+= textObject
->GetText();
6526 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6533 /// Dump to output stream for debugging
6534 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6536 wxRichTextObject::Dump(stream
);
6537 stream
<< m_text
<< wxT("\n");
6540 /// Get the first position from pos that has a line break character.
6541 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6544 int len
= m_text
.length();
6545 int startPos
= pos
- m_range
.GetStart();
6546 for (i
= startPos
; i
< len
; i
++)
6548 wxChar ch
= m_text
[i
];
6549 if (ch
== wxRichTextLineBreakChar
)
6551 return i
+ m_range
.GetStart();
6559 * This is a kind of box, used to represent the whole buffer
6562 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6564 wxList
wxRichTextBuffer::sm_handlers
;
6565 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6566 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6567 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6570 void wxRichTextBuffer::Init()
6572 m_commandProcessor
= new wxCommandProcessor
;
6573 m_styleSheet
= NULL
;
6575 m_batchedCommandDepth
= 0;
6576 m_batchedCommand
= NULL
;
6583 wxRichTextBuffer::~wxRichTextBuffer()
6585 delete m_commandProcessor
;
6586 delete m_batchedCommand
;
6589 ClearEventHandlers();
6592 void wxRichTextBuffer::ResetAndClearCommands()
6596 GetCommandProcessor()->ClearCommands();
6599 Invalidate(wxRICHTEXT_ALL
);
6602 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6604 wxRichTextParagraphLayoutBox::Copy(obj
);
6606 m_styleSheet
= obj
.m_styleSheet
;
6607 m_modified
= obj
.m_modified
;
6608 m_batchedCommandDepth
= 0;
6609 if (m_batchedCommand
)
6610 delete m_batchedCommand
;
6611 m_batchedCommand
= NULL
;
6612 m_suppressUndo
= obj
.m_suppressUndo
;
6613 m_invalidRange
= obj
.m_invalidRange
;
6616 /// Push style sheet to top of stack
6617 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6620 styleSheet
->InsertSheet(m_styleSheet
);
6622 SetStyleSheet(styleSheet
);
6627 /// Pop style sheet from top of stack
6628 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6632 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6633 m_styleSheet
= oldSheet
->GetNextSheet();
6642 /// Submit command to insert paragraphs
6643 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6645 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6648 /// Submit command to insert paragraphs
6649 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6651 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6653 action
->GetNewParagraphs() = paragraphs
;
6655 action
->SetPosition(pos
);
6657 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6658 if (!paragraphs
.GetPartialParagraph())
6659 range
.SetEnd(range
.GetEnd()+1);
6661 // Set the range we'll need to delete in Undo
6662 action
->SetRange(range
);
6664 buffer
->SubmitAction(action
);
6669 /// Submit command to insert the given text
6670 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6672 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6675 /// Submit command to insert the given text
6676 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6678 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6680 wxRichTextAttr
* p
= NULL
;
6681 wxRichTextAttr paraAttr
;
6682 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6684 // Get appropriate paragraph style
6685 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6686 if (!paraAttr
.IsDefault())
6690 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6692 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6694 if (!text
.empty() && text
.Last() != wxT('\n'))
6696 // Don't count the newline when undoing
6698 action
->GetNewParagraphs().SetPartialParagraph(true);
6700 else if (!text
.empty() && text
.Last() == wxT('\n'))
6703 action
->SetPosition(pos
);
6705 // Set the range we'll need to delete in Undo
6706 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6708 buffer
->SubmitAction(action
);
6713 /// Submit command to insert the given text
6714 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6716 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6719 /// Submit command to insert the given text
6720 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6722 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6724 wxRichTextAttr
* p
= NULL
;
6725 wxRichTextAttr paraAttr
;
6726 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6728 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6729 if (!paraAttr
.IsDefault())
6733 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6735 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6736 action
->GetNewParagraphs().AppendChild(newPara
);
6737 action
->GetNewParagraphs().UpdateRanges();
6738 action
->GetNewParagraphs().SetPartialParagraph(false);
6739 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6743 newPara
->SetAttributes(*p
);
6745 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6747 if (para
&& para
->GetRange().GetEnd() == pos
)
6750 // Now see if we need to number the paragraph.
6751 if (newPara
->GetAttributes().HasBulletNumber())
6753 wxRichTextAttr numberingAttr
;
6754 if (FindNextParagraphNumber(para
, numberingAttr
))
6755 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6759 action
->SetPosition(pos
);
6761 // Use the default character style
6762 // Use the default character style
6763 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6765 // Check whether the default style merely reflects the paragraph/basic style,
6766 // in which case don't apply it.
6767 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6768 wxRichTextAttr toApply
;
6771 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6772 wxRichTextAttr newAttr
;
6773 // This filters out attributes that are accounted for by the current
6774 // paragraph/basic style
6775 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6778 toApply
= defaultStyle
;
6780 if (!toApply
.IsDefault())
6781 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6784 // Set the range we'll need to delete in Undo
6785 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6787 buffer
->SubmitAction(action
);
6792 /// Submit command to insert the given image
6793 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6794 const wxRichTextAttr
& textAttr
)
6796 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
6799 /// Submit command to insert the given image
6800 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
6801 wxRichTextCtrl
* ctrl
, int flags
,
6802 const wxRichTextAttr
& textAttr
)
6804 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6806 wxRichTextAttr
* p
= NULL
;
6807 wxRichTextAttr paraAttr
;
6808 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6810 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6811 if (!paraAttr
.IsDefault())
6815 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6817 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6819 newPara
->SetAttributes(*p
);
6821 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6822 newPara
->AppendChild(imageObject
);
6823 imageObject
->SetAttributes(textAttr
);
6824 action
->GetNewParagraphs().AppendChild(newPara
);
6825 action
->GetNewParagraphs().UpdateRanges();
6827 action
->GetNewParagraphs().SetPartialParagraph(true);
6829 action
->SetPosition(pos
);
6831 // Set the range we'll need to delete in Undo
6832 action
->SetRange(wxRichTextRange(pos
, pos
));
6834 buffer
->SubmitAction(action
);
6839 // Insert an object with no change of it
6840 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6842 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
6845 // Insert an object with no change of it
6846 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6848 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6850 wxRichTextAttr
* p
= NULL
;
6851 wxRichTextAttr paraAttr
;
6852 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6854 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6855 if (!paraAttr
.IsDefault())
6859 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6861 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6863 newPara
->SetAttributes(*p
);
6865 newPara
->AppendChild(object
);
6866 action
->GetNewParagraphs().AppendChild(newPara
);
6867 action
->GetNewParagraphs().UpdateRanges();
6869 action
->GetNewParagraphs().SetPartialParagraph(true);
6871 action
->SetPosition(pos
);
6873 // Set the range we'll need to delete in Undo
6874 action
->SetRange(wxRichTextRange(pos
, pos
));
6876 buffer
->SubmitAction(action
);
6878 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
6882 /// Get the style that is appropriate for a new paragraph at this position.
6883 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6885 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
6887 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
6890 wxRichTextAttr attr
;
6891 bool foundAttributes
= false;
6893 // Look for a matching paragraph style
6894 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
6896 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
6899 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6900 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
6902 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
6905 foundAttributes
= true;
6906 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6910 // If we didn't find the 'next style', use this style instead.
6911 if (!foundAttributes
)
6913 foundAttributes
= true;
6914 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6919 // Also apply list style if present
6920 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
6922 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
6925 int thisIndent
= para
->GetAttributes().GetLeftIndent();
6926 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
6928 // Apply the overall list style, and item style for this level
6929 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
6930 wxRichTextApplyStyle(attr
, listStyle
);
6931 attr
.SetOutlineLevel(thisLevel
);
6932 if (para
->GetAttributes().HasBulletNumber())
6933 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
6937 if (!foundAttributes
)
6939 attr
= para
->GetAttributes();
6940 int flags
= attr
.GetFlags();
6942 // Eliminate character styles
6943 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
6944 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
6945 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
6946 attr
.SetFlags(flags
);
6952 return wxRichTextAttr();
6955 /// Submit command to delete this range
6956 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
6958 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
6961 /// Submit command to delete this range
6962 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
6964 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
6966 action
->SetPosition(ctrl
->GetCaretPosition());
6968 // Set the range to delete
6969 action
->SetRange(range
);
6971 // Copy the fragment that we'll need to restore in Undo
6972 CopyFragment(range
, action
->GetOldParagraphs());
6974 // See if we're deleting a paragraph marker, in which case we need to
6975 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6976 if (range
.GetStart() == range
.GetEnd())
6978 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
6979 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
6981 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
6982 if (nextPara
&& nextPara
!= para
)
6984 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
6985 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
6990 buffer
->SubmitAction(action
);
6995 /// Collapse undo/redo commands
6996 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
6998 if (m_batchedCommandDepth
== 0)
7000 wxASSERT(m_batchedCommand
== NULL
);
7001 if (m_batchedCommand
)
7003 GetCommandProcessor()->Store(m_batchedCommand
);
7005 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7008 m_batchedCommandDepth
++;
7013 /// Collapse undo/redo commands
7014 bool wxRichTextBuffer::EndBatchUndo()
7016 m_batchedCommandDepth
--;
7018 wxASSERT(m_batchedCommandDepth
>= 0);
7019 wxASSERT(m_batchedCommand
!= NULL
);
7021 if (m_batchedCommandDepth
== 0)
7023 GetCommandProcessor()->Store(m_batchedCommand
);
7024 m_batchedCommand
= NULL
;
7030 /// Submit immediately, or delay according to whether collapsing is on
7031 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7033 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7035 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7036 cmd
->AddAction(action
);
7038 cmd
->GetActions().Clear();
7041 m_batchedCommand
->AddAction(action
);
7045 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7046 cmd
->AddAction(action
);
7048 // Only store it if we're not suppressing undo.
7049 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7055 /// Begin suppressing undo/redo commands.
7056 bool wxRichTextBuffer::BeginSuppressUndo()
7063 /// End suppressing undo/redo commands.
7064 bool wxRichTextBuffer::EndSuppressUndo()
7071 /// Begin using a style
7072 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7074 wxRichTextAttr
newStyle(GetDefaultStyle());
7076 // Save the old default style
7077 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7079 wxRichTextApplyStyle(newStyle
, style
);
7080 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7082 SetDefaultStyle(newStyle
);
7088 bool wxRichTextBuffer::EndStyle()
7090 if (!m_attributeStack
.GetFirst())
7092 wxLogDebug(_("Too many EndStyle calls!"));
7096 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7097 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7098 m_attributeStack
.Erase(node
);
7100 SetDefaultStyle(*attr
);
7107 bool wxRichTextBuffer::EndAllStyles()
7109 while (m_attributeStack
.GetCount() != 0)
7114 /// Clear the style stack
7115 void wxRichTextBuffer::ClearStyleStack()
7117 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7118 delete (wxRichTextAttr
*) node
->GetData();
7119 m_attributeStack
.Clear();
7122 /// Begin using bold
7123 bool wxRichTextBuffer::BeginBold()
7125 wxRichTextAttr attr
;
7126 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7128 return BeginStyle(attr
);
7131 /// Begin using italic
7132 bool wxRichTextBuffer::BeginItalic()
7134 wxRichTextAttr attr
;
7135 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7137 return BeginStyle(attr
);
7140 /// Begin using underline
7141 bool wxRichTextBuffer::BeginUnderline()
7143 wxRichTextAttr attr
;
7144 attr
.SetFontUnderlined(true);
7146 return BeginStyle(attr
);
7149 /// Begin using point size
7150 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7152 wxRichTextAttr attr
;
7153 attr
.SetFontSize(pointSize
);
7155 return BeginStyle(attr
);
7158 /// Begin using this font
7159 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7161 wxRichTextAttr attr
;
7164 return BeginStyle(attr
);
7167 /// Begin using this colour
7168 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7170 wxRichTextAttr attr
;
7171 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7172 attr
.SetTextColour(colour
);
7174 return BeginStyle(attr
);
7177 /// Begin using alignment
7178 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7180 wxRichTextAttr attr
;
7181 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7182 attr
.SetAlignment(alignment
);
7184 return BeginStyle(attr
);
7187 /// Begin left indent
7188 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7190 wxRichTextAttr attr
;
7191 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7192 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7194 return BeginStyle(attr
);
7197 /// Begin right indent
7198 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7200 wxRichTextAttr attr
;
7201 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7202 attr
.SetRightIndent(rightIndent
);
7204 return BeginStyle(attr
);
7207 /// Begin paragraph spacing
7208 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7212 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7214 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7216 wxRichTextAttr attr
;
7217 attr
.SetFlags(flags
);
7218 attr
.SetParagraphSpacingBefore(before
);
7219 attr
.SetParagraphSpacingAfter(after
);
7221 return BeginStyle(attr
);
7224 /// Begin line spacing
7225 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7227 wxRichTextAttr attr
;
7228 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7229 attr
.SetLineSpacing(lineSpacing
);
7231 return BeginStyle(attr
);
7234 /// Begin numbered bullet
7235 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7237 wxRichTextAttr attr
;
7238 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7239 attr
.SetBulletStyle(bulletStyle
);
7240 attr
.SetBulletNumber(bulletNumber
);
7241 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7243 return BeginStyle(attr
);
7246 /// Begin symbol bullet
7247 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7249 wxRichTextAttr attr
;
7250 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7251 attr
.SetBulletStyle(bulletStyle
);
7252 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7253 attr
.SetBulletText(symbol
);
7255 return BeginStyle(attr
);
7258 /// Begin standard bullet
7259 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7261 wxRichTextAttr attr
;
7262 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7263 attr
.SetBulletStyle(bulletStyle
);
7264 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7265 attr
.SetBulletName(bulletName
);
7267 return BeginStyle(attr
);
7270 /// Begin named character style
7271 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7273 if (GetStyleSheet())
7275 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7278 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7279 return BeginStyle(attr
);
7285 /// Begin named paragraph style
7286 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7288 if (GetStyleSheet())
7290 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7293 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7294 return BeginStyle(attr
);
7300 /// Begin named list style
7301 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7303 if (GetStyleSheet())
7305 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7308 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7310 attr
.SetBulletNumber(number
);
7312 return BeginStyle(attr
);
7319 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7321 wxRichTextAttr attr
;
7323 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7325 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7328 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7333 return BeginStyle(attr
);
7336 /// Adds a handler to the end
7337 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7339 sm_handlers
.Append(handler
);
7342 /// Inserts a handler at the front
7343 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7345 sm_handlers
.Insert( handler
);
7348 /// Removes a handler
7349 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7351 wxRichTextFileHandler
*handler
= FindHandler(name
);
7354 sm_handlers
.DeleteObject(handler
);
7362 /// Finds a handler by filename or, if supplied, type
7363 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7364 wxRichTextFileType imageType
)
7366 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7367 return FindHandler(imageType
);
7368 else if (!filename
.IsEmpty())
7370 wxString path
, file
, ext
;
7371 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7372 return FindHandler(ext
, imageType
);
7379 /// Finds a handler by name
7380 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7382 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7385 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7386 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7388 node
= node
->GetNext();
7393 /// Finds a handler by extension and type
7394 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7396 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7399 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7400 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7401 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7403 node
= node
->GetNext();
7408 /// Finds a handler by type
7409 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7411 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7414 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7415 if (handler
->GetType() == type
) return handler
;
7416 node
= node
->GetNext();
7421 void wxRichTextBuffer::InitStandardHandlers()
7423 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7424 AddHandler(new wxRichTextPlainTextHandler
);
7427 void wxRichTextBuffer::CleanUpHandlers()
7429 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7432 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7433 wxList::compatibility_iterator next
= node
->GetNext();
7438 sm_handlers
.Clear();
7441 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7448 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7452 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7453 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7458 wildcard
+= wxT(";");
7459 wildcard
+= wxT("*.") + handler
->GetExtension();
7464 wildcard
+= wxT("|");
7465 wildcard
+= handler
->GetName();
7466 wildcard
+= wxT(" ");
7467 wildcard
+= _("files");
7468 wildcard
+= wxT(" (*.");
7469 wildcard
+= handler
->GetExtension();
7470 wildcard
+= wxT(")|*.");
7471 wildcard
+= handler
->GetExtension();
7473 types
->Add(handler
->GetType());
7478 node
= node
->GetNext();
7482 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7487 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7489 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7492 SetDefaultStyle(wxRichTextAttr());
7493 handler
->SetFlags(GetHandlerFlags());
7494 bool success
= handler
->LoadFile(this, filename
);
7495 Invalidate(wxRICHTEXT_ALL
);
7503 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7505 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7508 handler
->SetFlags(GetHandlerFlags());
7509 return handler
->SaveFile(this, filename
);
7515 /// Load from a stream
7516 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7518 wxRichTextFileHandler
* handler
= FindHandler(type
);
7521 SetDefaultStyle(wxRichTextAttr());
7522 handler
->SetFlags(GetHandlerFlags());
7523 bool success
= handler
->LoadFile(this, stream
);
7524 Invalidate(wxRICHTEXT_ALL
);
7531 /// Save to a stream
7532 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7534 wxRichTextFileHandler
* handler
= FindHandler(type
);
7537 handler
->SetFlags(GetHandlerFlags());
7538 return handler
->SaveFile(this, stream
);
7544 /// Copy the range to the clipboard
7545 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7547 bool success
= false;
7548 wxRichTextParagraphLayoutBox
* container
= this;
7549 if (GetRichTextCtrl())
7550 container
= GetRichTextCtrl()->GetFocusObject();
7552 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7554 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7556 wxTheClipboard
->Clear();
7558 // Add composite object
7560 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7563 wxString text
= container
->GetTextForRange(range
);
7566 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7569 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7572 // Add rich text buffer data object. This needs the XML handler to be present.
7574 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7576 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7577 container
->CopyFragment(range
, *richTextBuf
);
7579 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7582 if (wxTheClipboard
->SetData(compositeObject
))
7585 wxTheClipboard
->Close();
7594 /// Paste the clipboard content to the buffer
7595 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7597 bool success
= false;
7598 wxRichTextParagraphLayoutBox
* container
= this;
7599 if (GetRichTextCtrl())
7600 container
= GetRichTextCtrl()->GetFocusObject();
7602 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7603 if (CanPasteFromClipboard())
7605 if (wxTheClipboard
->Open())
7607 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7609 wxRichTextBufferDataObject data
;
7610 wxTheClipboard
->GetData(data
);
7611 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7614 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7615 if (GetRichTextCtrl())
7616 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7617 delete richTextBuffer
;
7620 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7622 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7626 wxTextDataObject data
;
7627 wxTheClipboard
->GetData(data
);
7628 wxString
text(data
.GetText());
7631 text2
.Alloc(text
.Length()+1);
7633 for (i
= 0; i
< text
.Length(); i
++)
7635 wxChar ch
= text
[i
];
7636 if (ch
!= wxT('\r'))
7640 wxString text2
= text
;
7642 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7644 if (GetRichTextCtrl())
7645 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7649 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7651 wxBitmapDataObject data
;
7652 wxTheClipboard
->GetData(data
);
7653 wxBitmap
bitmap(data
.GetBitmap());
7654 wxImage
image(bitmap
.ConvertToImage());
7656 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7658 action
->GetNewParagraphs().AddImage(image
);
7660 if (action
->GetNewParagraphs().GetChildCount() == 1)
7661 action
->GetNewParagraphs().SetPartialParagraph(true);
7663 action
->SetPosition(position
+1);
7665 // Set the range we'll need to delete in Undo
7666 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7668 SubmitAction(action
);
7672 wxTheClipboard
->Close();
7676 wxUnusedVar(position
);
7681 /// Can we paste from the clipboard?
7682 bool wxRichTextBuffer::CanPasteFromClipboard() const
7684 bool canPaste
= false;
7685 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7686 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7688 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7690 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7692 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7693 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7697 wxTheClipboard
->Close();
7703 /// Dumps contents of buffer for debugging purposes
7704 void wxRichTextBuffer::Dump()
7708 wxStringOutputStream
stream(& text
);
7709 wxTextOutputStream
textStream(stream
);
7716 /// Add an event handler
7717 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7719 m_eventHandlers
.Append(handler
);
7723 /// Remove an event handler
7724 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7726 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7729 m_eventHandlers
.Erase(node
);
7739 /// Clear event handlers
7740 void wxRichTextBuffer::ClearEventHandlers()
7742 m_eventHandlers
.Clear();
7745 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7746 /// otherwise will stop at the first successful one.
7747 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7749 bool success
= false;
7750 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7752 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7753 if (handler
->ProcessEvent(event
))
7763 /// Set style sheet and notify of the change
7764 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7766 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7768 wxWindowID winid
= wxID_ANY
;
7769 if (GetRichTextCtrl())
7770 winid
= GetRichTextCtrl()->GetId();
7772 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7773 event
.SetEventObject(GetRichTextCtrl());
7774 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7775 event
.SetOldStyleSheet(oldSheet
);
7776 event
.SetNewStyleSheet(sheet
);
7779 if (SendEvent(event
) && !event
.IsAllowed())
7781 if (sheet
!= oldSheet
)
7787 if (oldSheet
&& oldSheet
!= sheet
)
7790 SetStyleSheet(sheet
);
7792 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7793 event
.SetOldStyleSheet(NULL
);
7796 return SendEvent(event
);
7799 /// Set renderer, deleting old one
7800 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7804 sm_renderer
= renderer
;
7807 /// Hit-testing: returns a flag indicating hit test details, plus
7808 /// information about position
7809 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7811 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7812 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7818 textPosition
= m_ownRange
.GetEnd()-1;
7821 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7825 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7827 if (bulletAttr
.GetTextColour().IsOk())
7829 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7830 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7834 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7835 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7839 if (bulletAttr
.HasFont())
7841 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
7844 font
= (*wxNORMAL_FONT
);
7846 wxCheckSetFont(dc
, font
);
7848 int charHeight
= dc
.GetCharHeight();
7850 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
7851 int bulletHeight
= bulletWidth
;
7855 // Calculate the top position of the character (as opposed to the whole line height)
7856 int y
= rect
.y
+ (rect
.height
- charHeight
);
7858 // Calculate where the bullet should be positioned
7859 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
7861 // The margin between a bullet and text.
7862 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7864 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7865 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
7866 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7867 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
7869 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
7871 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
7873 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
7876 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
7877 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
7878 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
7879 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
7881 dc
.DrawPolygon(4, pts
);
7883 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
7886 pts
[0].x
= x
; pts
[0].y
= y
;
7887 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
7888 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
7890 dc
.DrawPolygon(3, pts
);
7892 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
7894 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
7895 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7897 else // "standard/circle", and catch-all
7899 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7905 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
7910 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
7912 wxRichTextAttr fontAttr
;
7913 fontAttr
.SetFontSize(attr
.GetFontSize());
7914 fontAttr
.SetFontStyle(attr
.GetFontStyle());
7915 fontAttr
.SetFontWeight(attr
.GetFontWeight());
7916 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
7917 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
7918 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
7920 else if (attr
.HasFont())
7921 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
7923 font
= (*wxNORMAL_FONT
);
7925 wxCheckSetFont(dc
, font
);
7927 if (attr
.GetTextColour().IsOk())
7928 dc
.SetTextForeground(attr
.GetTextColour());
7930 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
7932 int charHeight
= dc
.GetCharHeight();
7934 dc
.GetTextExtent(text
, & tw
, & th
);
7938 // Calculate the top position of the character (as opposed to the whole line height)
7939 int y
= rect
.y
+ (rect
.height
- charHeight
);
7941 // The margin between a bullet and text.
7942 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7944 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7945 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
7946 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7947 x
= x
+ (rect
.width
)/2 - tw
/2;
7949 dc
.DrawText(text
, x
, y
);
7957 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
7959 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7960 // with the buffer. The store will allow retrieval from memory, disk or other means.
7964 /// Enumerate the standard bullet names currently supported
7965 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
7967 bulletNames
.Add(wxTRANSLATE("standard/circle"));
7968 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
7969 bulletNames
.Add(wxTRANSLATE("standard/square"));
7970 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
7971 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
7980 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
7982 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
7983 wxRichTextParagraphLayoutBox(parent
)
7988 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
7993 // TODO: if the active object in the control, draw an indication.
7994 // We need to add the concept of active object, and not just focus object,
7995 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
7996 // Ultimately we would like to be able to interactively resize an active object
7997 // using drag handles.
7998 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8002 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8004 wxRichTextParagraphLayoutBox::Copy(obj
);
8007 // Edit properties via a GUI
8008 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8010 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8011 boxDlg
.SetAttributes(GetAttributes());
8013 if (boxDlg
.ShowModal() == wxID_OK
)
8015 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8016 // indeterminate in the object.
8017 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8024 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8026 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8027 wxRichTextBox(parent
)
8032 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8034 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8038 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8040 wxRichTextBox::Copy(obj
);
8043 // Edit properties via a GUI
8044 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8046 // We need to gather common attributes for all selected cells.
8048 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8049 bool multipleCells
= false;
8050 wxRichTextAttr attr
;
8052 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8053 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8055 wxRichTextAttr clashingAttr
, absentAttr
;
8056 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8058 int selectedCellCount
= 0;
8059 for (i
= 0; i
< sel
.GetCount(); i
++)
8061 const wxRichTextRange
& range
= sel
[i
];
8062 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8065 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8067 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8069 selectedCellCount
++;
8072 multipleCells
= selectedCellCount
> 1;
8076 attr
= GetAttributes();
8081 caption
= _("Multiple Cell Properties");
8083 caption
= _("Cell Properties");
8085 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8086 cellDlg
.SetAttributes(attr
);
8088 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8091 // We don't want position and floating controls for a cell.
8092 sizePage
->ShowPositionControls(false);
8093 sizePage
->ShowFloatingControls(false);
8096 if (cellDlg
.ShowModal() == wxID_OK
)
8100 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8101 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8102 // since it may represent clashing attributes across multiple objects.
8103 table
->SetCellStyle(sel
, attr
);
8106 // For a single object, indeterminate attributes set by the user should be reflected in the
8107 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8108 // the style directly instead of applying (which ignores indeterminate attributes,
8109 // leaving them as they were).
8110 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8117 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8119 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8121 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8127 // Draws the object.
8128 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8130 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8133 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8134 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8136 // Lays the object out. rect is the space available for layout. Often it will
8137 // be the specified overall space for this object, if trying to constrain
8138 // layout to a particular size, or it could be the total space available in the
8139 // parent. rect is the overall size, so we must subtract margins and padding.
8140 // to get the actual available space.
8141 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8143 SetPosition(rect
.GetPosition());
8145 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8146 // minimum size if within alloted size, then divide up remaining size
8147 // between rows/cols.
8150 wxRichTextBuffer
* buffer
= GetBuffer();
8151 if (buffer
) scale
= buffer
->GetScale();
8153 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8154 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8156 // If we have no fixed table size, and assuming we're not pushed for
8157 // space, then we don't have to try to stretch the table to fit the contents.
8158 bool stretchToFitTableWidth
= false;
8160 int tableWidth
= rect
.width
;
8161 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8163 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8165 // Fixed table width, so we do want to stretch columns out if necessary.
8166 stretchToFitTableWidth
= true;
8168 // Shouldn't be able to exceed the size passed to this function
8169 tableWidth
= wxMin(rect
.width
, tableWidth
);
8172 // Get internal padding
8173 int paddingLeft
= 0, paddingTop
= 0;
8174 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8175 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8176 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8177 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8179 // Assume that left and top padding are also used for inter-cell padding.
8180 int paddingX
= paddingLeft
;
8181 int paddingY
= paddingTop
;
8183 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8184 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8186 // Internal table width - the area for content
8187 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8189 int rowCount
= m_cells
.GetCount();
8190 if (m_colCount
== 0 || rowCount
== 0)
8192 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8193 SetCachedSize(overallRect
.GetSize());
8195 // Zero content size
8196 SetMinSize(overallRect
.GetSize());
8197 SetMaxSize(GetMinSize());
8201 // The final calculated widths
8202 wxArrayInt colWidths
;
8203 colWidths
.Add(0, m_colCount
);
8205 wxArrayInt absoluteColWidths
;
8206 absoluteColWidths
.Add(0, m_colCount
);
8207 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8208 wxArrayInt percentageColWidths
;
8209 percentageColWidths
.Add(0, m_colCount
);
8210 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8211 // These are only relevant when the first column contains spanning information.
8212 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8213 wxArrayInt maxColWidths
;
8214 maxColWidths
.Add(0, m_colCount
);
8215 wxArrayInt minColWidths
;
8216 minColWidths
.Add(0, m_colCount
);
8218 wxSize
tableSize(tableWidth
, 0);
8222 for (i
= 0; i
< m_colCount
; i
++)
8224 absoluteColWidths
[i
] = 0;
8225 // absoluteColWidthsSpanning[i] = 0;
8226 percentageColWidths
[i
] = -1;
8227 // percentageColWidthsSpanning[i] = -1;
8229 maxColWidths
[i
] = 0;
8230 minColWidths
[i
] = 0;
8231 // columnSpans[i] = 1;
8234 // (0) Determine which cells are visible according to spans
8236 // __________________
8241 // |------------------|
8242 // |__________________| 4
8244 // To calculate cell visibility:
8245 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8246 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8247 // that cell, hide the cell.
8249 // We can also use this array to match the size of spanning cells to the grid. Or just do
8250 // this when we iterate through all cells.
8252 // 0.1: add spanning cells to an array
8253 wxRichTextRectArray rectArray
;
8254 for (j
= 0; j
< m_rowCount
; j
++)
8256 for (i
= 0; i
< m_colCount
; i
++)
8258 wxRichTextBox
* cell
= GetCell(j
, i
);
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 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8270 // 0.2: find which cells are subsumed by a spanning cell
8271 for (j
= 0; j
< m_rowCount
; j
++)
8273 for (i
= 0; i
< m_colCount
; i
++)
8275 wxRichTextBox
* cell
= GetCell(j
, i
);
8276 if (rectArray
.GetCount() == 0)
8282 int colSpan
= 1, rowSpan
= 1;
8283 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8284 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8285 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8286 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8287 if (colSpan
> 1 || rowSpan
> 1)
8289 // Assume all spanning cells are shown
8295 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8297 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8309 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8310 // overlap with a spanned cell starting at a previous column position.
8311 // This means we need to keep an array of rects so we can check. However
8312 // it does also mean that some spans simply may not be taken into account
8313 // where there are different spans happening on different rows. In these cases,
8314 // they will simply be as wide as their constituent columns.
8316 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8317 // the absolute or percentage width of each column.
8319 for (j
= 0; j
< m_rowCount
; j
++)
8321 // First get the overall margins so we can calculate percentage widths based on
8322 // the available content space for all cells on the row
8324 int overallRowContentMargin
= 0;
8325 int visibleCellCount
= 0;
8327 for (i
= 0; i
< m_colCount
; i
++)
8329 wxRichTextBox
* cell
= GetCell(j
, i
);
8330 if (cell
->IsShown())
8332 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8333 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8335 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8336 visibleCellCount
++;
8340 // Add in inter-cell padding
8341 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8343 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8344 wxSize
rowTableSize(rowContentWidth
, 0);
8345 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8347 for (i
= 0; i
< m_colCount
; i
++)
8349 wxRichTextBox
* cell
= GetCell(j
, i
);
8350 if (cell
->IsShown())
8353 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8354 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8356 // Lay out cell to find min/max widths
8357 cell
->Invalidate(wxRICHTEXT_ALL
);
8358 cell
->Layout(dc
, availableSpace
, availableSpace
, style
);
8362 int absoluteCellWidth
= -1;
8363 int percentageCellWidth
= -1;
8365 // I think we need to calculate percentages from the internal table size,
8366 // minus the padding between cells which we'll need to calculate from the
8367 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8368 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8369 // so if we want to conform to that we'll need to add in the overall cell margins.
8370 // However, this will make it difficult to specify percentages that add up to
8371 // 100% and still fit within the table width.
8372 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8373 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8374 // If we're using internal content size for the width, we would calculate the
8375 // the overall cell width for n cells as:
8376 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8377 // + thisOverallCellMargin
8378 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8379 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8381 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8383 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8384 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8386 percentageCellWidth
= w
;
8390 absoluteCellWidth
= w
;
8392 // Override absolute width with minimum width if necessary
8393 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8394 absoluteCellWidth
= cell
->GetMinSize().x
;
8397 if (absoluteCellWidth
!= -1)
8399 if (absoluteCellWidth
> absoluteColWidths
[i
])
8400 absoluteColWidths
[i
] = absoluteCellWidth
;
8403 if (percentageCellWidth
!= -1)
8405 if (percentageCellWidth
> percentageColWidths
[i
])
8406 percentageColWidths
[i
] = percentageCellWidth
;
8409 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8410 minColWidths
[i
] = cell
->GetMinSize().x
;
8411 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8412 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8418 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8419 // TODO: simply merge this into (1).
8420 for (i
= 0; i
< m_colCount
; i
++)
8422 if (absoluteColWidths
[i
] > 0)
8424 colWidths
[i
] = absoluteColWidths
[i
];
8426 else if (percentageColWidths
[i
] > 0)
8428 colWidths
[i
] = percentageColWidths
[i
];
8430 // This is rubbish - we calculated the absolute widths from percentages, so
8431 // we can't do it again here.
8432 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8436 // (3) Process absolute or proportional widths of spanning columns,
8437 // now that we know what our fixed column widths are going to be.
8438 // Spanned cells will try to adjust columns so the span will fit.
8439 // Even existing fixed column widths can be expanded if necessary.
8440 // Actually, currently fixed columns widths aren't adjusted; instead,
8441 // the algorithm favours earlier rows and adjusts unspecified column widths
8442 // the first time only. After that, we can't know whether the column has been
8443 // specified explicitly or not. (We could make a note if necessary.)
8444 for (j
= 0; j
< m_rowCount
; j
++)
8446 // First get the overall margins so we can calculate percentage widths based on
8447 // the available content space for all cells on the row
8449 int overallRowContentMargin
= 0;
8450 int visibleCellCount
= 0;
8452 for (i
= 0; i
< m_colCount
; i
++)
8454 wxRichTextBox
* cell
= GetCell(j
, i
);
8455 if (cell
->IsShown())
8457 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8458 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8460 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8461 visibleCellCount
++;
8465 // Add in inter-cell padding
8466 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8468 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8469 wxSize
rowTableSize(rowContentWidth
, 0);
8470 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8472 for (i
= 0; i
< m_colCount
; i
++)
8474 wxRichTextBox
* cell
= GetCell(j
, i
);
8475 if (cell
->IsShown())
8478 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8479 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8483 int spans
= wxMin(colSpan
, m_colCount
- i
);
8487 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8489 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8490 // Override absolute width with minimum width if necessary
8491 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8492 cellWidth
= cell
->GetMinSize().x
;
8496 // Do we want to do this? It's the only chance we get to
8497 // use the cell's min/max sizes, so we need to work out
8498 // how we're going to balance the unspecified spanning cell
8499 // width with the possibility more-constrained constituent cell widths.
8500 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8501 // don't want to constraint all the spanned columns to fit into this cell.
8502 // OK, let's say that if any of the constituent columns don't fit,
8503 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8504 // cells to the columns later.
8505 cellWidth
= cell
->GetMinSize().x
;
8506 if (cell
->GetMaxSize().x
> cellWidth
)
8507 cellWidth
= cell
->GetMaxSize().x
;
8510 // Subtract the padding between cells
8511 int spanningWidth
= cellWidth
;
8512 spanningWidth
-= paddingX
* (spans
-1);
8514 if (spanningWidth
> 0)
8516 // Now share the spanning width between columns within that span
8517 // TODO: take into account min widths of columns within the span
8518 int spanningWidthLeft
= spanningWidth
;
8519 int stretchColCount
= 0;
8520 for (k
= i
; k
< (i
+spans
); k
++)
8522 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8523 spanningWidthLeft
-= colWidths
[k
];
8527 // Now divide what's left between the remaining columns
8529 if (stretchColCount
> 0)
8530 colShare
= spanningWidthLeft
/ stretchColCount
;
8531 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8533 // If fixed-width columns are currently too big, then we'll later
8534 // stretch the spanned cell to fit.
8536 if (spanningWidthLeft
> 0)
8538 for (k
= i
; k
< (i
+spans
); k
++)
8540 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8542 int newWidth
= colShare
;
8543 if (k
== (i
+spans
-1))
8544 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8545 colWidths
[k
] = newWidth
;
8556 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8557 // TODO: take into account min widths of columns within the span
8558 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8559 int widthLeft
= tableWidthMinusPadding
;
8560 int stretchColCount
= 0;
8561 for (i
= 0; i
< m_colCount
; i
++)
8563 // TODO: we need to take into account min widths.
8564 // Subtract min width from width left, then
8565 // add the colShare to the min width
8566 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8567 widthLeft
-= colWidths
[i
];
8570 if (minColWidths
[i
] > 0)
8571 widthLeft
-= minColWidths
[i
];
8577 // Now divide what's left between the remaining columns
8579 if (stretchColCount
> 0)
8580 colShare
= widthLeft
/ stretchColCount
;
8581 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8583 // Check we don't have enough space, in which case shrink all columns, overriding
8584 // any absolute/proportional widths
8585 // TODO: actually we would like to divide up the shrinkage according to size.
8586 // How do we calculate the proportions that will achieve this?
8587 // Could first choose an arbitrary value for stretching cells, and then calculate
8588 // factors to multiply each width by.
8589 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8590 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8592 colShare
= tableWidthMinusPadding
/ m_colCount
;
8593 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8594 for (i
= 0; i
< m_colCount
; i
++)
8597 minColWidths
[i
] = 0;
8601 // We have to adjust the columns if either we need to shrink the
8602 // table to fit the parent/table width, or we explicitly set the
8603 // table width and need to stretch out the table.
8604 if (widthLeft
< 0 || stretchToFitTableWidth
)
8606 for (i
= 0; i
< m_colCount
; i
++)
8608 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8610 if (minColWidths
[i
] > 0)
8611 colWidths
[i
] = minColWidths
[i
] + colShare
;
8613 colWidths
[i
] = colShare
;
8614 if (i
== (m_colCount
-1))
8615 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8620 // TODO: if spanned cells have no specified or max width, make them the
8621 // as big as the columns they span. Do this for all spanned cells in all
8622 // rows, of course. Size any spanned cells left over at the end - even if they
8623 // have width > 0, make sure they're limited to the appropriate column edge.
8627 Sort out confusion between content width
8628 and overall width later. For now, assume we specify overall width.
8630 So, now we've laid out the table to fit into the given space
8631 and have used specified widths and minimum widths.
8633 Now we need to consider how we will try to take maximum width into account.
8637 // (??) TODO: take max width into account
8639 // (6) Lay out all cells again with the current values
8642 int y
= availableSpace
.y
;
8643 for (j
= 0; j
< m_rowCount
; j
++)
8645 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8646 int maxCellHeight
= 0;
8647 int maxSpecifiedCellHeight
= 0;
8649 wxArrayInt actualWidths
;
8650 actualWidths
.Add(0, m_colCount
);
8652 wxTextAttrDimensionConverter
converter(dc
, scale
);
8653 for (i
= 0; i
< m_colCount
; i
++)
8655 wxRichTextCell
* cell
= GetCell(j
, i
);
8656 if (cell
->IsShown())
8658 // Get max specified cell height
8659 // Don't handle percentages for height
8660 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8662 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8663 if (h
> maxSpecifiedCellHeight
)
8664 maxSpecifiedCellHeight
= h
;
8667 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8670 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8671 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8673 wxRect availableCellSpace
;
8675 // TODO: take into acount spans
8678 // Calculate the size of this spanning cell from its constituent columns
8680 int spans
= wxMin(colSpan
, m_colCount
- i
);
8681 for (k
= i
; k
< spans
; k
++)
8687 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8690 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8692 // Store actual width so we can force cell to be the appropriate width on the final loop
8693 actualWidths
[i
] = availableCellSpace
.GetWidth();
8696 cell
->Invalidate(wxRICHTEXT_ALL
);
8697 cell
->Layout(dc
, availableCellSpace
, availableSpace
, style
);
8699 // TODO: use GetCachedSize().x to compute 'natural' size
8701 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8702 if (cell
->GetCachedSize().y
> maxCellHeight
)
8703 maxCellHeight
= cell
->GetCachedSize().y
;
8708 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8710 for (i
= 0; i
< m_colCount
; i
++)
8712 wxRichTextCell
* cell
= GetCell(j
, i
);
8713 if (cell
->IsShown())
8715 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8716 // Lay out cell with new height
8717 cell
->Invalidate(wxRICHTEXT_ALL
);
8718 cell
->Layout(dc
, availableCellSpace
, availableSpace
, style
);
8720 // Make sure the cell size really is the appropriate size,
8721 // not the calculated box size
8722 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8724 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8729 if (j
< (m_rowCount
-1))
8733 // We need to add back the margins etc.
8735 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8736 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8737 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8738 SetCachedSize(marginRect
.GetSize());
8741 // TODO: calculate max size
8743 SetMaxSize(GetCachedSize());
8746 // TODO: calculate min size
8748 SetMinSize(GetCachedSize());
8751 // TODO: currently we use either a fixed table width or the parent's size.
8752 // We also want to be able to calculate the table width from its content,
8753 // whether using fixed column widths or cell content min/max width.
8754 // Probably need a boolean flag to say whether we need to stretch cells
8755 // to fit the table width, or to simply use min/max cell widths. The
8756 // trouble with this is that if cell widths are not specified, they
8757 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8758 // Anyway, ignoring that problem, we probably need to factor layout into a function
8759 // that can can calculate the maximum unconstrained layout in case table size is
8760 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8761 // constrain Layout(), or the previously-calculated max size to constraint layout.
8766 // Finds the absolute position and row height for the given character position
8767 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8769 wxRichTextCell
* child
= GetCell(index
+1);
8772 // Find the position at the start of the child cell, since the table doesn't
8773 // have any caret position of its own.
8774 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8780 // Get the cell at the given character position (in the range of the table).
8781 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8783 int row
= 0, col
= 0;
8784 if (GetCellRowColumnPosition(pos
, row
, col
))
8786 return GetCell(row
, col
);
8792 // Get the row/column for a given character position
8793 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8795 if (m_colCount
== 0 || m_rowCount
== 0)
8798 row
= (int) (pos
/ m_colCount
);
8799 col
= pos
- (row
* m_colCount
);
8801 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8803 if (row
< m_rowCount
&& col
< m_colCount
)
8809 // Calculate range, taking row/cell ordering into account instead of relying
8810 // on list ordering.
8811 void wxRichTextTable::CalculateRange(long start
, long& end
)
8813 long current
= start
;
8814 long lastEnd
= current
;
8823 for (i
= 0; i
< m_rowCount
; i
++)
8825 for (j
= 0; j
< m_colCount
; j
++)
8827 wxRichTextCell
* child
= GetCell(i
, j
);
8832 child
->CalculateRange(current
, childEnd
);
8835 current
= childEnd
+ 1;
8840 // A top-level object always has a range of size 1,
8841 // because its children don't count at this level.
8843 m_range
.SetRange(start
, start
);
8845 // An object with no children has zero length
8846 if (m_children
.GetCount() == 0)
8848 m_ownRange
.SetRange(0, lastEnd
);
8851 // Gets the range size.
8852 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8854 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
8857 // Deletes content in the given range.
8858 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
8860 // TODO: implement deletion of cells
8864 // Gets any text in this object for the given range.
8865 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
8867 return wxRichTextBox::GetTextForRange(range
);
8870 // Copies this object.
8871 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
8873 wxRichTextBox::Copy(obj
);
8877 m_rowCount
= obj
.m_rowCount
;
8878 m_colCount
= obj
.m_colCount
;
8880 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
8883 for (i
= 0; i
< m_rowCount
; i
++)
8885 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8886 for (j
= 0; j
< m_colCount
; j
++)
8888 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
8896 void wxRichTextTable::ClearTable()
8902 bool wxRichTextTable::CreateTable(int rows
, int cols
)
8909 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
8912 for (i
= 0; i
< rows
; i
++)
8914 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8915 for (j
= 0; j
< cols
; j
++)
8917 wxRichTextCell
* cell
= new wxRichTextCell
;
8919 cell
->AddParagraph(wxEmptyString
);
8928 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
8930 wxASSERT(row
< m_rowCount
);
8931 wxASSERT(col
< m_colCount
);
8933 if (row
< m_rowCount
&& col
< m_colCount
)
8935 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
8936 wxRichTextObject
* obj
= colArray
[col
];
8937 return wxDynamicCast(obj
, wxRichTextCell
);
8943 // Returns a selection object specifying the selections between start and end character positions.
8944 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8945 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
8947 wxRichTextSelection selection
;
8948 selection
.SetContainer((wxRichTextTable
*) this);
8957 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
8959 if (end
>= (m_colCount
* m_rowCount
))
8962 // We need to find the rectangle of cells that is described by the rectangle
8963 // with start, end as the diagonal. Make sure we don't add cells that are
8964 // not currenty visible because they are overlapped by spanning cells.
8966 --------------------------
8967 | 0 | 1 | 2 | 3 | 4 |
8968 --------------------------
8969 | 5 | 6 | 7 | 8 | 9 |
8970 --------------------------
8971 | 10 | 11 | 12 | 13 | 14 |
8972 --------------------------
8973 | 15 | 16 | 17 | 18 | 19 |
8974 --------------------------
8976 Let's say we select 6 -> 18.
8978 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8979 which is left and which is right.
8981 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8983 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8989 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
8990 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
8992 int topRow
= int(start
/m_colCount
);
8993 int bottomRow
= int(end
/m_colCount
);
8995 if (leftCol
> rightCol
)
9002 if (topRow
> bottomRow
)
9004 int tmp
= bottomRow
;
9010 for (i
= topRow
; i
<= bottomRow
; i
++)
9012 for (j
= leftCol
; j
<= rightCol
; j
++)
9014 wxRichTextCell
* cell
= GetCell(i
, j
);
9015 if (cell
&& cell
->IsShown())
9016 selection
.Add(cell
->GetRange());
9023 // Sets the attributes for the cells specified by the selection.
9024 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9026 if (selection
.GetContainer() != this)
9029 wxRichTextBuffer
* buffer
= GetBuffer();
9030 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9031 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9034 buffer
->BeginBatchUndo(_("Set Cell Style"));
9036 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9039 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9040 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9041 SetStyle(cell
, style
, flags
);
9042 node
= node
->GetNext();
9045 // Do action, or delay it until end of batch.
9047 buffer
->EndBatchUndo();
9052 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9054 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9055 if ((startRow
+ noRows
) >= m_rowCount
)
9059 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9061 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9062 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9064 wxRichTextObject
* cell
= colArray
[j
];
9065 RemoveChild(cell
, true);
9068 // Keep deleting at the same position, since we move all
9070 m_cells
.RemoveAt(startRow
);
9073 m_rowCount
= m_rowCount
- noRows
;
9078 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9080 wxASSERT((startCol
+ noCols
) < m_colCount
);
9081 if ((startCol
+ noCols
) >= m_colCount
)
9084 bool deleteRows
= (noCols
== m_colCount
);
9087 for (i
= 0; i
< m_rowCount
; i
++)
9089 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9090 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9092 wxRichTextObject
* cell
= colArray
[j
];
9093 RemoveChild(cell
, true);
9097 m_cells
.RemoveAt(0);
9102 m_colCount
= m_colCount
- noCols
;
9107 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9109 wxASSERT(startRow
<= m_rowCount
);
9110 if (startRow
> m_rowCount
)
9114 for (i
= 0; i
< noRows
; i
++)
9117 if (startRow
== m_rowCount
)
9119 m_cells
.Add(wxRichTextObjectPtrArray());
9120 idx
= m_cells
.GetCount() - 1;
9124 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9128 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9129 for (j
= 0; j
< m_colCount
; j
++)
9131 wxRichTextCell
* cell
= new wxRichTextCell
;
9132 cell
->GetAttributes() = attr
;
9139 m_rowCount
= m_rowCount
+ noRows
;
9143 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9145 wxASSERT(startCol
<= m_colCount
);
9146 if (startCol
> m_colCount
)
9150 for (i
= 0; i
< m_rowCount
; i
++)
9152 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9153 for (j
= 0; j
< noCols
; j
++)
9155 wxRichTextCell
* cell
= new wxRichTextCell
;
9156 cell
->GetAttributes() = attr
;
9160 if (startCol
== m_colCount
)
9163 colArray
.Insert(cell
, startCol
+j
);
9167 m_colCount
= m_colCount
+ noCols
;
9172 // Edit properties via a GUI
9173 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9175 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9176 boxDlg
.SetAttributes(GetAttributes());
9178 if (boxDlg
.ShowModal() == wxID_OK
)
9180 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9188 * Module to initialise and clean up handlers
9191 class wxRichTextModule
: public wxModule
9193 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9195 wxRichTextModule() {}
9198 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9199 wxRichTextBuffer::InitStandardHandlers();
9200 wxRichTextParagraph::InitDefaultTabs();
9205 wxRichTextBuffer::CleanUpHandlers();
9206 wxRichTextDecimalToRoman(-1);
9207 wxRichTextParagraph::ClearDefaultTabs();
9208 wxRichTextCtrl::ClearAvailableFontNames();
9209 wxRichTextBuffer::SetRenderer(NULL
);
9213 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9216 // If the richtext lib is dynamically loaded after the app has already started
9217 // (such as from wxPython) then the built-in module system will not init this
9218 // module. Provide this function to do it manually.
9219 void wxRichTextModuleInit()
9221 wxModule
* module = new wxRichTextModule
;
9223 wxModule::RegisterModule(module);
9228 * Commands for undo/redo
9232 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9233 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9235 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9238 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9242 wxRichTextCommand::~wxRichTextCommand()
9247 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9249 if (!m_actions
.Member(action
))
9250 m_actions
.Append(action
);
9253 bool wxRichTextCommand::Do()
9255 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9257 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9264 bool wxRichTextCommand::Undo()
9266 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9268 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9275 void wxRichTextCommand::ClearActions()
9277 WX_CLEAR_LIST(wxList
, m_actions
);
9285 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9286 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9287 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9291 m_containerAddress
.Create(buffer
, container
);
9292 m_ignoreThis
= ignoreFirstTime
;
9297 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9298 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9300 cmd
->AddAction(this);
9303 wxRichTextAction::~wxRichTextAction()
9309 // Returns the container that this action refers to, using the container address and top-level buffer.
9310 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9312 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9317 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9319 // Store a list of line start character and y positions so we can figure out which area
9320 // we need to refresh
9322 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9323 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9324 wxASSERT(container
!= NULL
);
9328 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9329 // If we had several actions, which only invalidate and leave layout until the
9330 // paint handler is called, then this might not be true. So we may need to switch
9331 // optimisation on only when we're simply adding text and not simultaneously
9332 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9333 // first, but of course this means we'll be doing it twice.
9334 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9336 wxSize clientSize
= m_ctrl
->GetClientSize();
9337 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9338 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9340 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9341 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9344 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9345 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9348 wxRichTextLine
* line
= node2
->GetData();
9349 wxPoint pt
= line
->GetAbsolutePosition();
9350 wxRichTextRange range
= line
->GetAbsoluteRange();
9354 node2
= wxRichTextLineList::compatibility_iterator();
9355 node
= wxRichTextObjectList::compatibility_iterator();
9357 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9359 optimizationLineCharPositions
.Add(range
.GetStart());
9360 optimizationLineYPositions
.Add(pt
.y
);
9364 node2
= node2
->GetNext();
9368 node
= node
->GetNext();
9374 bool wxRichTextAction::Do()
9376 m_buffer
->Modify(true);
9378 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9379 wxASSERT(container
!= NULL
);
9385 case wxRICHTEXT_INSERT
:
9387 // Store a list of line start character and y positions so we can figure out which area
9388 // we need to refresh
9389 wxArrayInt optimizationLineCharPositions
;
9390 wxArrayInt optimizationLineYPositions
;
9392 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9393 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9396 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9397 container
->UpdateRanges();
9399 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9400 // Layout() would stop prematurely at the top level.
9401 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9403 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9405 // Character position to caret position
9406 newCaretPosition
--;
9408 // Don't take into account the last newline
9409 if (m_newParagraphs
.GetPartialParagraph())
9410 newCaretPosition
--;
9412 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9414 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9415 if (p
->GetRange().GetLength() == 1)
9416 newCaretPosition
--;
9419 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9421 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9423 wxRichTextEvent
cmdEvent(
9424 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9425 m_ctrl
? m_ctrl
->GetId() : -1);
9426 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9427 cmdEvent
.SetRange(GetRange());
9428 cmdEvent
.SetPosition(GetRange().GetStart());
9429 cmdEvent
.SetContainer(container
);
9431 m_buffer
->SendEvent(cmdEvent
);
9435 case wxRICHTEXT_DELETE
:
9437 wxArrayInt optimizationLineCharPositions
;
9438 wxArrayInt optimizationLineYPositions
;
9440 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9441 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9444 container
->DeleteRange(GetRange());
9445 container
->UpdateRanges();
9446 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9447 // Layout() would stop prematurely at the top level.
9448 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9450 long caretPos
= GetRange().GetStart()-1;
9451 if (caretPos
>= container
->GetOwnRange().GetEnd())
9454 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9456 wxRichTextEvent
cmdEvent(
9457 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9458 m_ctrl
? m_ctrl
->GetId() : -1);
9459 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9460 cmdEvent
.SetRange(GetRange());
9461 cmdEvent
.SetPosition(GetRange().GetStart());
9462 cmdEvent
.SetContainer(container
);
9464 m_buffer
->SendEvent(cmdEvent
);
9468 case wxRICHTEXT_CHANGE_STYLE
:
9470 ApplyParagraphs(GetNewParagraphs());
9472 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9473 // Layout() would stop prematurely at the top level.
9474 container
->InvalidateHierarchy(GetRange());
9476 UpdateAppearance(GetPosition());
9478 wxRichTextEvent
cmdEvent(
9479 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9480 m_ctrl
? m_ctrl
->GetId() : -1);
9481 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9482 cmdEvent
.SetRange(GetRange());
9483 cmdEvent
.SetPosition(GetRange().GetStart());
9484 cmdEvent
.SetContainer(container
);
9486 m_buffer
->SendEvent(cmdEvent
);
9490 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9492 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9495 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9496 obj
->GetAttributes() = m_attributes
;
9497 m_attributes
= oldAttr
;
9500 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9501 // Layout() would stop prematurely at the top level.
9502 container
->InvalidateHierarchy(GetRange());
9504 UpdateAppearance(GetPosition());
9506 wxRichTextEvent
cmdEvent(
9507 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9508 m_ctrl
? m_ctrl
->GetId() : -1);
9509 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9510 cmdEvent
.SetRange(GetRange());
9511 cmdEvent
.SetPosition(GetRange().GetStart());
9512 cmdEvent
.SetContainer(container
);
9514 m_buffer
->SendEvent(cmdEvent
);
9518 case wxRICHTEXT_CHANGE_OBJECT
:
9520 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9521 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9522 if (obj
&& m_object
)
9524 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9527 wxRichTextObject
* obj
= node
->GetData();
9528 node
->SetData(m_object
);
9533 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9534 // Layout() would stop prematurely at the top level.
9535 container
->InvalidateHierarchy(GetRange());
9537 UpdateAppearance(GetPosition());
9539 // TODO: send new kind of modification event
9550 bool wxRichTextAction::Undo()
9552 m_buffer
->Modify(true);
9554 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9555 wxASSERT(container
!= NULL
);
9561 case wxRICHTEXT_INSERT
:
9563 wxArrayInt optimizationLineCharPositions
;
9564 wxArrayInt optimizationLineYPositions
;
9566 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9567 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9570 container
->DeleteRange(GetRange());
9571 container
->UpdateRanges();
9572 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9573 // Layout() would stop prematurely at the top level.
9574 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9576 long newCaretPosition
= GetPosition() - 1;
9578 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9580 wxRichTextEvent
cmdEvent(
9581 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9582 m_ctrl
? m_ctrl
->GetId() : -1);
9583 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9584 cmdEvent
.SetRange(GetRange());
9585 cmdEvent
.SetPosition(GetRange().GetStart());
9586 cmdEvent
.SetContainer(container
);
9588 m_buffer
->SendEvent(cmdEvent
);
9592 case wxRICHTEXT_DELETE
:
9594 wxArrayInt optimizationLineCharPositions
;
9595 wxArrayInt optimizationLineYPositions
;
9597 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9598 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9601 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9602 container
->UpdateRanges();
9603 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9604 // Layout() would stop prematurely at the top level.
9605 container
->InvalidateHierarchy(GetRange());
9607 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9609 wxRichTextEvent
cmdEvent(
9610 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9611 m_ctrl
? m_ctrl
->GetId() : -1);
9612 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9613 cmdEvent
.SetRange(GetRange());
9614 cmdEvent
.SetPosition(GetRange().GetStart());
9615 cmdEvent
.SetContainer(container
);
9617 m_buffer
->SendEvent(cmdEvent
);
9621 case wxRICHTEXT_CHANGE_STYLE
:
9623 ApplyParagraphs(GetOldParagraphs());
9624 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9625 // Layout() would stop prematurely at the top level.
9626 container
->InvalidateHierarchy(GetRange());
9628 UpdateAppearance(GetPosition());
9630 wxRichTextEvent
cmdEvent(
9631 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9632 m_ctrl
? m_ctrl
->GetId() : -1);
9633 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9634 cmdEvent
.SetRange(GetRange());
9635 cmdEvent
.SetPosition(GetRange().GetStart());
9636 cmdEvent
.SetContainer(container
);
9638 m_buffer
->SendEvent(cmdEvent
);
9642 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9643 case wxRICHTEXT_CHANGE_OBJECT
:
9654 /// Update the control appearance
9655 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9657 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9658 wxASSERT(container
!= NULL
);
9664 m_ctrl
->SetFocusObject(container
);
9665 m_ctrl
->SetCaretPosition(caretPosition
);
9667 if (!m_ctrl
->IsFrozen())
9669 wxRect containerRect
= container
->GetRect();
9671 m_ctrl
->LayoutContent();
9673 // Refresh everything if there were floating objects or the container changed size
9674 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9675 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9677 m_ctrl
->Refresh(false);
9681 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9682 // Find refresh rectangle if we are in a position to optimise refresh
9683 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9687 wxSize clientSize
= m_ctrl
->GetClientSize();
9688 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9690 // Start/end positions
9692 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9694 bool foundEnd
= false;
9696 // position offset - how many characters were inserted
9697 int positionOffset
= GetRange().GetLength();
9699 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9700 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9701 positionOffset
= - positionOffset
;
9703 // find the first line which is being drawn at the same position as it was
9704 // before. Since we're talking about a simple insertion, we can assume
9705 // that the rest of the window does not need to be redrawn.
9707 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9708 // Since we support floating layout, we should redraw the whole para instead of just
9709 // the first line touching the invalid range.
9712 firstY
= para
->GetPosition().y
;
9715 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9718 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9719 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9722 wxRichTextLine
* line
= node2
->GetData();
9723 wxPoint pt
= line
->GetAbsolutePosition();
9724 wxRichTextRange range
= line
->GetAbsoluteRange();
9726 // we want to find the first line that is in the same position
9727 // as before. This will mean we're at the end of the changed text.
9729 if (pt
.y
> lastY
) // going past the end of the window, no more info
9731 node2
= wxRichTextLineList::compatibility_iterator();
9732 node
= wxRichTextObjectList::compatibility_iterator();
9734 // Detect last line in the buffer
9735 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9737 // If deleting text, make sure we refresh below as well as above
9738 if (positionOffset
>= 0)
9741 lastY
= pt
.y
+ line
->GetSize().y
;
9744 node2
= wxRichTextLineList::compatibility_iterator();
9745 node
= wxRichTextObjectList::compatibility_iterator();
9751 // search for this line being at the same position as before
9752 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9754 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9755 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9757 // Stop, we're now the same as we were
9762 node2
= wxRichTextLineList::compatibility_iterator();
9763 node
= wxRichTextObjectList::compatibility_iterator();
9771 node2
= node2
->GetNext();
9775 node
= node
->GetNext();
9778 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9780 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9782 // Convert to device coordinates
9783 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9784 m_ctrl
->RefreshRect(rect
);
9788 m_ctrl
->Refresh(false);
9790 m_ctrl
->PositionCaret();
9792 // This causes styles to persist when doing programmatic
9793 // content creation except when Freeze/Thaw is used, so
9794 // disable this and check for the consequences.
9795 // m_ctrl->SetDefaultStyleToCursorStyle();
9797 if (sendUpdateEvent
)
9798 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9803 /// Replace the buffer paragraphs with the new ones.
9804 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9806 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9807 wxASSERT(container
!= NULL
);
9811 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9814 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9815 wxASSERT (para
!= NULL
);
9817 // We'll replace the existing paragraph by finding the paragraph at this position,
9818 // delete its node data, and setting a copy as the new node data.
9819 // TODO: make more efficient by simply swapping old and new paragraph objects.
9821 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9824 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9827 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9828 newPara
->SetParent(container
);
9830 bufferParaNode
->SetData(newPara
);
9832 delete existingPara
;
9836 node
= node
->GetNext();
9843 * This stores beginning and end positions for a range of data.
9846 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
9848 /// Limit this range to be within 'range'
9849 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
9851 if (m_start
< range
.m_start
)
9852 m_start
= range
.m_start
;
9854 if (m_end
> range
.m_end
)
9855 m_end
= range
.m_end
;
9861 * wxRichTextImage implementation
9862 * This object represents an image.
9865 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
9867 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9868 wxRichTextObject(parent
)
9870 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
9872 SetAttributes(*charStyle
);
9875 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9876 wxRichTextObject(parent
)
9878 m_imageBlock
= imageBlock
;
9880 SetAttributes(*charStyle
);
9883 /// Create a cached image at the required size
9884 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
9886 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9888 if (!m_imageBlock
.IsOk())
9892 m_imageBlock
.Load(image
);
9896 int width
= image
.GetWidth();
9897 int height
= image
.GetHeight();
9899 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9901 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9902 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9904 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9906 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9908 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9909 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9911 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9914 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
9915 m_imageCache
= wxBitmap(image
);
9918 // If the original width and height is small, e.g. 400 or below,
9919 // scale up and then down to improve image quality. This can make
9920 // a big difference, with not much performance hit.
9921 int upscaleThreshold
= 400;
9923 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
9925 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
9926 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9929 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9930 m_imageCache
= wxBitmap(img
);
9934 return m_imageCache
.IsOk();
9938 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
9943 // Don't need cached size AFAIK
9944 // wxSize size = GetCachedSize();
9945 if (!LoadImageCache(dc
))
9948 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(rect
.GetPosition(), GetCachedSize()));
9951 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
9953 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
9956 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9957 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9958 marginRect
= rect
; // outer rectangle, will calculate contentRect
9959 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9961 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
9963 if (selection
.WithinSelection(range
.GetStart(), this))
9965 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9966 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9967 dc
.SetLogicalFunction(wxINVERT
);
9968 dc
.DrawRectangle(contentRect
);
9969 dc
.SetLogicalFunction(wxCOPY
);
9975 /// Lay the item out
9976 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
9978 if (!LoadImageCache(dc
))
9981 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9982 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9983 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9984 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9986 wxSize overallSize
= marginRect
.GetSize();
9988 SetCachedSize(overallSize
);
9989 SetMaxSize(overallSize
);
9990 SetMinSize(overallSize
);
9991 SetPosition(rect
.GetPosition());
9996 /// Get/set the object size for the given range. Returns false if the range
9997 /// is invalid for this object.
9998 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10000 if (!range
.IsWithin(GetRange()))
10003 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10005 size
.x
= 0; size
.y
= 0;
10006 if (partialExtents
)
10007 partialExtents
->Add(0);
10011 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10012 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10013 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10014 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10016 wxSize overallSize
= marginRect
.GetSize();
10018 if (partialExtents
)
10019 partialExtents
->Add(overallSize
.x
);
10021 size
= overallSize
;
10026 // Get the 'natural' size for an object. For an image, it would be the
10028 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10030 wxTextAttrSize size
;
10031 if (GetImageCache().IsOk())
10033 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10034 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10041 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10043 wxRichTextObject::Copy(obj
);
10045 m_imageBlock
= obj
.m_imageBlock
;
10048 /// Edit properties via a GUI
10049 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10051 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10052 imageDlg
.SetAttributes(GetAttributes());
10054 if (imageDlg
.ShowModal() == wxID_OK
)
10056 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10057 // indeterminate in the object.
10058 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10070 /// Compare two attribute objects
10071 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10073 return (attr1
== attr2
);
10076 // Partial equality test taking flags into account
10077 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10079 return attr1
.EqPartial(attr2
);
10083 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10085 if (tabs1
.GetCount() != tabs2
.GetCount())
10089 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10091 if (tabs1
[i
] != tabs2
[i
])
10097 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10099 return destStyle
.Apply(style
, compareWith
);
10102 // Remove attributes
10103 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10105 return destStyle
.RemoveStyle(style
);
10108 /// Combine two bitlists, specifying the bits of interest with separate flags.
10109 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10111 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10114 /// Compare two bitlists
10115 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10117 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10120 /// Split into paragraph and character styles
10121 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10123 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10126 /// Convert a decimal to Roman numerals
10127 wxString
wxRichTextDecimalToRoman(long n
)
10129 static wxArrayInt decimalNumbers
;
10130 static wxArrayString romanNumbers
;
10135 decimalNumbers
.Clear();
10136 romanNumbers
.Clear();
10137 return wxEmptyString
;
10140 if (decimalNumbers
.GetCount() == 0)
10142 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10144 wxRichTextAddDecRom(1000, wxT("M"));
10145 wxRichTextAddDecRom(900, wxT("CM"));
10146 wxRichTextAddDecRom(500, wxT("D"));
10147 wxRichTextAddDecRom(400, wxT("CD"));
10148 wxRichTextAddDecRom(100, wxT("C"));
10149 wxRichTextAddDecRom(90, wxT("XC"));
10150 wxRichTextAddDecRom(50, wxT("L"));
10151 wxRichTextAddDecRom(40, wxT("XL"));
10152 wxRichTextAddDecRom(10, wxT("X"));
10153 wxRichTextAddDecRom(9, wxT("IX"));
10154 wxRichTextAddDecRom(5, wxT("V"));
10155 wxRichTextAddDecRom(4, wxT("IV"));
10156 wxRichTextAddDecRom(1, wxT("I"));
10162 while (n
> 0 && i
< 13)
10164 if (n
>= decimalNumbers
[i
])
10166 n
-= decimalNumbers
[i
];
10167 roman
+= romanNumbers
[i
];
10174 if (roman
.IsEmpty())
10180 * wxRichTextFileHandler
10181 * Base class for file handlers
10184 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10186 #if wxUSE_FFILE && wxUSE_STREAMS
10187 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10189 wxFFileInputStream
stream(filename
);
10191 return LoadFile(buffer
, stream
);
10196 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10198 wxFFileOutputStream
stream(filename
);
10200 return SaveFile(buffer
, stream
);
10204 #endif // wxUSE_FFILE && wxUSE_STREAMS
10206 /// Can we handle this filename (if using files)? By default, checks the extension.
10207 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10209 wxString path
, file
, ext
;
10210 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10212 return (ext
.Lower() == GetExtension());
10216 * wxRichTextTextHandler
10217 * Plain text handler
10220 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10223 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10225 if (!stream
.IsOk())
10231 while (!stream
.Eof())
10233 int ch
= stream
.GetC();
10237 if (ch
== 10 && lastCh
!= 13)
10240 if (ch
> 0 && ch
!= 10)
10247 buffer
->ResetAndClearCommands();
10249 buffer
->AddParagraphs(str
);
10250 buffer
->UpdateRanges();
10255 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10257 if (!stream
.IsOk())
10260 wxString text
= buffer
->GetText();
10262 wxString newLine
= wxRichTextLineBreakChar
;
10263 text
.Replace(newLine
, wxT("\n"));
10265 wxCharBuffer buf
= text
.ToAscii();
10267 stream
.Write((const char*) buf
, text
.length());
10270 #endif // wxUSE_STREAMS
10273 * Stores information about an image, in binary in-memory form
10276 wxRichTextImageBlock::wxRichTextImageBlock()
10281 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10287 wxRichTextImageBlock::~wxRichTextImageBlock()
10292 void wxRichTextImageBlock::Init()
10296 m_imageType
= wxBITMAP_TYPE_INVALID
;
10299 void wxRichTextImageBlock::Clear()
10303 m_imageType
= wxBITMAP_TYPE_INVALID
;
10307 // Load the original image into a memory block.
10308 // If the image is not a JPEG, we must convert it into a JPEG
10309 // to conserve space.
10310 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10311 // load the image a 2nd time.
10313 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10314 wxImage
& image
, bool convertToJPEG
)
10316 m_imageType
= imageType
;
10318 wxString
filenameToRead(filename
);
10319 bool removeFile
= false;
10321 if (imageType
== wxBITMAP_TYPE_INVALID
)
10322 return false; // Could not determine image type
10324 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10326 wxString tempFile
=
10327 wxFileName::CreateTempFileName(_("image"));
10329 wxASSERT(!tempFile
.IsEmpty());
10331 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10332 filenameToRead
= tempFile
;
10335 m_imageType
= wxBITMAP_TYPE_JPEG
;
10338 if (!file
.Open(filenameToRead
))
10341 m_dataSize
= (size_t) file
.Length();
10346 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10349 wxRemoveFile(filenameToRead
);
10351 return (m_data
!= NULL
);
10354 // Make an image block from the wxImage in the given
10356 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10358 image
.SetOption(wxT("quality"), quality
);
10360 if (imageType
== wxBITMAP_TYPE_INVALID
)
10361 return false; // Could not determine image type
10363 return DoMakeImageBlock(image
, imageType
);
10366 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10367 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10369 if (imageType
== wxBITMAP_TYPE_INVALID
)
10370 return false; // Could not determine image type
10372 return DoMakeImageBlock(image
, imageType
);
10375 // Makes the image block
10376 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10378 wxMemoryOutputStream memStream
;
10379 if (!image
.SaveFile(memStream
, imageType
))
10384 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10392 m_imageType
= imageType
;
10393 m_dataSize
= memStream
.GetSize();
10395 memStream
.CopyTo(m_data
, m_dataSize
);
10397 return (m_data
!= NULL
);
10401 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10403 return WriteBlock(filename
, m_data
, m_dataSize
);
10406 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10408 m_imageType
= block
.m_imageType
;
10410 m_dataSize
= block
.m_dataSize
;
10411 if (m_dataSize
== 0)
10414 m_data
= new unsigned char[m_dataSize
];
10416 for (i
= 0; i
< m_dataSize
; i
++)
10417 m_data
[i
] = block
.m_data
[i
];
10421 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10426 // Load a wxImage from the block
10427 bool wxRichTextImageBlock::Load(wxImage
& image
)
10432 // Read in the image.
10434 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10435 bool success
= image
.LoadFile(mstream
, GetImageType());
10437 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10438 wxASSERT(!tempFile
.IsEmpty());
10440 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10444 success
= image
.LoadFile(tempFile
, GetImageType());
10445 wxRemoveFile(tempFile
);
10451 // Write data in hex to a stream
10452 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10454 if (m_dataSize
== 0)
10457 int bufSize
= 100000;
10458 if (int(2*m_dataSize
) < bufSize
)
10459 bufSize
= 2*m_dataSize
;
10460 char* buf
= new char[bufSize
+1];
10462 int left
= m_dataSize
;
10467 if (left
*2 > bufSize
)
10469 n
= bufSize
; left
-= (bufSize
/2);
10473 n
= left
*2; left
= 0;
10477 for (i
= 0; i
< (n
/2); i
++)
10479 wxDecToHex(m_data
[j
], b
, b
+1);
10484 stream
.Write((const char*) buf
, n
);
10490 // Read data in hex from a stream
10491 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10493 int dataSize
= length
/2;
10498 // create a null terminated temporary string:
10502 m_data
= new unsigned char[dataSize
];
10504 for (i
= 0; i
< dataSize
; i
++)
10506 str
[0] = (char)stream
.GetC();
10507 str
[1] = (char)stream
.GetC();
10509 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10512 m_dataSize
= dataSize
;
10513 m_imageType
= imageType
;
10518 // Allocate and read from stream as a block of memory
10519 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10521 unsigned char* block
= new unsigned char[size
];
10525 stream
.Read(block
, size
);
10530 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10532 wxFileInputStream
stream(filename
);
10533 if (!stream
.IsOk())
10536 return ReadBlock(stream
, size
);
10539 // Write memory block to stream
10540 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10542 stream
.Write((void*) block
, size
);
10543 return stream
.IsOk();
10547 // Write memory block to file
10548 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10550 wxFileOutputStream
outStream(filename
);
10551 if (!outStream
.IsOk())
10554 return WriteBlock(outStream
, block
, size
);
10557 // Gets the extension for the block's type
10558 wxString
wxRichTextImageBlock::GetExtension() const
10560 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10562 return handler
->GetExtension();
10564 return wxEmptyString
;
10570 * The data object for a wxRichTextBuffer
10573 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10575 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10577 m_richTextBuffer
= richTextBuffer
;
10579 // this string should uniquely identify our format, but is otherwise
10581 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10583 SetFormat(m_formatRichTextBuffer
);
10586 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10588 delete m_richTextBuffer
;
10591 // after a call to this function, the richTextBuffer is owned by the caller and it
10592 // is responsible for deleting it!
10593 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10595 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10596 m_richTextBuffer
= NULL
;
10598 return richTextBuffer
;
10601 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10603 return m_formatRichTextBuffer
;
10606 size_t wxRichTextBufferDataObject::GetDataSize() const
10608 if (!m_richTextBuffer
)
10614 wxStringOutputStream
stream(& bufXML
);
10615 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10617 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10623 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10624 return strlen(buffer
) + 1;
10626 return bufXML
.Length()+1;
10630 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10632 if (!pBuf
|| !m_richTextBuffer
)
10638 wxStringOutputStream
stream(& bufXML
);
10639 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10641 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10647 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10648 size_t len
= strlen(buffer
);
10649 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10650 ((char*) pBuf
)[len
] = 0;
10652 size_t len
= bufXML
.Length();
10653 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10654 ((char*) pBuf
)[len
] = 0;
10660 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10662 wxDELETE(m_richTextBuffer
);
10664 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10666 m_richTextBuffer
= new wxRichTextBuffer
;
10668 wxStringInputStream
stream(bufXML
);
10669 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10671 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10673 wxDELETE(m_richTextBuffer
);
10685 * wxRichTextFontTable
10686 * Manages quick access to a pool of fonts for rendering rich text
10689 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10691 class wxRichTextFontTableData
: public wxObjectRefData
10694 wxRichTextFontTableData() {}
10696 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10698 wxRichTextFontTableHashMap m_hashMap
;
10701 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10703 wxString
facename(fontSpec
.GetFontFaceName());
10704 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()));
10705 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10707 if ( entry
== m_hashMap
.end() )
10709 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10710 m_hashMap
[spec
] = font
;
10715 return entry
->second
;
10719 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10721 wxRichTextFontTable::wxRichTextFontTable()
10723 m_refData
= new wxRichTextFontTableData
;
10726 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10732 wxRichTextFontTable::~wxRichTextFontTable()
10737 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10739 return (m_refData
== table
.m_refData
);
10742 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10747 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10749 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10751 return data
->FindFont(fontSpec
);
10756 void wxRichTextFontTable::Clear()
10758 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10760 data
->m_hashMap
.clear();
10766 void wxTextBoxAttr::Reset()
10769 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10770 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10771 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10772 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10773 m_boxStyleName
= wxEmptyString
;
10777 m_position
.Reset();
10786 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10789 m_flags
== attr
.m_flags
&&
10790 m_floatMode
== attr
.m_floatMode
&&
10791 m_clearMode
== attr
.m_clearMode
&&
10792 m_collapseMode
== attr
.m_collapseMode
&&
10793 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10795 m_margins
== attr
.m_margins
&&
10796 m_padding
== attr
.m_padding
&&
10797 m_position
== attr
.m_position
&&
10799 m_size
== attr
.m_size
&&
10801 m_border
== attr
.m_border
&&
10802 m_outline
== attr
.m_outline
&&
10804 m_boxStyleName
== attr
.m_boxStyleName
10808 // Partial equality test
10809 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10811 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10814 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10817 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10820 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10823 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
10828 if (!m_position
.EqPartial(attr
.m_position
))
10833 if (!m_margins
.EqPartial(attr
.m_margins
))
10838 if (!m_padding
.EqPartial(attr
.m_padding
))
10843 if (!GetBorder().EqPartial(attr
.GetBorder()))
10848 if (!GetOutline().EqPartial(attr
.GetOutline()))
10854 // Merges the given attributes. If compareWith
10855 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10856 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10857 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
10859 if (attr
.HasFloatMode())
10861 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
10862 SetFloatMode(attr
.GetFloatMode());
10865 if (attr
.HasClearMode())
10867 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
10868 SetClearMode(attr
.GetClearMode());
10871 if (attr
.HasCollapseBorders())
10873 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
10874 SetCollapseBorders(attr
.GetCollapseBorders());
10877 if (attr
.HasVerticalAlignment())
10879 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
10880 SetVerticalAlignment(attr
.GetVerticalAlignment());
10883 if (attr
.HasBoxStyleName())
10885 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
10886 SetBoxStyleName(attr
.GetBoxStyleName());
10889 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
10890 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
10891 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
10893 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
10895 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
10896 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
10901 // Remove specified attributes from this object
10902 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
10904 if (attr
.HasFloatMode())
10905 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10907 if (attr
.HasClearMode())
10908 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10910 if (attr
.HasCollapseBorders())
10911 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10913 if (attr
.HasVerticalAlignment())
10914 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10916 if (attr
.HasBoxStyleName())
10918 SetBoxStyleName(wxEmptyString
);
10919 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
10922 m_margins
.RemoveStyle(attr
.m_margins
);
10923 m_padding
.RemoveStyle(attr
.m_padding
);
10924 m_position
.RemoveStyle(attr
.m_position
);
10926 m_size
.RemoveStyle(attr
.m_size
);
10928 m_border
.RemoveStyle(attr
.m_border
);
10929 m_outline
.RemoveStyle(attr
.m_outline
);
10934 // Collects the attributes that are common to a range of content, building up a note of
10935 // which attributes are absent in some objects and which clash in some objects.
10936 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
10938 if (attr
.HasFloatMode())
10940 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
10942 if (HasFloatMode())
10944 if (GetFloatMode() != attr
.GetFloatMode())
10946 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10947 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10951 SetFloatMode(attr
.GetFloatMode());
10955 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10957 if (attr
.HasClearMode())
10959 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
10961 if (HasClearMode())
10963 if (GetClearMode() != attr
.GetClearMode())
10965 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10966 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10970 SetClearMode(attr
.GetClearMode());
10974 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10976 if (attr
.HasCollapseBorders())
10978 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
10980 if (HasCollapseBorders())
10982 if (GetCollapseBorders() != attr
.GetCollapseBorders())
10984 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10985 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10989 SetCollapseBorders(attr
.GetCollapseBorders());
10993 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10995 if (attr
.HasVerticalAlignment())
10997 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
10999 if (HasVerticalAlignment())
11001 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11003 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11004 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11008 SetVerticalAlignment(attr
.GetVerticalAlignment());
11012 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11014 if (attr
.HasBoxStyleName())
11016 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11018 if (HasBoxStyleName())
11020 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11022 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11023 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11027 SetBoxStyleName(attr
.GetBoxStyleName());
11031 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11033 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11034 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11035 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11037 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11039 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11040 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11043 bool wxTextBoxAttr::IsDefault() const
11045 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11046 !m_size
.GetWidth().IsValid() && !m_size
.GetHeight().IsValid() &&
11047 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11052 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11054 wxTextAttr::Copy(attr
);
11056 m_textBoxAttr
= attr
.m_textBoxAttr
;
11059 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11061 if (!(wxTextAttr::operator==(attr
)))
11064 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11067 // Partial equality test taking comparison object into account
11068 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11070 if (!(wxTextAttr::EqPartial(attr
)))
11073 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11076 // Merges the given attributes. If compareWith
11077 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11078 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11079 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11081 wxTextAttr::Apply(style
, compareWith
);
11083 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11086 // Remove specified attributes from this object
11087 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11089 wxTextAttr::RemoveStyle(*this, attr
);
11091 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11094 // Collects the attributes that are common to a range of content, building up a note of
11095 // which attributes are absent in some objects and which clash in some objects.
11096 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11098 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11100 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11103 // Partial equality test
11104 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11106 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11109 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11112 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11118 // Apply border to 'this', but not if the same as compareWith
11119 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11121 if (border
.HasStyle())
11123 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11124 SetStyle(border
.GetStyle());
11126 if (border
.HasColour())
11128 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11129 SetColour(border
.GetColourLong());
11131 if (border
.HasWidth())
11133 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11134 SetWidth(border
.GetWidth());
11140 // Remove specified attributes from this object
11141 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11143 if (attr
.HasStyle() && HasStyle())
11144 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11145 if (attr
.HasColour() && HasColour())
11146 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11147 if (attr
.HasWidth() && HasWidth())
11148 m_borderWidth
.Reset();
11153 // Collects the attributes that are common to a range of content, building up a note of
11154 // which attributes are absent in some objects and which clash in some objects.
11155 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11157 if (attr
.HasStyle())
11159 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11163 if (GetStyle() != attr
.GetStyle())
11165 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11166 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11170 SetStyle(attr
.GetStyle());
11174 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11176 if (attr
.HasColour())
11178 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11182 if (GetColour() != attr
.GetColour())
11184 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11185 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11189 SetColour(attr
.GetColourLong());
11193 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11195 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11198 // Partial equality test
11199 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11201 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11202 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11205 // Apply border to 'this', but not if the same as compareWith
11206 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11208 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11209 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11210 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11211 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11215 // Remove specified attributes from this object
11216 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11218 m_left
.RemoveStyle(attr
.m_left
);
11219 m_right
.RemoveStyle(attr
.m_right
);
11220 m_top
.RemoveStyle(attr
.m_top
);
11221 m_bottom
.RemoveStyle(attr
.m_bottom
);
11225 // Collects the attributes that are common to a range of content, building up a note of
11226 // which attributes are absent in some objects and which clash in some objects.
11227 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11229 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11230 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11231 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11232 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11235 // Set style of all borders
11236 void wxTextAttrBorders::SetStyle(int style
)
11238 m_left
.SetStyle(style
);
11239 m_right
.SetStyle(style
);
11240 m_top
.SetStyle(style
);
11241 m_bottom
.SetStyle(style
);
11244 // Set colour of all borders
11245 void wxTextAttrBorders::SetColour(unsigned long colour
)
11247 m_left
.SetColour(colour
);
11248 m_right
.SetColour(colour
);
11249 m_top
.SetColour(colour
);
11250 m_bottom
.SetColour(colour
);
11253 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11255 m_left
.SetColour(colour
);
11256 m_right
.SetColour(colour
);
11257 m_top
.SetColour(colour
);
11258 m_bottom
.SetColour(colour
);
11261 // Set width of all borders
11262 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11264 m_left
.SetWidth(width
);
11265 m_right
.SetWidth(width
);
11266 m_top
.SetWidth(width
);
11267 m_bottom
.SetWidth(width
);
11270 // Partial equality test
11271 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11273 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11279 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11283 if (!(compareWith
&& dim
== (*compareWith
)))
11290 // Collects the attributes that are common to a range of content, building up a note of
11291 // which attributes are absent in some objects and which clash in some objects.
11292 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11294 if (attr
.IsValid())
11296 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11300 if (!((*this) == attr
))
11302 clashingAttr
.SetValid(true);
11311 absentAttr
.SetValid(true);
11314 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11316 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11319 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11321 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11324 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11326 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11329 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11331 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11334 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11336 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11337 return ConvertTenthsMMToPixels(dim
.GetValue());
11338 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11339 return dim
.GetValue();
11340 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11342 wxASSERT(m_parentSize
!= wxDefaultSize
);
11343 if (direction
== wxHORIZONTAL
)
11344 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11346 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11355 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11357 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11358 return dim
.GetValue();
11359 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11360 return ConvertPixelsToTenthsMM(dim
.GetValue());
11368 // Partial equality test
11369 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11371 if (!m_left
.EqPartial(dims
.m_left
))
11374 if (!m_right
.EqPartial(dims
.m_right
))
11377 if (!m_top
.EqPartial(dims
.m_top
))
11380 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11386 // Apply border to 'this', but not if the same as compareWith
11387 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11389 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11390 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11391 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11392 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11397 // Remove specified attributes from this object
11398 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11400 if (attr
.m_left
.IsValid())
11402 if (attr
.m_right
.IsValid())
11404 if (attr
.m_top
.IsValid())
11406 if (attr
.m_bottom
.IsValid())
11412 // Collects the attributes that are common to a range of content, building up a note of
11413 // which attributes are absent in some objects and which clash in some objects.
11414 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11416 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11417 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11418 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11419 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11422 // Partial equality test
11423 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11425 if (!m_width
.EqPartial(size
.m_width
))
11428 if (!m_height
.EqPartial(size
.m_height
))
11434 // Apply border to 'this', but not if the same as compareWith
11435 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11437 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11438 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11443 // Remove specified attributes from this object
11444 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11446 if (attr
.m_width
.IsValid())
11448 if (attr
.m_height
.IsValid())
11454 // Collects the attributes that are common to a range of content, building up a note of
11455 // which attributes are absent in some objects and which clash in some objects.
11456 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11458 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11459 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11462 // Collects the attributes that are common to a range of content, building up a note of
11463 // which attributes are absent in some objects and which clash in some objects.
11464 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11466 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11467 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11469 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11471 if (attr
.HasFont())
11473 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11475 if (currentStyle
.HasFontSize())
11477 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11479 // Clash of attr - mark as such
11480 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11481 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11485 currentStyle
.SetFontSize(attr
.GetFontSize());
11488 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11490 if (currentStyle
.HasFontItalic())
11492 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11494 // Clash of attr - mark as such
11495 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11496 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11500 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11503 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11505 if (currentStyle
.HasFontFamily())
11507 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11509 // Clash of attr - mark as such
11510 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11511 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11515 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11518 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11520 if (currentStyle
.HasFontWeight())
11522 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11524 // Clash of attr - mark as such
11525 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11526 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11530 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11533 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11535 if (currentStyle
.HasFontFaceName())
11537 wxString
faceName1(currentStyle
.GetFontFaceName());
11538 wxString
faceName2(attr
.GetFontFaceName());
11540 if (faceName1
!= faceName2
)
11542 // Clash of attr - mark as such
11543 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11544 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11548 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11551 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11553 if (currentStyle
.HasFontUnderlined())
11555 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11557 // Clash of attr - mark as such
11558 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11559 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11563 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11567 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11569 if (currentStyle
.HasTextColour())
11571 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11573 // Clash of attr - mark as such
11574 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11575 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11579 currentStyle
.SetTextColour(attr
.GetTextColour());
11582 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11584 if (currentStyle
.HasBackgroundColour())
11586 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11588 // Clash of attr - mark as such
11589 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11590 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11594 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11597 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11599 if (currentStyle
.HasAlignment())
11601 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11603 // Clash of attr - mark as such
11604 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11605 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11609 currentStyle
.SetAlignment(attr
.GetAlignment());
11612 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11614 if (currentStyle
.HasTabs())
11616 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11618 // Clash of attr - mark as such
11619 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11620 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11624 currentStyle
.SetTabs(attr
.GetTabs());
11627 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11629 if (currentStyle
.HasLeftIndent())
11631 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11633 // Clash of attr - mark as such
11634 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11635 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11639 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11642 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11644 if (currentStyle
.HasRightIndent())
11646 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11648 // Clash of attr - mark as such
11649 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11650 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11654 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11657 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11659 if (currentStyle
.HasParagraphSpacingAfter())
11661 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11663 // Clash of attr - mark as such
11664 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11665 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11669 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11672 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11674 if (currentStyle
.HasParagraphSpacingBefore())
11676 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11678 // Clash of attr - mark as such
11679 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11680 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11684 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11687 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11689 if (currentStyle
.HasLineSpacing())
11691 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11693 // Clash of attr - mark as such
11694 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11695 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11699 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11702 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11704 if (currentStyle
.HasCharacterStyleName())
11706 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11708 // Clash of attr - mark as such
11709 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11710 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11714 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11717 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11719 if (currentStyle
.HasParagraphStyleName())
11721 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11723 // Clash of attr - mark as such
11724 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11725 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11729 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11732 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11734 if (currentStyle
.HasListStyleName())
11736 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11738 // Clash of attr - mark as such
11739 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11740 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11744 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11747 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11749 if (currentStyle
.HasBulletStyle())
11751 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11753 // Clash of attr - mark as such
11754 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11755 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11759 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11762 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11764 if (currentStyle
.HasBulletNumber())
11766 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11768 // Clash of attr - mark as such
11769 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11770 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11774 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11777 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11779 if (currentStyle
.HasBulletText())
11781 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11783 // Clash of attr - mark as such
11784 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11785 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11790 currentStyle
.SetBulletText(attr
.GetBulletText());
11791 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11795 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11797 if (currentStyle
.HasBulletName())
11799 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11801 // Clash of attr - mark as such
11802 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11803 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11808 currentStyle
.SetBulletName(attr
.GetBulletName());
11812 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11814 if (currentStyle
.HasURL())
11816 if (currentStyle
.GetURL() != attr
.GetURL())
11818 // Clash of attr - mark as such
11819 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11820 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
11825 currentStyle
.SetURL(attr
.GetURL());
11829 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
11831 if (currentStyle
.HasTextEffects())
11833 // We need to find the bits in the new attr that are different:
11834 // just look at those bits that are specified by the new attr.
11836 // We need to remove the bits and flags that are not common between current attr
11837 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11838 // previous styles.
11840 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
11841 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
11843 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
11845 // Find the text effects that were different, using XOR
11846 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
11848 // Clash of attr - mark as such
11849 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
11850 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
11855 currentStyle
.SetTextEffects(attr
.GetTextEffects());
11856 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
11859 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11860 // that we've looked at so far
11861 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
11862 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
11864 if (currentStyle
.GetTextEffectFlags() == 0)
11865 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
11868 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
11870 if (currentStyle
.HasOutlineLevel())
11872 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
11874 // Clash of attr - mark as such
11875 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11876 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11880 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
11884 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
11886 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
11888 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
11890 if (m_properties
.GetCount() != props
.GetCount())
11894 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11896 const wxVariant
& var1
= m_properties
[i
];
11897 int idx
= props
.Find(var1
.GetName());
11900 const wxVariant
& var2
= props
.m_properties
[idx
];
11901 if (!(var1
== var2
))
11908 wxArrayString
wxRichTextProperties::GetPropertyNames() const
11912 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11914 arr
.Add(m_properties
[i
].GetName());
11919 int wxRichTextProperties::Find(const wxString
& name
) const
11922 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11924 if (m_properties
[i
].GetName() == name
)
11930 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
11932 int idx
= Find(name
);
11933 if (idx
== wxNOT_FOUND
)
11934 SetProperty(name
, wxString());
11936 if (idx
!= wxNOT_FOUND
)
11938 return & (*this)[idx
];
11944 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
11946 static const wxVariant nullVariant
;
11947 int idx
= Find(name
);
11949 return m_properties
[idx
];
11951 return nullVariant
;
11954 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
11956 return GetProperty(name
).GetString();
11959 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
11961 return GetProperty(name
).GetLong();
11964 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
11966 return GetProperty(name
).GetBool();
11969 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
11971 return GetProperty(name
).GetDouble();
11974 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
11976 wxASSERT(!variant
.GetName().IsEmpty());
11978 int idx
= Find(variant
.GetName());
11981 m_properties
.Add(variant
);
11983 m_properties
[idx
] = variant
;
11986 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
11988 int idx
= Find(name
);
11989 wxVariant
var(variant
);
11993 m_properties
.Add(var
);
11995 m_properties
[idx
] = var
;
11998 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12000 SetProperty(name
, wxVariant(value
, name
));
12003 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12005 SetProperty(name
, wxVariant(value
, name
));
12008 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12010 SetProperty(name
, wxVariant(value
, name
));
12013 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12015 SetProperty(name
, wxVariant(value
, name
));
12018 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12020 if (m_address
.GetCount() == 0)
12021 return topLevelContainer
;
12023 wxRichTextCompositeObject
* p
= topLevelContainer
;
12025 while (p
&& i
< m_address
.GetCount())
12027 int pos
= m_address
[i
];
12028 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12029 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12032 wxRichTextObject
* p1
= p
->GetChild(pos
);
12033 if (i
== (m_address
.GetCount()-1))
12036 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12042 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12046 if (topLevelContainer
== obj
)
12049 wxRichTextObject
* o
= obj
;
12052 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12056 int pos
= p
->GetChildren().IndexOf(o
);
12060 m_address
.Insert(pos
, 0);
12062 if (p
== topLevelContainer
)
12071 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12073 if (m_container
!= sel
.m_container
)
12075 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12078 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12079 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12084 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12085 // or none at the level of the object's container.
12086 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12090 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12092 if (container
== m_container
)
12095 container
= obj
->GetContainer();
12098 if (container
->GetParent())
12100 // If we found that our object's container is within the range of
12101 // a selection higher up, then assume the whole original object
12102 // is also selected.
12103 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12104 if (parentContainer
== m_container
)
12106 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12108 wxRichTextRangeArray ranges
;
12109 ranges
.Add(obj
->GetRange());
12114 container
= parentContainer
;
12123 return wxRichTextRangeArray();
12126 // Is the given position within the selection?
12127 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12133 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12134 return WithinSelection(pos
, selectionRanges
);
12138 // Is the given position within the selection range?
12139 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12142 for (i
= 0; i
< ranges
.GetCount(); i
++)
12144 const wxRichTextRange
& range
= ranges
[i
];
12145 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12151 // Is the given range completely within the selection range?
12152 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12155 for (i
= 0; i
< ranges
.GetCount(); i
++)
12157 const wxRichTextRange
& eachRange
= ranges
[i
];
12158 if (range
.IsWithin(eachRange
))