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;
119 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
121 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
123 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
125 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
127 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
130 wxRichTextFloatRectMapArray m_left
;
131 wxRichTextFloatRectMapArray m_right
;
133 wxRect m_availableRect
;
134 wxRichTextParagraph
* m_para
;
137 // Get floating objects
138 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
141 for (i
= 0; i
< m_left
.GetCount(); i
++)
142 objects
.Append(m_left
[i
]->anchor
);
143 for (i
= 0; i
< m_right
.GetCount(); i
++)
144 objects
.Append(m_right
[i
]->anchor
);
150 * Binary search helper function
151 * The argument point is the Y coordinate, and this fuction
152 * always return the floating rect that contain this coordinate
153 * or under this coordinate.
155 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
157 int end
= array
.GetCount() - 1;
170 int mid
= (start
+ end
) / 2;
171 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
173 else if (array
[mid
]->startY
> point
)
178 else if (array
[mid
]->endY
< point
)
188 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
191 int len
= array
.GetCount();
193 wxASSERT(index
>= 0 && index
< len
);
195 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
196 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
197 while (index
< len
&& array
[index
]->startY
<= endY
)
199 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
206 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
208 m_availableRect
= rect
;
212 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
214 int len
= array
.GetCount();
215 for (int i
= 0; i
< len
; i
++)
219 wxRichTextFloatCollector::~wxRichTextFloatCollector()
221 FreeFloatRectMapArray(m_left
);
222 FreeFloatRectMapArray(m_right
);
225 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
227 if (array
.GetCount() == 0)
230 int i
= SearchAdjacentRect(array
, start
);
232 while (i
< (int) array
.GetCount())
234 if (array
[i
]->startY
- last
>= height
)
236 last
= array
[i
]->endY
;
243 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
245 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
246 return GetFitPosition(m_left
, start
, height
);
247 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
248 return GetFitPosition(m_right
, start
, height
);
251 wxASSERT("Never should be here");
256 // Adds a floating image to the float collector.
257 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
258 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
260 int direction
= floating
->GetFloatDirection();
262 wxPoint pos
= floating
->GetPosition();
263 wxSize size
= floating
->GetCachedSize();
264 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
267 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
270 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
271 // Just a not-enough simple assertion
272 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
275 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
276 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
281 wxASSERT("Unrecognised float attribute.");
287 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
289 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
292 wxRichTextObject
* floating
= node
->GetData();
294 if (floating
->IsFloating())
296 CollectFloat(para
, floating
);
299 node
= node
->GetNext();
305 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
310 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
312 int widthLeft
= 0, widthRight
= 0;
313 if (m_left
.GetCount() != 0)
315 int i
= SearchAdjacentRect(m_left
, startY
);
316 if (i
< (int) m_left
.GetCount())
317 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
319 if (m_right
.GetCount() != 0)
321 int j
= SearchAdjacentRect(m_right
, startY
);
322 if (j
< (int) m_right
.GetCount())
323 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
326 // TODO: actually we want to use the actual image positions to find the
327 // available remaining space, since the image might not be right up against
328 // the left or right edge of the container.
329 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
332 int wxRichTextFloatCollector::GetLastRectBottom()
335 int len
= m_left
.GetCount();
337 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
339 len
= m_right
.GetCount();
341 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
347 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
350 int end
= rect
.y
+ rect
.height
;
352 i
= SearchAdjacentRect(array
, start
);
353 if (i
< 0 || i
>= (int) array
.GetCount())
355 j
= SearchAdjacentRect(array
, end
);
356 if (j
< 0 || j
>= (int) array
.GetCount())
357 j
= array
.GetCount() - 1;
360 wxRichTextObject
* obj
= array
[i
]->anchor
;
361 wxRichTextRange r
= obj
->GetRange();
362 obj
->Draw(dc
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
367 void wxRichTextFloatCollector::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
369 if (m_left
.GetCount() > 0)
370 DrawFloat(m_left
, dc
, range
, selection
, rect
, descent
, style
);
371 if (m_right
.GetCount() > 0)
372 DrawFloat(m_right
, dc
, range
, selection
, rect
, descent
, style
);
375 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
378 if (array
.GetCount() == 0)
379 return wxRICHTEXT_HITTEST_NONE
;
380 i
= SearchAdjacentRect(array
, pt
.y
);
381 if (i
< 0 || i
>= (int) array
.GetCount())
382 return wxRICHTEXT_HITTEST_NONE
;
383 if (!array
[i
]->anchor
->IsShown())
384 return wxRICHTEXT_HITTEST_NONE
;
386 wxPoint point
= array
[i
]->anchor
->GetPosition();
387 wxSize size
= array
[i
]->anchor
->GetCachedSize();
388 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
389 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
391 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
392 * obj
= array
[i
]->anchor
;
393 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
394 return wxRICHTEXT_HITTEST_BEFORE
;
396 return wxRICHTEXT_HITTEST_AFTER
;
399 return wxRICHTEXT_HITTEST_NONE
;
402 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
404 int ret
= HitTestFloat(m_left
, dc
, pt
, textPosition
, obj
, flags
);
405 if (ret
== wxRICHTEXT_HITTEST_NONE
)
407 ret
= HitTestFloat(m_right
, dc
, pt
, textPosition
, obj
, flags
);
412 // Helpers for efficiency
413 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
415 // JACS: did I do this some time ago when testing? Should we re-enable it?
417 const wxFont
& font1
= dc
.GetFont();
418 if (font1
.IsOk() && font
.IsOk())
420 if (font1
.GetPointSize() == font
.GetPointSize() &&
421 font1
.GetFamily() == font
.GetFamily() &&
422 font1
.GetStyle() == font
.GetStyle() &&
423 font1
.GetWeight() == font
.GetWeight() &&
424 font1
.GetUnderlined() == font
.GetUnderlined() &&
425 font1
.GetFamily() == font
.GetFamily() &&
426 font1
.GetFaceName() == font
.GetFaceName())
433 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
435 const wxPen
& pen1
= dc
.GetPen();
436 if (pen1
.IsOk() && pen
.IsOk())
438 if (pen1
.GetWidth() == pen
.GetWidth() &&
439 pen1
.GetStyle() == pen
.GetStyle() &&
440 pen1
.GetColour() == pen
.GetColour())
446 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
448 const wxBrush
& brush1
= dc
.GetBrush();
449 if (brush1
.IsOk() && brush
.IsOk())
451 if (brush1
.GetStyle() == brush
.GetStyle() &&
452 brush1
.GetColour() == brush
.GetColour())
460 * This is the base for drawable objects.
463 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
465 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
473 wxRichTextObject::~wxRichTextObject()
477 void wxRichTextObject::Dereference()
485 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
488 m_maxSize
= obj
.m_maxSize
;
489 m_minSize
= obj
.m_minSize
;
491 m_range
= obj
.m_range
;
492 m_ownRange
= obj
.m_ownRange
;
493 m_attributes
= obj
.m_attributes
;
494 m_properties
= obj
.m_properties
;
495 m_descent
= obj
.m_descent
;
499 // Get/set the top-level container of this object.
500 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
502 const wxRichTextObject
* p
= this;
507 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
514 void wxRichTextObject::SetMargins(int margin
)
516 SetMargins(margin
, margin
, margin
, margin
);
519 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
521 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
522 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
523 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
524 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
527 int wxRichTextObject::GetLeftMargin() const
529 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
532 int wxRichTextObject::GetRightMargin() const
534 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
537 int wxRichTextObject::GetTopMargin() const
539 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
542 int wxRichTextObject::GetBottomMargin() const
544 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
547 // Calculate the available content space in the given rectangle, given the
548 // margins, border and padding specified in the object's attributes.
549 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, const wxRect
& outerRect
) const
551 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
552 marginRect
= outerRect
;
553 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
557 // Invalidate the buffer. With no argument, invalidates whole buffer.
558 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
560 if (invalidRange
!= wxRICHTEXT_NONE
)
562 SetCachedSize(wxDefaultSize
);
563 SetMaxSize(wxDefaultSize
);
564 SetMinSize(wxDefaultSize
);
568 // Convert units in tenths of a millimetre to device units
569 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
574 scale
= GetBuffer()->GetScale();
575 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
580 // Convert units in tenths of a millimetre to device units
581 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
583 // There are ppi pixels in 254.1 "1/10 mm"
585 double pixels
= ((double) units
* (double)ppi
) / 254.1;
589 // If the result is very small, make it at least one pixel in size.
590 if (pixels
== 0 && units
> 0)
596 // Convert units in pixels to tenths of a millimetre
597 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
602 scale
= GetBuffer()->GetScale();
604 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
607 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
609 // There are ppi pixels in 254.1 "1/10 mm"
611 double p
= double(pixels
);
616 int units
= int( p
* 254.1 / (double) ppi
);
620 // Draw the borders and background for the given rectangle and attributes.
621 // Width and height are taken to be the outer margin size, not the content.
622 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
624 // Assume boxRect is the area around the content
625 wxRect marginRect
= boxRect
;
626 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
628 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
630 // Margin is transparent. Draw background from margin.
631 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
634 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
636 // TODO: get selection colour from control?
637 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
640 colour
= attr
.GetBackgroundColour();
643 wxBrush
brush(colour
);
647 dc
.DrawRectangle(marginRect
);
650 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
652 wxRichTextAttr editBorderAttr
= attr
;
653 // TODO: make guideline colour configurable
654 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
655 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
656 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
658 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
661 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
662 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
664 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
665 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
671 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
673 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
674 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
676 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
678 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
679 wxColour
col(attr
.GetLeft().GetColour());
681 // If pen width is > 1, resorts to a solid rectangle.
684 int penStyle
= wxSOLID
;
685 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
687 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
688 penStyle
= wxLONG_DASH
;
689 wxPen
pen(col
, 1, penStyle
);
691 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
694 else if (borderLeft
> 1)
700 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
704 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
706 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
708 wxColour
col(attr
.GetRight().GetColour());
710 // If pen width is > 1, resorts to a solid rectangle.
711 if (borderRight
== 1)
713 int penStyle
= wxSOLID
;
714 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
716 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
717 penStyle
= wxLONG_DASH
;
718 wxPen
pen(col
, 1, penStyle
);
720 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
723 else if (borderRight
> 1)
729 dc
.DrawRectangle(rect
.x
- borderRight
, rect
.y
, borderRight
, rect
.height
);
733 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
735 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
737 wxColour
col(attr
.GetTop().GetColour());
739 // If pen width is > 1, resorts to a solid rectangle.
742 int penStyle
= wxSOLID
;
743 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
745 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
746 penStyle
= wxLONG_DASH
;
747 wxPen
pen(col
, 1, penStyle
);
749 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
752 else if (borderTop
> 1)
758 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
762 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
764 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
765 wxColour
col(attr
.GetTop().GetColour());
767 // If pen width is > 1, resorts to a solid rectangle.
768 if (borderBottom
== 1)
770 int penStyle
= wxSOLID
;
771 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
773 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
774 penStyle
= wxLONG_DASH
;
775 wxPen
pen(col
, 1, penStyle
);
777 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
780 else if (borderBottom
> 1)
786 dc
.DrawRectangle(rect
.x
, rect
.y
- rect
.height
- borderBottom
, rect
.width
, borderBottom
);
793 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
794 // or marginRect (outer), and the other must be the default rectangle (no width or height).
795 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
798 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
800 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
802 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
803 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
804 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
805 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
807 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
809 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
810 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
811 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
812 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
813 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
814 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
815 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
816 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
818 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
819 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
820 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
821 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
822 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
823 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
824 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
825 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
827 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
828 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
829 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
830 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
831 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
832 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
833 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
834 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
836 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
837 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
838 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
839 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
840 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
841 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
842 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
843 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
845 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
846 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
847 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
848 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
850 if (marginRect
!= wxRect())
852 contentRect
.x
= marginRect
.x
+ leftTotal
;
853 contentRect
.y
= marginRect
.y
+ topTotal
;
854 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
855 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
859 marginRect
.x
= contentRect
.x
- leftTotal
;
860 marginRect
.y
= contentRect
.y
- topTotal
;
861 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
862 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
865 borderRect
.x
= marginRect
.x
+ marginLeft
;
866 borderRect
.y
= marginRect
.y
+ marginTop
;
867 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
868 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
870 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
871 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
872 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
873 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
875 // The outline is outside the margin and doesn't influence the overall box position or content size.
876 outlineRect
.x
= marginRect
.x
- outlineLeft
;
877 outlineRect
.y
= marginRect
.y
- outlineTop
;
878 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
879 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
884 // Get the total margin for the object in pixels, taking into account margin, padding and border size
885 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
886 int& topMargin
, int& bottomMargin
)
888 // Assume boxRect is the area around the content
889 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
890 marginRect
= wxRect(0, 0, 1000, 1000);
892 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
894 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
895 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
896 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
897 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
902 // Returns the rectangle which the child has available to it given restrictions specified in the
903 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
904 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
)
906 wxRect rect
= availableParentSpace
;
909 scale
= buffer
->GetScale();
911 wxTextAttrDimensionConverter
converter(dc
, scale
, availableParentSpace
.GetSize());
913 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
914 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
916 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
917 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
919 // Can specify either left or right for the position (we're assuming we can't
920 // set the left and right edges to effectively set the size. Would we want to do that?)
921 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
923 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
925 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
927 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
928 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
929 rect
.x
= availableParentSpace
.x
+ availableParentSpace
.width
- rect
.width
;
934 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
936 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
938 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
940 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
941 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
942 rect
.y
= availableParentSpace
.y
+ availableParentSpace
.height
- rect
.height
;
950 // Dump to output stream for debugging
951 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
953 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
954 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");
955 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");
958 // Gets the containing buffer
959 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
961 const wxRichTextObject
* obj
= this;
962 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
963 obj
= obj
->GetParent();
964 return wxDynamicCast(obj
, wxRichTextBuffer
);
967 // Get the absolute object position, by traversing up the child/parent hierarchy
968 wxPoint
wxRichTextObject::GetAbsolutePosition() const
970 wxPoint pt
= GetPosition();
972 wxRichTextObject
* p
= GetParent();
975 pt
= pt
+ p
->GetPosition();
982 // Hit-testing: returns a flag indicating hit test details, plus
983 // information about position
984 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
987 return wxRICHTEXT_HITTEST_NONE
;
989 wxRect rect
= GetRect();
990 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
991 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
994 *contextObj
= GetParentContainer();
995 textPosition
= GetRange().GetStart();
996 return wxRICHTEXT_HITTEST_ON
;
999 return wxRICHTEXT_HITTEST_NONE
;
1002 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1003 // lays out the object again using the maximum ('best') size
1004 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextBuffer
* buffer
,
1005 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
, const wxRect
& availableParentSpace
,
1008 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
);
1009 wxRect originalAvailableRect
= availableChildRect
;
1010 Layout(dc
, availableChildRect
, style
);
1012 wxSize maxSize
= GetMaxSize();
1014 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1016 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
/* && maxSize.x > 0 */)
1018 // Redo the layout with a fixed, minimum size this time.
1019 Invalidate(wxRICHTEXT_ALL
);
1020 wxRichTextAttr
newAttr(attr
);
1021 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1022 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1024 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
);
1026 // If a paragraph, align the whole paragraph.
1027 // Problem with this: if we're limited by a floating object, a line may be centered
1028 // w.r.t. the smaller resulting box rather than the actual available width.
1029 if (attr
.HasAlignment())
1031 // centering, right-justification
1032 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1034 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1036 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1038 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1042 Layout(dc
, availableChildRect
, style
);
1056 // Move the object recursively, by adding the offset from old to new
1057 void wxRichTextObject::Move(const wxPoint
& pt
)
1064 * wxRichTextCompositeObject
1065 * This is the base for drawable objects.
1068 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1070 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1071 wxRichTextObject(parent
)
1075 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1080 /// Get the nth child
1081 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1083 wxASSERT ( n
< m_children
.GetCount() );
1085 return m_children
.Item(n
)->GetData();
1088 /// Append a child, returning the position
1089 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1091 m_children
.Append(child
);
1092 child
->SetParent(this);
1093 return m_children
.GetCount() - 1;
1096 /// Insert the child in front of the given object, or at the beginning
1097 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1101 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1102 m_children
.Insert(node
, child
);
1105 m_children
.Insert(child
);
1106 child
->SetParent(this);
1111 /// Delete the child
1112 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1114 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1117 wxRichTextObject
* obj
= node
->GetData();
1118 m_children
.Erase(node
);
1127 /// Delete all children
1128 bool wxRichTextCompositeObject::DeleteChildren()
1130 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1133 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1135 wxRichTextObject
* child
= node
->GetData();
1136 child
->Dereference(); // Only delete if reference count is zero
1138 node
= node
->GetNext();
1139 m_children
.Erase(oldNode
);
1145 /// Get the child count
1146 size_t wxRichTextCompositeObject::GetChildCount() const
1148 return m_children
.GetCount();
1152 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1154 wxRichTextObject::Copy(obj
);
1158 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1161 wxRichTextObject
* child
= node
->GetData();
1162 wxRichTextObject
* newChild
= child
->Clone();
1163 newChild
->SetParent(this);
1164 m_children
.Append(newChild
);
1166 node
= node
->GetNext();
1170 /// Hit-testing: returns a flag indicating hit test details, plus
1171 /// information about position
1172 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1175 return wxRICHTEXT_HITTEST_NONE
;
1177 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1180 wxRichTextObject
* child
= node
->GetData();
1182 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1184 // Just check if we hit the overall object
1185 int ret
= child
->wxRichTextObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1186 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1189 else if (child
->IsShown())
1191 int ret
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1192 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1196 node
= node
->GetNext();
1199 return wxRICHTEXT_HITTEST_NONE
;
1202 /// Finds the absolute position and row height for the given character position
1203 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1205 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1208 wxRichTextObject
* child
= node
->GetData();
1210 // Don't recurse if the child is a top-level object,
1211 // such as a text box, because the character position will no longer
1212 // apply. By definition, a top-level object has its own range of
1213 // character positions.
1214 if (!child
->IsTopLevel() && child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
))
1217 node
= node
->GetNext();
1224 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1226 long current
= start
;
1227 long lastEnd
= current
;
1235 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1238 wxRichTextObject
* child
= node
->GetData();
1241 child
->CalculateRange(current
, childEnd
);
1244 current
= childEnd
+ 1;
1246 node
= node
->GetNext();
1251 // A top-level object always has a range of size 1,
1252 // because its children don't count at this level.
1254 m_range
.SetRange(start
, start
);
1256 // An object with no children has zero length
1257 if (m_children
.GetCount() == 0)
1259 m_ownRange
.SetRange(0, lastEnd
);
1265 // An object with no children has zero length
1266 if (m_children
.GetCount() == 0)
1269 m_range
.SetRange(start
, end
);
1273 /// Delete range from layout.
1274 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1276 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1280 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1281 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1283 // Delete the range in each paragraph
1285 // When a chunk has been deleted, internally the content does not
1286 // now match the ranges.
1287 // However, so long as deletion is not done on the same object twice this is OK.
1288 // If you may delete content from the same object twice, recalculate
1289 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1290 // adjust the range you're deleting accordingly.
1292 if (!obj
->GetRange().IsOutside(range
))
1294 // No need to delete within a top-level object; just removing this object will do fine
1295 if (!obj
->IsTopLevel())
1296 obj
->DeleteRange(range
);
1298 // Delete an empty object, or paragraph within this range.
1299 if (obj
->IsEmpty() ||
1300 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1302 // An empty paragraph has length 1, so won't be deleted unless the
1303 // whole range is deleted.
1304 RemoveChild(obj
, true);
1314 /// Get any text in this object for the given range
1315 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1318 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1321 wxRichTextObject
* child
= node
->GetData();
1322 wxRichTextRange childRange
= range
;
1323 if (!child
->GetRange().IsOutside(range
))
1325 childRange
.LimitTo(child
->GetRange());
1327 wxString childText
= child
->GetTextForRange(childRange
);
1331 node
= node
->GetNext();
1337 /// Get the child object at the given character position
1338 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1340 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1343 wxRichTextObject
* child
= node
->GetData();
1344 if (child
->GetRange().GetStart() == pos
)
1346 node
= node
->GetNext();
1351 /// Recursively merge all pieces that can be merged.
1352 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1354 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1357 wxRichTextObject
* child
= node
->GetData();
1358 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1360 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1362 composite
->Defragment();
1364 if (node
->GetNext())
1366 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1367 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1369 nextChild
->Dereference();
1370 m_children
.Erase(node
->GetNext());
1372 // Don't set node -- we'll see if we can merge again with the next
1376 node
= node
->GetNext();
1379 node
= node
->GetNext();
1382 node
= node
->GetNext();
1385 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1386 if (GetChildCount() > 1)
1388 node
= m_children
.GetFirst();
1391 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1392 wxRichTextObject
* child
= node
->GetData();
1393 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1395 if (child
->IsEmpty())
1397 child
->Dereference();
1398 m_children
.Erase(node
);
1403 node
= node
->GetNext();
1410 /// Dump to output stream for debugging
1411 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1413 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1416 wxRichTextObject
* child
= node
->GetData();
1417 child
->Dump(stream
);
1418 node
= node
->GetNext();
1422 /// Get/set the object size for the given range. Returns false if the range
1423 /// is invalid for this object.
1424 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1426 if (!range
.IsWithin(GetRange()))
1431 wxArrayInt childExtents
;
1438 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1441 wxRichTextObject
* child
= node
->GetData();
1442 if (!child
->GetRange().IsOutside(range
))
1444 // Floating objects have a zero size within the paragraph.
1445 if (child
->IsFloating())
1450 if (partialExtents
->GetCount() > 0)
1451 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1455 partialExtents
->Add(0 /* zero size */ + lastSize
);
1462 wxRichTextRange rangeToUse
= range
;
1463 rangeToUse
.LimitTo(child
->GetRange());
1464 if (child
->IsTopLevel())
1465 rangeToUse
= child
->GetOwnRange();
1467 int childDescent
= 0;
1469 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1470 // but it's only going to be used after caching has taken place.
1471 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1473 childDescent
= child
->GetDescent();
1474 childSize
= child
->GetCachedSize();
1476 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1477 sz
.x
+= childSize
.x
;
1478 descent
= wxMax(descent
, childDescent
);
1480 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1482 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1483 sz
.x
+= childSize
.x
;
1484 descent
= wxMax(descent
, childDescent
);
1486 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1488 child
->SetCachedSize(childSize
);
1489 child
->SetDescent(childDescent
);
1495 if (partialExtents
->GetCount() > 0)
1496 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1501 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1503 partialExtents
->Add(childExtents
[i
] + lastSize
);
1513 node
= node
->GetNext();
1519 // Invalidate the buffer. With no argument, invalidates whole buffer.
1520 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1522 wxRichTextObject::Invalidate(invalidRange
);
1524 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1527 wxRichTextObject
* child
= node
->GetData();
1528 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1532 else if (child
->IsTopLevel())
1534 if (invalidRange
== wxRICHTEXT_NONE
)
1535 child
->Invalidate(wxRICHTEXT_NONE
);
1537 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1540 child
->Invalidate(invalidRange
);
1541 node
= node
->GetNext();
1545 // Move the object recursively, by adding the offset from old to new
1546 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1548 wxPoint oldPos
= GetPosition();
1550 wxPoint offset
= pt
- oldPos
;
1552 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1555 wxRichTextObject
* child
= node
->GetData();
1556 wxPoint childPos
= child
->GetPosition() + offset
;
1557 child
->Move(childPos
);
1558 node
= node
->GetNext();
1564 * wxRichTextParagraphLayoutBox
1565 * This box knows how to lay out paragraphs.
1568 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1570 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1571 wxRichTextCompositeObject(parent
)
1576 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1578 if (m_floatCollector
)
1580 delete m_floatCollector
;
1581 m_floatCollector
= NULL
;
1585 /// Initialize the object.
1586 void wxRichTextParagraphLayoutBox::Init()
1590 // For now, assume is the only box and has no initial size.
1591 m_range
= wxRichTextRange(0, -1);
1592 m_ownRange
= wxRichTextRange(0, -1);
1594 m_invalidRange
= wxRICHTEXT_ALL
;
1597 m_partialParagraph
= false;
1598 m_floatCollector
= NULL
;
1601 void wxRichTextParagraphLayoutBox::Clear()
1605 if (m_floatCollector
)
1606 delete m_floatCollector
;
1607 m_floatCollector
= NULL
;
1608 m_partialParagraph
= false;
1612 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1616 wxRichTextCompositeObject::Copy(obj
);
1618 m_partialParagraph
= obj
.m_partialParagraph
;
1619 m_defaultAttributes
= obj
.m_defaultAttributes
;
1622 // Gather information about floating objects
1623 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1625 if (m_floatCollector
!= NULL
)
1626 delete m_floatCollector
;
1627 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1628 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1629 while (node
&& node
->GetData() != untilObj
)
1631 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1632 wxASSERT (child
!= NULL
);
1634 m_floatCollector
->CollectFloat(child
);
1635 node
= node
->GetNext();
1641 // Returns the style sheet associated with the overall buffer.
1642 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1644 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1647 // Get the number of floating objects at this level
1648 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1650 if (m_floatCollector
)
1651 return m_floatCollector
->GetFloatingObjectCount();
1656 // Get a list of floating objects
1657 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1659 if (m_floatCollector
)
1661 return m_floatCollector
->GetFloatingObjects(objects
);
1668 void wxRichTextParagraphLayoutBox::UpdateRanges()
1672 start
= GetRange().GetStart();
1674 CalculateRange(start
, end
);
1678 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1681 return wxRICHTEXT_HITTEST_NONE
;
1683 int ret
= wxRICHTEXT_HITTEST_NONE
;
1684 if (m_floatCollector
)
1685 ret
= m_floatCollector
->HitTest(dc
, pt
, textPosition
, obj
, flags
);
1687 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1688 return wxRichTextCompositeObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1696 /// Draw the floating objects
1697 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1699 if (m_floatCollector
)
1700 m_floatCollector
->Draw(dc
, range
, selection
, rect
, descent
, style
);
1703 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1708 from
->RemoveChild(obj
);
1709 to
->AppendChild(obj
);
1713 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1718 wxRect
thisRect(GetPosition(), GetCachedSize());
1721 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1722 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1724 // Don't draw guidelines if at top level
1725 int theseFlags
= flags
;
1727 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1728 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), thisRect
, theseFlags
);
1730 DrawFloats(dc
, range
, selection
, rect
, descent
, style
);
1731 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1734 wxRichTextObject
* child
= node
->GetData();
1736 if (child
&& !child
->GetRange().IsOutside(range
))
1738 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1739 wxRichTextRange childRange
= range
;
1740 if (child
->IsTopLevel())
1742 childRange
= child
->GetOwnRange();
1745 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1750 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1755 child
->Draw(dc
, childRange
, selection
, rect
, descent
, style
);
1758 node
= node
->GetNext();
1763 /// Lay the item out
1764 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
1766 SetPosition(rect
.GetPosition());
1771 wxRect availableSpace
;
1772 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1774 // If only laying out a specific area, the passed rect has a different meaning:
1775 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1776 // so that during a size, only the visible part will be relaid out, or
1777 // it would take too long causing flicker. As an approximation, we assume that
1778 // everything up to the start of the visible area is laid out correctly.
1781 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1782 availableSpace
= GetAvailableContentArea(dc
, rect2
);
1784 // Invalidate the part of the buffer from the first visible line
1785 // to the end. If other parts of the buffer are currently invalid,
1786 // then they too will be taken into account if they are above
1787 // the visible point.
1789 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1791 startPos
= line
->GetAbsoluteRange().GetStart();
1793 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1797 availableSpace
= GetAvailableContentArea(dc
, rect
);
1800 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1801 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), GetAttributes(), leftMargin
, rightMargin
,
1802 topMargin
, bottomMargin
);
1807 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1808 int maxMaxWidth
= 0;
1810 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1811 int maxMinWidth
= 0;
1813 // If we have vertical alignment, we must recalculate everything.
1814 bool hasVerticalAlignment
= (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1815 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1817 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1819 bool layoutAll
= true;
1821 // Get invalid range, rounding to paragraph start/end.
1822 wxRichTextRange invalidRange
= GetInvalidRange(true);
1824 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1827 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1829 else // If we know what range is affected, start laying out from that point on.
1830 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1832 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1835 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1836 wxRichTextObjectList::compatibility_iterator previousNode
;
1838 previousNode
= firstNode
->GetPrevious();
1843 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1844 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1847 // Now we're going to start iterating from the first affected paragraph.
1855 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1857 // A way to force speedy rest-of-buffer layout (the 'else' below)
1858 bool forceQuickLayout
= false;
1862 // Assume this box only contains paragraphs
1864 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1865 wxCHECK_MSG( child
, false, wxT("Unknown object in layout") );
1867 if (child
&& child
->IsShown())
1869 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1870 if ( !forceQuickLayout
&&
1872 child
->GetLines().IsEmpty() ||
1873 !child
->GetRange().IsOutside(invalidRange
)) )
1875 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1876 // lays out the object again using the minimum size
1877 child
->LayoutToBestSize(dc
, GetBuffer(),
1878 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1880 // Layout must set the cached size
1881 availableSpace
.y
+= child
->GetCachedSize().y
;
1882 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1883 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1884 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1886 // If we're just formatting the visible part of the buffer,
1887 // and we're now past the bottom of the window, and we don't have any
1888 // floating objects (since they may cause wrapping to change for the rest of the
1889 // the buffer), start quick layout.
1890 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1891 forceQuickLayout
= true;
1895 // We're outside the immediately affected range, so now let's just
1896 // move everything up or down. This assumes that all the children have previously
1897 // been laid out and have wrapped line lists associated with them.
1898 // TODO: check all paragraphs before the affected range.
1900 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1904 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1907 if (child
->GetLines().GetCount() == 0)
1909 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1910 // lays out the object again using the minimum size
1911 child
->LayoutToBestSize(dc
, GetBuffer(),
1912 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1914 //child->Layout(dc, availableChildRect, style);
1917 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1919 availableSpace
.y
+= child
->GetCachedSize().y
;
1920 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1921 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1922 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1925 node
= node
->GetNext();
1931 node
= node
->GetNext();
1934 node
= m_children
.GetLast();
1935 if (node
&& node
->GetData()->IsShown())
1937 wxRichTextObject
* child
= node
->GetData();
1938 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
1939 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
1942 maxHeight
= 0; // topMargin + bottomMargin;
1944 // TODO: (also in para layout) should set the
1945 // object's size to an absolute one if specified,
1946 // but if not specified, calculate it from content.
1948 // We need to add back the margins etc.
1950 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
1951 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
1952 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
1953 SetCachedSize(marginRect
.GetSize());
1956 // The maximum size is the greatest of all maximum widths for all paragraphs.
1958 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
1959 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
1960 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
1961 SetMaxSize(marginRect
.GetSize());
1964 // The minimum size is the greatest of all minimum widths for all paragraphs.
1966 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
1967 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
1968 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
1969 SetMinSize(marginRect
.GetSize());
1972 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1973 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
1976 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
1977 if (leftOverSpace
> 0)
1979 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
1981 yOffset
= (leftOverSpace
/2);
1983 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
1985 yOffset
= leftOverSpace
;
1989 // Move all the children to vertically align the content
1990 // This doesn't take into account floating objects, unfortunately.
1993 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1996 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1998 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2000 node
= node
->GetNext();
2005 m_invalidRange
= wxRICHTEXT_NONE
;
2010 /// Get/set the size for the given range.
2011 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2015 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2016 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2018 // First find the first paragraph whose starting position is within the range.
2019 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2022 // child is a paragraph
2023 wxRichTextObject
* child
= node
->GetData();
2024 const wxRichTextRange
& r
= child
->GetRange();
2026 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2032 node
= node
->GetNext();
2035 // Next find the last paragraph containing part of the range
2036 node
= m_children
.GetFirst();
2039 // child is a paragraph
2040 wxRichTextObject
* child
= node
->GetData();
2041 const wxRichTextRange
& r
= child
->GetRange();
2043 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2049 node
= node
->GetNext();
2052 if (!startPara
|| !endPara
)
2055 // Now we can add up the sizes
2056 for (node
= startPara
; node
; node
= node
->GetNext())
2058 // child is a paragraph
2059 wxRichTextObject
* child
= node
->GetData();
2060 const wxRichTextRange
& childRange
= child
->GetRange();
2061 wxRichTextRange rangeToFind
= range
;
2062 rangeToFind
.LimitTo(childRange
);
2064 if (child
->IsTopLevel())
2065 rangeToFind
= child
->GetOwnRange();
2069 int childDescent
= 0;
2070 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2072 descent
= wxMax(childDescent
, descent
);
2074 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2075 sz
.y
+= childSize
.y
;
2077 if (node
== endPara
)
2086 /// Get the paragraph at the given position
2087 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2092 // First find the first paragraph whose starting position is within the range.
2093 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2096 // child is a paragraph
2097 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2098 // wxASSERT (child != NULL);
2102 // Return first child in buffer if position is -1
2106 if (child
->GetRange().Contains(pos
))
2110 node
= node
->GetNext();
2115 /// Get the line at the given position
2116 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2121 // First find the first paragraph whose starting position is within the range.
2122 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2125 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2126 if (obj
->GetRange().Contains(pos
))
2128 // child is a paragraph
2129 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2130 // wxASSERT (child != NULL);
2134 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2137 wxRichTextLine
* line
= node2
->GetData();
2139 wxRichTextRange range
= line
->GetAbsoluteRange();
2141 if (range
.Contains(pos
) ||
2143 // If the position is end-of-paragraph, then return the last line of
2144 // of the paragraph.
2145 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2148 node2
= node2
->GetNext();
2153 node
= node
->GetNext();
2156 int lineCount
= GetLineCount();
2158 return GetLineForVisibleLineNumber(lineCount
-1);
2163 /// Get the line at the given y pixel position, or the last line.
2164 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2166 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2169 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2170 // wxASSERT (child != NULL);
2174 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2177 wxRichTextLine
* line
= node2
->GetData();
2179 wxRect
rect(line
->GetRect());
2181 if (y
<= rect
.GetBottom())
2184 node2
= node2
->GetNext();
2188 node
= node
->GetNext();
2192 int lineCount
= GetLineCount();
2194 return GetLineForVisibleLineNumber(lineCount
-1);
2199 /// Get the number of visible lines
2200 int wxRichTextParagraphLayoutBox::GetLineCount() const
2204 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2207 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2208 // wxASSERT (child != NULL);
2211 count
+= child
->GetLines().GetCount();
2213 node
= node
->GetNext();
2219 /// Get the paragraph for a given line
2220 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2222 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2225 /// Get the line size at the given position
2226 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2228 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2231 return line
->GetSize();
2234 return wxSize(0, 0);
2238 /// Convenience function to add a paragraph of text
2239 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2241 // Don't use the base style, just the default style, and the base style will
2242 // be combined at display time.
2243 // Divide into paragraph and character styles.
2245 wxRichTextAttr defaultCharStyle
;
2246 wxRichTextAttr defaultParaStyle
;
2248 // If the default style is a named paragraph style, don't apply any character formatting
2249 // to the initial text string.
2250 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2252 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2254 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2257 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2259 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2260 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2262 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2268 return para
->GetRange();
2271 /// Adds multiple paragraphs, based on newlines.
2272 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2274 // Don't use the base style, just the default style, and the base style will
2275 // be combined at display time.
2276 // Divide into paragraph and character styles.
2278 wxRichTextAttr defaultCharStyle
;
2279 wxRichTextAttr defaultParaStyle
;
2281 // If the default style is a named paragraph style, don't apply any character formatting
2282 // to the initial text string.
2283 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2285 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2287 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2290 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2292 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2293 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2295 wxRichTextParagraph
* firstPara
= NULL
;
2296 wxRichTextParagraph
* lastPara
= NULL
;
2298 wxRichTextRange
range(-1, -1);
2301 size_t len
= text
.length();
2303 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2312 wxChar ch
= text
[i
];
2313 if (ch
== wxT('\n') || ch
== wxT('\r'))
2317 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2318 plainText
->SetText(line
);
2320 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2325 line
= wxEmptyString
;
2336 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2337 plainText
->SetText(line
);
2342 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2345 /// Convenience function to add an image
2346 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2348 // Don't use the base style, just the default style, and the base style will
2349 // be combined at display time.
2350 // Divide into paragraph and character styles.
2352 wxRichTextAttr defaultCharStyle
;
2353 wxRichTextAttr defaultParaStyle
;
2355 // If the default style is a named paragraph style, don't apply any character formatting
2356 // to the initial text string.
2357 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2359 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2361 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2364 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2366 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2367 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2369 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2371 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2375 return para
->GetRange();
2379 /// Insert fragment into this box at the given position. If partialParagraph is true,
2380 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2383 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2385 // First, find the first paragraph whose starting position is within the range.
2386 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2389 wxRichTextAttr originalAttr
= para
->GetAttributes();
2391 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2393 // Now split at this position, returning the object to insert the new
2394 // ones in front of.
2395 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2397 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2398 // text, for example, so let's optimize.
2400 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2402 // Add the first para to this para...
2403 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2407 // Iterate through the fragment paragraph inserting the content into this paragraph.
2408 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2409 wxASSERT (firstPara
!= NULL
);
2411 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2414 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2419 para
->AppendChild(newObj
);
2423 // Insert before nextObject
2424 para
->InsertChild(newObj
, nextObject
);
2427 objectNode
= objectNode
->GetNext();
2434 // Procedure for inserting a fragment consisting of a number of
2437 // 1. Remove and save the content that's after the insertion point, for adding
2438 // back once we've added the fragment.
2439 // 2. Add the content from the first fragment paragraph to the current
2441 // 3. Add remaining fragment paragraphs after the current paragraph.
2442 // 4. Add back the saved content from the first paragraph. If partialParagraph
2443 // is true, add it to the last paragraph added and not a new one.
2445 // 1. Remove and save objects after split point.
2446 wxList savedObjects
;
2448 para
->MoveToList(nextObject
, savedObjects
);
2450 // 2. Add the content from the 1st fragment paragraph.
2451 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2455 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2456 wxASSERT(firstPara
!= NULL
);
2458 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2459 para
->SetAttributes(firstPara
->GetAttributes());
2461 // Save empty paragraph attributes for appending later
2462 // These are character attributes deliberately set for a new paragraph. Without this,
2463 // we couldn't pass default attributes when appending a new paragraph.
2464 wxRichTextAttr emptyParagraphAttributes
;
2466 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2468 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2469 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2473 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2476 para
->AppendChild(newObj
);
2478 objectNode
= objectNode
->GetNext();
2481 // 3. Add remaining fragment paragraphs after the current paragraph.
2482 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2483 wxRichTextObject
* nextParagraph
= NULL
;
2484 if (nextParagraphNode
)
2485 nextParagraph
= nextParagraphNode
->GetData();
2487 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2488 wxRichTextParagraph
* finalPara
= para
;
2490 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2492 // If there was only one paragraph, we need to insert a new one.
2495 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2496 wxASSERT( para
!= NULL
);
2498 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2501 InsertChild(finalPara
, nextParagraph
);
2503 AppendChild(finalPara
);
2508 // If there was only one paragraph, or we have full paragraphs in our fragment,
2509 // we need to insert a new one.
2512 finalPara
= new wxRichTextParagraph
;
2515 InsertChild(finalPara
, nextParagraph
);
2517 AppendChild(finalPara
);
2520 // 4. Add back the remaining content.
2524 finalPara
->MoveFromList(savedObjects
);
2526 // Ensure there's at least one object
2527 if (finalPara
->GetChildCount() == 0)
2529 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2530 text
->SetAttributes(emptyParagraphAttributes
);
2532 finalPara
->AppendChild(text
);
2536 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2537 finalPara
->SetAttributes(firstPara
->GetAttributes());
2538 else if (finalPara
&& finalPara
!= para
)
2539 finalPara
->SetAttributes(originalAttr
);
2547 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2550 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2551 wxASSERT( para
!= NULL
);
2553 AppendChild(para
->Clone());
2562 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2563 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2564 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2566 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2569 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2570 wxASSERT( para
!= NULL
);
2572 if (!para
->GetRange().IsOutside(range
))
2574 fragment
.AppendChild(para
->Clone());
2579 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2580 if (!fragment
.IsEmpty())
2582 wxRichTextRange
topTailRange(range
);
2584 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2585 wxASSERT( firstPara
!= NULL
);
2587 // Chop off the start of the paragraph
2588 if (topTailRange
.GetStart() > firstPara
->GetRange().GetStart())
2590 wxRichTextRange
r(firstPara
->GetRange().GetStart(), topTailRange
.GetStart()-1);
2591 firstPara
->DeleteRange(r
);
2593 // Make sure the numbering is correct
2595 fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
);
2597 // Now, we've deleted some positions, so adjust the range
2599 topTailRange
.SetEnd(topTailRange
.GetEnd() - r
.GetLength());
2602 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2603 wxASSERT( lastPara
!= NULL
);
2605 if (topTailRange
.GetEnd() < (lastPara
->GetRange().GetEnd()-1))
2607 wxRichTextRange
r(topTailRange
.GetEnd()+1, lastPara
->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
2608 lastPara
->DeleteRange(r
);
2610 // Make sure the numbering is correct
2612 fragment
.CalculateRange(firstPara
->GetRange().GetStart(), end
);
2614 // We only have part of a paragraph at the end
2615 fragment
.SetPartialParagraph(true);
2619 if (topTailRange
.GetEnd() == (lastPara
->GetRange().GetEnd() - 1))
2620 // We have a partial paragraph (don't save last new paragraph marker)
2621 fragment
.SetPartialParagraph(true);
2623 // We have a complete paragraph
2624 fragment
.SetPartialParagraph(false);
2631 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2632 /// starting from zero at the start of the buffer.
2633 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2640 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2643 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2644 // wxASSERT( child != NULL );
2648 if (child
->GetRange().Contains(pos
))
2650 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2653 wxRichTextLine
* line
= node2
->GetData();
2654 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2656 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2658 // If the caret is displayed at the end of the previous wrapped line,
2659 // we want to return the line it's _displayed_ at (not the actual line
2660 // containing the position).
2661 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2662 return lineCount
- 1;
2669 node2
= node2
->GetNext();
2671 // If we didn't find it in the lines, it must be
2672 // the last position of the paragraph. So return the last line.
2676 lineCount
+= child
->GetLines().GetCount();
2679 node
= node
->GetNext();
2686 /// Given a line number, get the corresponding wxRichTextLine object.
2687 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2691 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2694 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2695 // wxASSERT(child != NULL);
2699 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2701 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2704 wxRichTextLine
* line
= node2
->GetData();
2706 if (lineCount
== lineNumber
)
2711 node2
= node2
->GetNext();
2715 lineCount
+= child
->GetLines().GetCount();
2718 node
= node
->GetNext();
2725 /// Delete range from layout.
2726 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2728 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2730 wxRichTextParagraph
* firstPara
= NULL
;
2733 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2734 // wxASSERT (obj != NULL);
2736 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2740 // Delete the range in each paragraph
2742 if (!obj
->GetRange().IsOutside(range
))
2744 // Deletes the content of this object within the given range
2745 obj
->DeleteRange(range
);
2747 wxRichTextRange thisRange
= obj
->GetRange();
2748 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2750 // If the whole paragraph is within the range to delete,
2751 // delete the whole thing.
2752 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2754 // Delete the whole object
2755 RemoveChild(obj
, true);
2758 else if (!firstPara
)
2761 // If the range includes the paragraph end, we need to join this
2762 // and the next paragraph.
2763 if (range
.GetEnd() <= thisRange
.GetEnd())
2765 // We need to move the objects from the next paragraph
2766 // to this paragraph
2768 wxRichTextParagraph
* nextParagraph
= NULL
;
2769 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2770 nextParagraph
= obj
;
2773 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2775 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2778 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2780 wxRichTextAttr nextParaAttr
;
2781 if (applyFinalParagraphStyle
)
2783 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2784 // not the next one.
2785 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2786 nextParaAttr
= thisAttr
;
2788 nextParaAttr
= nextParagraph
->GetAttributes();
2791 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2793 // Move the objects to the previous para
2794 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2798 wxRichTextObject
* obj1
= node1
->GetData();
2800 firstPara
->AppendChild(obj1
);
2802 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2803 nextParagraph
->GetChildren().Erase(node1
);
2808 // Delete the paragraph
2809 RemoveChild(nextParagraph
, true);
2812 // Avoid empty paragraphs
2813 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2815 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2816 firstPara
->AppendChild(text
);
2819 if (applyFinalParagraphStyle
)
2820 firstPara
->SetAttributes(nextParaAttr
);
2833 /// Get any text in this object for the given range
2834 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2838 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2841 wxRichTextObject
* child
= node
->GetData();
2842 if (!child
->GetRange().IsOutside(range
))
2844 wxRichTextRange childRange
= range
;
2845 childRange
.LimitTo(child
->GetRange());
2847 wxString childText
= child
->GetTextForRange(childRange
);
2851 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2856 node
= node
->GetNext();
2862 /// Get all the text
2863 wxString
wxRichTextParagraphLayoutBox::GetText() const
2865 return GetTextForRange(GetOwnRange());
2868 /// Get the paragraph by number
2869 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2871 if ((size_t) paragraphNumber
>= GetChildCount())
2874 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2877 /// Get the length of the paragraph
2878 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2880 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2882 return para
->GetRange().GetLength() - 1; // don't include newline
2887 /// Get the text of the paragraph
2888 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2890 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2892 return para
->GetTextForRange(para
->GetRange());
2894 return wxEmptyString
;
2897 /// Convert zero-based line column and paragraph number to a position.
2898 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
2900 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
2903 return para
->GetRange().GetStart() + x
;
2909 /// Convert zero-based position to line column and paragraph number
2910 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
2912 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
2916 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2919 wxRichTextObject
* child
= node
->GetData();
2923 node
= node
->GetNext();
2927 *x
= pos
- para
->GetRange().GetStart();
2935 /// Get the leaf object in a paragraph at this position.
2936 /// Given a line number, get the corresponding wxRichTextLine object.
2937 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
2939 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2942 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
2946 wxRichTextObject
* child
= node
->GetData();
2947 if (child
->GetRange().Contains(position
))
2950 node
= node
->GetNext();
2952 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
2953 return para
->GetChildren().GetLast()->GetData();
2958 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
2959 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
2961 bool characterStyle
= false;
2962 bool paragraphStyle
= false;
2964 if (style
.IsCharacterStyle())
2965 characterStyle
= true;
2966 if (style
.IsParagraphStyle())
2967 paragraphStyle
= true;
2969 wxRichTextBuffer
* buffer
= GetBuffer();
2971 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
2972 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
2973 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
2974 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
2975 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
2976 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
2978 // Apply paragraph style first, if any
2979 wxRichTextAttr
wholeStyle(style
);
2981 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
2983 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
2985 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
2988 // Limit the attributes to be set to the content to only character attributes.
2989 wxRichTextAttr
characterAttributes(wholeStyle
);
2990 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
2992 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
2994 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
2996 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
2999 // If we are associated with a control, make undoable; otherwise, apply immediately
3002 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3004 wxRichTextAction
* action
= NULL
;
3006 if (haveControl
&& withUndo
)
3008 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3009 action
->SetRange(range
);
3010 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3013 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3016 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3017 // wxASSERT (para != NULL);
3019 if (para
&& para
->GetChildCount() > 0)
3021 // Stop searching if we're beyond the range of interest
3022 if (para
->GetRange().GetStart() > range
.GetEnd())
3025 if (!para
->GetRange().IsOutside(range
))
3027 // We'll be using a copy of the paragraph to make style changes,
3028 // not updating the buffer directly.
3029 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3031 if (haveControl
&& withUndo
)
3033 newPara
= new wxRichTextParagraph(*para
);
3034 action
->GetNewParagraphs().AppendChild(newPara
);
3036 // Also store the old ones for Undo
3037 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3042 // If we're specifying paragraphs only, then we really mean character formatting
3043 // to be included in the paragraph style
3044 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3048 // Removes the given style from the paragraph
3049 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3051 else if (resetExistingStyle
)
3052 newPara
->GetAttributes() = wholeStyle
;
3057 // Only apply attributes that will make a difference to the combined
3058 // style as seen on the display
3059 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3060 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3063 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3067 // When applying paragraph styles dynamically, don't change the text objects' attributes
3068 // since they will computed as needed. Only apply the character styling if it's _only_
3069 // character styling. This policy is subject to change and might be put under user control.
3071 // Hm. we might well be applying a mix of paragraph and character styles, in which
3072 // case we _do_ want to apply character styles regardless of what para styles are set.
3073 // But if we're applying a paragraph style, which has some character attributes, but
3074 // we only want the paragraphs to hold this character style, then we _don't_ want to
3075 // apply the character style. So we need to be able to choose.
3077 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3079 wxRichTextRange
childRange(range
);
3080 childRange
.LimitTo(newPara
->GetRange());
3082 // Find the starting position and if necessary split it so
3083 // we can start applying a different style.
3084 // TODO: check that the style actually changes or is different
3085 // from style outside of range
3086 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3087 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3089 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3090 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3092 firstObject
= newPara
->SplitAt(range
.GetStart());
3094 // Increment by 1 because we're apply the style one _after_ the split point
3095 long splitPoint
= childRange
.GetEnd();
3096 if (splitPoint
!= newPara
->GetRange().GetEnd())
3100 if (splitPoint
== newPara
->GetRange().GetEnd())
3101 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3103 // lastObject is set as a side-effect of splitting. It's
3104 // returned as the object before the new object.
3105 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3107 wxASSERT(firstObject
!= NULL
);
3108 wxASSERT(lastObject
!= NULL
);
3110 if (!firstObject
|| !lastObject
)
3113 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3114 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3116 wxASSERT(firstNode
);
3119 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3123 wxRichTextObject
* child
= node2
->GetData();
3127 // Removes the given style from the paragraph
3128 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3130 else if (resetExistingStyle
)
3131 child
->GetAttributes() = characterAttributes
;
3136 // Only apply attributes that will make a difference to the combined
3137 // style as seen on the display
3138 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3139 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3142 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3145 if (node2
== lastNode
)
3148 node2
= node2
->GetNext();
3154 node
= node
->GetNext();
3157 // Do action, or delay it until end of batch.
3158 if (haveControl
&& withUndo
)
3159 buffer
->SubmitAction(action
);
3164 // Just change the attributes for this single object.
3165 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3167 wxRichTextBuffer
* buffer
= GetBuffer();
3168 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3169 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3170 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3172 wxRichTextAction
*action
= NULL
;
3173 wxRichTextAttr newAttr
= obj
->GetAttributes();
3174 if (resetExistingStyle
)
3177 newAttr
.Apply(textAttr
);
3179 if (haveControl
&& withUndo
)
3181 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3182 action
->SetRange(obj
->GetRange().FromInternal());
3183 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3184 action
->MakeObject(obj
);
3186 action
->GetAttributes() = newAttr
;
3189 obj
->GetAttributes() = newAttr
;
3191 if (haveControl
&& withUndo
)
3192 buffer
->SubmitAction(action
);
3195 /// Get the text attributes for this position.
3196 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3198 return DoGetStyle(position
, style
, true);
3201 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3203 return DoGetStyle(position
, style
, false);
3206 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3207 /// context attributes.
3208 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3210 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3212 if (style
.IsParagraphStyle())
3214 obj
= GetParagraphAtPosition(position
);
3219 // Start with the base style
3220 style
= GetAttributes();
3222 // Apply the paragraph style
3223 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3226 style
= obj
->GetAttributes();
3233 obj
= GetLeafObjectAtPosition(position
);
3238 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3239 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3242 style
= obj
->GetAttributes();
3250 static bool wxHasStyle(long flags
, long style
)
3252 return (flags
& style
) != 0;
3255 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3257 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3259 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3264 /// Get the combined style for a range - if any attribute is different within the range,
3265 /// that attribute is not present within the flags.
3266 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3268 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3270 style
= wxRichTextAttr();
3272 wxRichTextAttr clashingAttr
;
3273 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3275 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3278 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3279 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3281 if (para
->GetChildren().GetCount() == 0)
3283 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3285 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3289 wxRichTextRange
paraRange(para
->GetRange());
3290 paraRange
.LimitTo(range
);
3292 // First collect paragraph attributes only
3293 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3294 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3295 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3297 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3301 wxRichTextObject
* child
= childNode
->GetData();
3302 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3304 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3306 // Now collect character attributes only
3307 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3309 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3312 childNode
= childNode
->GetNext();
3316 node
= node
->GetNext();
3321 /// Set default style
3322 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3324 m_defaultAttributes
= style
;
3328 /// Test if this whole range has character attributes of the specified kind. If any
3329 /// of the attributes are different within the range, the test fails. You
3330 /// can use this to implement, for example, bold button updating. style must have
3331 /// flags indicating which attributes are of interest.
3332 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3335 int matchingCount
= 0;
3337 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3340 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3341 // wxASSERT (para != NULL);
3345 // Stop searching if we're beyond the range of interest
3346 if (para
->GetRange().GetStart() > range
.GetEnd())
3347 return foundCount
== matchingCount
&& foundCount
!= 0;
3349 if (!para
->GetRange().IsOutside(range
))
3351 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3355 wxRichTextObject
* child
= node2
->GetData();
3356 // Allow for empty string if no buffer
3357 wxRichTextRange childRange
= child
->GetRange();
3358 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3359 childRange
.SetEnd(childRange
.GetEnd()+1);
3361 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3364 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3366 if (wxTextAttrEqPartial(textAttr
, style
))
3370 node2
= node2
->GetNext();
3375 node
= node
->GetNext();
3378 return foundCount
== matchingCount
&& foundCount
!= 0;
3381 /// Test if this whole range has paragraph attributes of the specified kind. If any
3382 /// of the attributes are different within the range, the test fails. You
3383 /// can use this to implement, for example, centering button updating. style must have
3384 /// flags indicating which attributes are of interest.
3385 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3388 int matchingCount
= 0;
3390 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3393 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3394 // wxASSERT (para != NULL);
3398 // Stop searching if we're beyond the range of interest
3399 if (para
->GetRange().GetStart() > range
.GetEnd())
3400 return foundCount
== matchingCount
&& foundCount
!= 0;
3402 if (!para
->GetRange().IsOutside(range
))
3404 wxRichTextAttr textAttr
= GetAttributes();
3405 // Apply the paragraph style
3406 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3409 if (wxTextAttrEqPartial(textAttr
, style
))
3414 node
= node
->GetNext();
3416 return foundCount
== matchingCount
&& foundCount
!= 0;
3419 void wxRichTextParagraphLayoutBox::Reset()
3423 wxRichTextBuffer
* buffer
= GetBuffer();
3424 if (buffer
&& buffer
->GetRichTextCtrl())
3426 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3427 event
.SetEventObject(buffer
->GetRichTextCtrl());
3428 event
.SetContainer(this);
3430 buffer
->SendEvent(event
, true);
3433 AddParagraph(wxEmptyString
);
3435 InvalidateHierarchy(wxRICHTEXT_ALL
);
3438 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3439 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3441 wxRichTextCompositeObject::Invalidate(invalidRange
);
3443 DoInvalidate(invalidRange
);
3446 // Do the (in)validation for this object only
3447 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3449 if (invalidRange
== wxRICHTEXT_ALL
)
3451 m_invalidRange
= wxRICHTEXT_ALL
;
3453 // Already invalidating everything
3454 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3459 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3460 m_invalidRange
.SetStart(invalidRange
.GetStart());
3461 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3462 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3466 // Do the (in)validation both up and down the hierarchy
3467 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3469 Invalidate(invalidRange
);
3471 if (invalidRange
!= wxRICHTEXT_NONE
)
3473 // Now go up the hierarchy
3474 wxRichTextObject
* thisObj
= this;
3475 wxRichTextObject
* p
= GetParent();
3478 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3480 l
->DoInvalidate(thisObj
->GetRange());
3488 /// Get invalid range, rounding to entire paragraphs if argument is true.
3489 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3491 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3492 return m_invalidRange
;
3494 wxRichTextRange range
= m_invalidRange
;
3496 if (wholeParagraphs
)
3498 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3500 range
.SetStart(para1
->GetRange().GetStart());
3501 // floating layout make all child should be relayout
3502 range
.SetEnd(GetOwnRange().GetEnd());
3507 /// Apply the style sheet to the buffer, for example if the styles have changed.
3508 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3510 wxASSERT(styleSheet
!= NULL
);
3516 wxRichTextAttr
attr(GetBasicStyle());
3517 if (GetBasicStyle().HasParagraphStyleName())
3519 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3522 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3523 SetBasicStyle(attr
);
3528 if (GetBasicStyle().HasCharacterStyleName())
3530 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3533 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3534 SetBasicStyle(attr
);
3539 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3542 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3543 // wxASSERT (para != NULL);
3547 // Combine paragraph and list styles. If there is a list style in the original attributes,
3548 // the current indentation overrides anything else and is used to find the item indentation.
3549 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3550 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3551 // exception as above).
3552 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3553 // So when changing a list style interactively, could retrieve level based on current style, then
3554 // set appropriate indent and apply new style.
3558 if (para
->GetAttributes().HasOutlineLevel())
3559 outline
= para
->GetAttributes().GetOutlineLevel();
3560 if (para
->GetAttributes().HasBulletNumber())
3561 num
= para
->GetAttributes().GetBulletNumber();
3563 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3565 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3567 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3568 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3569 if (paraDef
&& !listDef
)
3571 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3574 else if (listDef
&& !paraDef
)
3576 // Set overall style defined for the list style definition
3577 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3579 // Apply the style for this level
3580 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3583 else if (listDef
&& paraDef
)
3585 // Combines overall list style, style for level, and paragraph style
3586 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3590 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3592 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3594 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3596 // Overall list definition style
3597 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3599 // Style for this level
3600 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3604 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3606 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3609 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3615 para
->GetAttributes().SetOutlineLevel(outline
);
3617 para
->GetAttributes().SetBulletNumber(num
);
3620 node
= node
->GetNext();
3622 return foundCount
!= 0;
3626 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3628 wxRichTextBuffer
* buffer
= GetBuffer();
3629 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3631 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3632 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3633 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3634 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3636 // Current number, if numbering
3639 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3641 // If we are associated with a control, make undoable; otherwise, apply immediately
3644 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3646 wxRichTextAction
* action
= NULL
;
3648 if (haveControl
&& withUndo
)
3650 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3651 action
->SetRange(range
);
3652 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3655 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3658 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3659 // wxASSERT (para != NULL);
3661 if (para
&& para
->GetChildCount() > 0)
3663 // Stop searching if we're beyond the range of interest
3664 if (para
->GetRange().GetStart() > range
.GetEnd())
3667 if (!para
->GetRange().IsOutside(range
))
3669 // We'll be using a copy of the paragraph to make style changes,
3670 // not updating the buffer directly.
3671 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3673 if (haveControl
&& withUndo
)
3675 newPara
= new wxRichTextParagraph(*para
);
3676 action
->GetNewParagraphs().AppendChild(newPara
);
3678 // Also store the old ones for Undo
3679 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3686 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3687 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3689 // How is numbering going to work?
3690 // If we are renumbering, or numbering for the first time, we need to keep
3691 // track of the number for each level. But we might be simply applying a different
3693 // In Word, applying a style to several paragraphs, even if at different levels,
3694 // reverts the level back to the same one. So we could do the same here.
3695 // Renumbering will need to be done when we promote/demote a paragraph.
3697 // Apply the overall list style, and item style for this level
3698 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3699 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3701 // Now we need to do numbering
3704 newPara
->GetAttributes().SetBulletNumber(n
);
3709 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3711 // if def is NULL, remove list style, applying any associated paragraph style
3712 // to restore the attributes
3714 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3715 newPara
->GetAttributes().SetLeftIndent(0, 0);
3716 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3718 // Eliminate the main list-related attributes
3719 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
);
3721 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3723 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3726 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3733 node
= node
->GetNext();
3736 // Do action, or delay it until end of batch.
3737 if (haveControl
&& withUndo
)
3738 buffer
->SubmitAction(action
);
3743 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3745 wxRichTextBuffer
* buffer
= GetBuffer();
3746 if (buffer
&& buffer
->GetStyleSheet())
3748 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3750 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
3755 /// Clear list for given range
3756 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3758 return SetListStyle(range
, NULL
, flags
);
3761 /// Number/renumber any list elements in the given range
3762 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3764 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
3767 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3768 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
3769 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3771 wxRichTextBuffer
* buffer
= GetBuffer();
3772 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3774 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3775 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3777 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3780 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3782 // Max number of levels
3783 const int maxLevels
= 10;
3785 // The level we're looking at now
3786 int currentLevel
= -1;
3788 // The item number for each level
3789 int levels
[maxLevels
];
3792 // Reset all numbering
3793 for (i
= 0; i
< maxLevels
; i
++)
3795 if (startFrom
!= -1)
3796 levels
[i
] = startFrom
-1;
3797 else if (renumber
) // start again
3800 levels
[i
] = -1; // start from the number we found, if any
3803 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3805 // If we are associated with a control, make undoable; otherwise, apply immediately
3808 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3810 wxRichTextAction
* action
= NULL
;
3812 if (haveControl
&& withUndo
)
3814 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3815 action
->SetRange(range
);
3816 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3819 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3822 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3823 // wxASSERT (para != NULL);
3825 if (para
&& para
->GetChildCount() > 0)
3827 // Stop searching if we're beyond the range of interest
3828 if (para
->GetRange().GetStart() > range
.GetEnd())
3831 if (!para
->GetRange().IsOutside(range
))
3833 // We'll be using a copy of the paragraph to make style changes,
3834 // not updating the buffer directly.
3835 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3837 if (haveControl
&& withUndo
)
3839 newPara
= new wxRichTextParagraph(*para
);
3840 action
->GetNewParagraphs().AppendChild(newPara
);
3842 // Also store the old ones for Undo
3843 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3848 wxRichTextListStyleDefinition
* defToUse
= def
;
3851 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
3852 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
3857 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3858 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
3860 // If we've specified a level to apply to all, change the level.
3861 if (specifiedLevel
!= -1)
3862 thisLevel
= specifiedLevel
;
3864 // Do promotion if specified
3865 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
3867 thisLevel
= thisLevel
- promoteBy
;
3874 // Apply the overall list style, and item style for this level
3875 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3876 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3878 // OK, we've (re)applied the style, now let's get the numbering right.
3880 if (currentLevel
== -1)
3881 currentLevel
= thisLevel
;
3883 // Same level as before, do nothing except increment level's number afterwards
3884 if (currentLevel
== thisLevel
)
3887 // A deeper level: start renumbering all levels after current level
3888 else if (thisLevel
> currentLevel
)
3890 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
3894 currentLevel
= thisLevel
;
3896 else if (thisLevel
< currentLevel
)
3898 currentLevel
= thisLevel
;
3901 // Use the current numbering if -1 and we have a bullet number already
3902 if (levels
[currentLevel
] == -1)
3904 if (newPara
->GetAttributes().HasBulletNumber())
3905 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
3907 levels
[currentLevel
] = 1;
3911 levels
[currentLevel
] ++;
3914 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
3916 // Create the bullet text if an outline list
3917 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
3920 for (i
= 0; i
<= currentLevel
; i
++)
3922 if (!text
.IsEmpty())
3924 text
+= wxString::Format(wxT("%d"), levels
[i
]);
3926 newPara
->GetAttributes().SetBulletText(text
);
3932 node
= node
->GetNext();
3935 // Do action, or delay it until end of batch.
3936 if (haveControl
&& withUndo
)
3937 buffer
->SubmitAction(action
);
3942 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3944 wxRichTextBuffer
* buffer
= GetBuffer();
3945 if (buffer
->GetStyleSheet())
3947 wxRichTextListStyleDefinition
* def
= NULL
;
3948 if (!defName
.IsEmpty())
3949 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3950 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
3955 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
3956 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
3959 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
3960 // to NumberList with a flag indicating promotion is required within one of the ranges.
3961 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
3962 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
3963 // We start renumbering from the para after that different para we found. We specify that the numbering of that
3964 // list position will start from 1.
3965 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
3966 // We can end the renumbering at this point.
3968 // For now, only renumber within the promotion range.
3970 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
3973 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
3975 wxRichTextBuffer
* buffer
= GetBuffer();
3976 if (buffer
->GetStyleSheet())
3978 wxRichTextListStyleDefinition
* def
= NULL
;
3979 if (!defName
.IsEmpty())
3980 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3981 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
3986 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
3987 /// position of the paragraph that it had to start looking from.
3988 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
3990 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
3993 wxRichTextBuffer
* buffer
= GetBuffer();
3994 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3995 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
3997 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4000 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4001 // int thisLevel = def->FindLevelForIndent(thisIndent);
4003 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4005 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4006 if (previousParagraph
->GetAttributes().HasBulletName())
4007 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4008 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4009 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4011 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4012 attr
.SetBulletNumber(nextNumber
);
4016 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4017 if (!text
.IsEmpty())
4019 int pos
= text
.Find(wxT('.'), true);
4020 if (pos
!= wxNOT_FOUND
)
4022 text
= text
.Mid(0, text
.Length() - pos
- 1);
4025 text
= wxEmptyString
;
4026 if (!text
.IsEmpty())
4028 text
+= wxString::Format(wxT("%d"), nextNumber
);
4029 attr
.SetBulletText(text
);
4043 * wxRichTextParagraph
4044 * This object represents a single paragraph (or in a straight text editor, a line).
4047 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4049 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4051 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4052 wxRichTextCompositeObject(parent
)
4055 SetAttributes(*style
);
4058 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4059 wxRichTextCompositeObject(parent
)
4062 SetAttributes(*paraStyle
);
4064 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4067 wxRichTextParagraph::~wxRichTextParagraph()
4073 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4078 // Currently we don't merge these attributes with the parent, but we
4079 // should consider whether we should (e.g. if we set a border colour
4080 // for all paragraphs). But generally box attributes are likely to be
4081 // different for different objects.
4082 wxRect paraRect
= GetRect();
4083 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4085 wxRichTextAttr attr
= GetCombinedAttributes();
4087 // Draw the bullet, if any
4088 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4090 if (attr
.GetLeftSubIndent() != 0)
4092 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4093 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4095 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4097 // Combine with the font of the first piece of content, if one is specified
4098 if (GetChildren().GetCount() > 0)
4100 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4101 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4103 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4107 // Get line height from first line, if any
4108 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4111 int lineHeight
wxDUMMY_INITIALIZE(0);
4114 lineHeight
= line
->GetSize().y
;
4115 linePos
= line
->GetPosition() + GetPosition();
4120 if (bulletAttr
.HasFont() && GetBuffer())
4121 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4123 font
= (*wxNORMAL_FONT
);
4125 wxCheckSetFont(dc
, font
);
4127 lineHeight
= dc
.GetCharHeight();
4128 linePos
= GetPosition();
4129 linePos
.y
+= spaceBeforePara
;
4132 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4134 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4136 if (wxRichTextBuffer::GetRenderer())
4137 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4139 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4141 if (wxRichTextBuffer::GetRenderer())
4142 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4146 wxString bulletText
= GetBulletText();
4148 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4149 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4154 // Draw the range for each line, one object at a time.
4156 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4159 wxRichTextLine
* line
= node
->GetData();
4160 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4162 // Lines are specified relative to the paragraph
4164 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4166 // Don't draw if off the screen
4167 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4169 wxPoint objectPosition
= linePosition
;
4170 int maxDescent
= line
->GetDescent();
4172 // Loop through objects until we get to the one within range
4173 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4178 wxRichTextObject
* child
= node2
->GetData();
4180 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4182 // Draw this part of the line at the correct position
4183 wxRichTextRange
objectRange(child
->GetRange());
4184 objectRange
.LimitTo(lineRange
);
4187 if (child
->IsTopLevel())
4189 objectSize
= child
->GetCachedSize();
4190 objectRange
= child
->GetOwnRange();
4194 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4195 if (i
< (int) line
->GetObjectSizes().GetCount())
4197 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4203 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4207 // Use the child object's width, but the whole line's height
4208 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4209 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4211 objectPosition
.x
+= objectSize
.x
;
4214 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4215 // Can break out of inner loop now since we've passed this line's range
4218 node2
= node2
->GetNext();
4222 node
= node
->GetNext();
4228 // Get the range width using partial extents calculated for the whole paragraph.
4229 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4231 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4233 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4236 int leftMostPos
= 0;
4237 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4238 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4240 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4242 int w
= rightMostPos
- leftMostPos
;
4247 /// Lay the item out
4248 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
4250 // Deal with floating objects firstly before the normal layout
4251 wxRichTextBuffer
* buffer
= GetBuffer();
4253 wxRichTextFloatCollector
* collector
= buffer
->GetFloatCollector();
4254 wxASSERT(collector
);
4255 LayoutFloat(dc
, rect
, style
, collector
);
4257 wxRichTextAttr attr
= GetCombinedAttributes();
4261 // Increase the size of the paragraph due to spacing
4262 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4263 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4264 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4265 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4266 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4268 int lineSpacing
= 0;
4270 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4271 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().Ok())
4273 wxCheckSetFont(dc
, attr
.GetFont());
4274 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4277 // Start position for each line relative to the paragraph
4278 int startPositionFirstLine
= leftIndent
;
4279 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4281 // If we have a bullet in this paragraph, the start position for the first line's text
4282 // is actually leftIndent + leftSubIndent.
4283 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4284 startPositionFirstLine
= startPositionSubsequentLines
;
4286 long lastEndPos
= GetRange().GetStart()-1;
4287 long lastCompletedEndPos
= lastEndPos
;
4289 int currentWidth
= 0;
4290 SetPosition(rect
.GetPosition());
4292 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4295 int maxHeight
= currentPosition
.y
;
4300 int lineDescent
= 0;
4302 wxRichTextObjectList::compatibility_iterator node
;
4304 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4306 node
= m_children
.GetFirst();
4309 wxRichTextObject
* child
= node
->GetData();
4310 if (child
->IsTopLevel())
4312 //child->SetCachedSize(wxDefaultSize);
4313 wxRect availableChildRect
= AdjustAvailableSpace(dc
, GetBuffer(), GetAttributes(), child
->GetAttributes(), rect
);
4315 // Hm, can't do this here, we surely need to take into account indents, margins, floating images etc.
4316 // So need to call layout lower down.
4317 child
->Layout(dc
, availableChildRect
, style
);
4320 node
= node
->GetNext();
4325 wxArrayInt partialExtents
;
4328 int paraDescent
= 0;
4330 // This calculates the partial text extents
4331 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4333 node
= m_children
.GetFirst();
4336 wxRichTextObject
* child
= node
->GetData();
4338 //child->SetCachedSize(wxDefaultSize);
4339 child
->Layout(dc
, rect
, style
);
4341 node
= node
->GetNext();
4348 // We may need to go back to a previous child, in which case create the new line,
4349 // find the child corresponding to the start position of the string, and
4352 wxRect availableRect
;
4354 node
= m_children
.GetFirst();
4357 wxRichTextObject
* child
= node
->GetData();
4359 // If floating, ignore. We already laid out floats.
4360 // Also ignore if empty object, except if we haven't got any
4362 if (child
->IsFloating() || !child
->IsShown() ||
4363 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4366 node
= node
->GetNext();
4370 // If this is e.g. a composite text box, it will need to be laid out itself.
4371 // But if just a text fragment or image, for example, this will
4372 // do nothing. NB: won't we need to set the position after layout?
4373 // since for example if position is dependent on vertical line size, we
4374 // can't tell the position until the size is determined. So possibly introduce
4375 // another layout phase.
4377 // We may only be looking at part of a child, if we searched back for wrapping
4378 // and found a suitable point some way into the child. So get the size for the fragment
4381 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4382 long lastPosToUse
= child
->GetRange().GetEnd();
4383 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4385 if (lineBreakInThisObject
)
4386 lastPosToUse
= nextBreakPos
;
4389 int childDescent
= 0;
4391 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4392 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4393 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4395 if (child
->IsTopLevel())
4397 wxSize oldSize
= child
->GetCachedSize();
4399 child
->Invalidate(wxRICHTEXT_ALL
);
4400 child
->SetPosition(wxPoint(0, 0));
4402 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4403 // lays out the object again using the minimum size
4404 // The position will be determined by its location in its line,
4405 // and not by the child's actual position.
4406 child
->LayoutToBestSize(dc
, GetBuffer(),
4407 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4409 if (oldSize
!= child
->GetCachedSize())
4411 partialExtents
.Clear();
4413 // Recalculate the partial text extents since the child object changed size
4414 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4418 // Problem: we need to layout composites here for which we need the available width,
4419 // but we can't get the available width without using the float collector which
4420 // needs to know the object height.
4422 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4424 childSize
= child
->GetCachedSize();
4425 childDescent
= child
->GetDescent();
4429 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4430 // Get height only, then the width using the partial extents
4431 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4432 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4434 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4439 int loopIterations
= 0;
4441 // If there are nested objects that need to lay themselves out, we have to do this in a
4442 // loop because the height of the object may well depend on the available width.
4443 // And because of floating object positioning, the available width depends on the
4444 // height of the object and whether it will clash with the floating objects.
4445 // So, we see whether the available width changes due to the presence of floating images.
4446 // If it does, then we'll use the new restricted width to find the object height again.
4447 // If this causes another restriction in the available width, we'll try again, until
4448 // either we lose patience or the available width settles down.
4453 wxRect oldAvailableRect
= availableRect
;
4455 // Available width depends on the floating objects and the line height.
4456 // Note: the floating objects may be placed vertically along the two side of
4457 // buffer, so we may have different available line widths with different
4458 // [startY, endY]. So, we can't determine how wide the available
4459 // space is until we know the exact line height.
4460 lineDescent
= wxMax(childDescent
, maxDescent
);
4461 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4462 lineHeight
= lineDescent
+ lineAscent
;
4463 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4465 // Adjust availableRect to the space that is available when taking floating objects into account.
4467 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4469 int newX
= floatAvailableRect
.x
+ startOffset
;
4470 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4471 availableRect
.x
= newX
;
4472 availableRect
.width
= newW
;
4475 if (floatAvailableRect
.width
< availableRect
.width
)
4476 availableRect
.width
= floatAvailableRect
.width
;
4478 currentPosition
.x
= availableRect
.x
- rect
.x
;
4480 if (child
->IsTopLevel() && loopIterations
<= 20)
4482 if (availableRect
!= oldAvailableRect
)
4484 wxSize oldSize
= child
->GetCachedSize();
4486 //child->SetCachedSize(wxDefaultSize);
4487 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4488 // lays out the object again using the minimum size
4489 child
->Invalidate(wxRICHTEXT_ALL
);
4490 child
->LayoutToBestSize(dc
, GetBuffer(),
4491 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4492 childSize
= child
->GetCachedSize();
4493 childDescent
= child
->GetDescent();
4494 //child->SetPosition(availableRect.GetPosition());
4496 if (oldSize
!= child
->GetCachedSize())
4498 partialExtents
.Clear();
4500 // Recalculate the partial text extents since the child object changed size
4501 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4504 // Go around the loop finding the available rect for the given floating objects
4515 // 1) There was a line break BEFORE the natural break
4516 // 2) There was a line break AFTER the natural break
4517 // 3) It's the last line
4518 // 4) The child still fits (carry on) - 'else' clause
4520 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4522 (childSize
.x
+ currentWidth
> availableRect
.width
)
4524 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4528 if (child
->IsTopLevel())
4530 // We can move it to the correct position at this point
4531 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4534 long wrapPosition
= 0;
4535 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4536 wrapPosition
= child
->GetRange().GetEnd();
4539 // Find a place to wrap. This may walk back to previous children,
4540 // for example if a word spans several objects.
4541 // Note: one object must contains only one wxTextAtrr, so the line height will not
4542 // change inside one object. Thus, we can pass the remain line width to the
4543 // FindWrapPosition function.
4544 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4546 // If the function failed, just cut it off at the end of this child.
4547 wrapPosition
= child
->GetRange().GetEnd();
4550 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4551 if (wrapPosition
<= lastCompletedEndPos
)
4552 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4554 // Line end position shouldn't be the same as the end, or greater.
4555 if (wrapPosition
>= GetRange().GetEnd())
4556 wrapPosition
= GetRange().GetEnd()-1;
4558 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4560 // Let's find the actual size of the current line now
4562 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4564 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4565 /// for the fragment we're about to add.
4566 childDescent
= maxDescent
;
4568 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4569 if (!child
->IsEmpty())
4571 // Get height only, then the width using the partial extents
4572 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4573 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4577 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4579 currentWidth
= actualSize
.x
;
4580 maxDescent
= wxMax(childDescent
, maxDescent
);
4581 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4582 lineHeight
= maxDescent
+ maxAscent
;
4584 if (lineHeight
== 0 && GetBuffer())
4586 wxFont
font(GetBuffer()->GetFontTable().FindFont(attr
));
4587 wxCheckSetFont(dc
, font
);
4588 lineHeight
= dc
.GetCharHeight();
4591 if (maxDescent
== 0)
4594 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4598 wxRichTextLine
* line
= AllocateLine(lineCount
);
4600 // Set relative range so we won't have to change line ranges when paragraphs are moved
4601 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4602 line
->SetPosition(currentPosition
);
4603 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4604 line
->SetDescent(maxDescent
);
4606 maxHeight
= currentPosition
.y
+ lineHeight
;
4608 // Now move down a line. TODO: add margins, spacing
4609 currentPosition
.y
+= lineHeight
;
4610 currentPosition
.y
+= lineSpacing
;
4613 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4618 // TODO: account for zero-length objects, such as fields
4619 // wxASSERT(wrapPosition > lastCompletedEndPos);
4621 lastEndPos
= wrapPosition
;
4622 lastCompletedEndPos
= lastEndPos
;
4626 if (wrapPosition
< GetRange().GetEnd()-1)
4628 // May need to set the node back to a previous one, due to searching back in wrapping
4629 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4630 if (childAfterWrapPosition
)
4631 node
= m_children
.Find(childAfterWrapPosition
);
4633 node
= node
->GetNext();
4636 node
= node
->GetNext();
4638 // Apply paragraph styles such as alignment to the wrapped line
4639 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4643 // We still fit, so don't add a line, and keep going
4644 currentWidth
+= childSize
.x
;
4645 maxDescent
= wxMax(childDescent
, maxDescent
);
4646 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4647 lineHeight
= maxDescent
+ maxAscent
;
4649 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4650 lastEndPos
= child
->GetRange().GetEnd();
4652 node
= node
->GetNext();
4656 wxASSERT(!(lastCompletedEndPos
!= -1 && lastCompletedEndPos
< GetRange().GetEnd()-1));
4659 // Add the last line - it's the current pos -> last para pos
4660 // Substract -1 because the last position is always the end-paragraph position.
4661 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
4663 currentPosition
.x
= availableRect
.x
- rect
.x
;
4665 wxRichTextLine
* line
= AllocateLine(lineCount
);
4667 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
4669 // Set relative range so we won't have to change line ranges when paragraphs are moved
4670 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4672 line
->SetPosition(currentPosition
);
4674 if (lineHeight
== 0 && GetBuffer())
4676 wxFont
font(GetBuffer()->GetFontTable().FindFont(attr
));
4677 wxCheckSetFont(dc
, font
);
4678 lineHeight
= dc
.GetCharHeight();
4680 if (maxDescent
== 0)
4683 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4686 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4687 line
->SetDescent(maxDescent
);
4688 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4689 currentPosition
.y
+= lineHeight
;
4690 currentPosition
.y
+= lineSpacing
;
4695 // Remove remaining unused line objects, if any
4696 ClearUnusedLines(lineCount
);
4698 // We need to add back the margins etc.
4700 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4701 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4702 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4703 SetCachedSize(marginRect
.GetSize());
4706 // The maximum size is the length of the paragraph stretched out into a line.
4707 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4708 // this size. TODO: take into account line breaks.
4710 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4711 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
, currentPosition
.y
+ spaceAfterPara
));
4712 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4713 SetMaxSize(marginRect
.GetSize());
4716 // Find the greatest minimum size. Currently we only look at non-text objects,
4717 // which isn't ideal but it would be slow to find the maximum word width to
4718 // use as the minimum.
4721 node
= m_children
.GetFirst();
4724 wxRichTextObject
* child
= node
->GetData();
4726 // If floating, ignore. We already laid out floats.
4727 // Also ignore if empty object, except if we haven't got any
4729 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4731 if (child
->GetCachedSize().x
> minWidth
)
4732 minWidth
= child
->GetMinSize().x
;
4734 node
= node
->GetNext();
4737 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4738 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4739 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4740 SetMinSize(marginRect
.GetSize());
4744 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4745 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4746 // Use the text extents to calculate the size of each fragment in each line
4747 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4750 wxRichTextLine
* line
= lineNode
->GetData();
4751 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4753 // Loop through objects until we get to the one within range
4754 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4758 wxRichTextObject
* child
= node2
->GetData();
4760 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4762 wxRichTextRange rangeToUse
= lineRange
;
4763 rangeToUse
.LimitTo(child
->GetRange());
4765 // Find the size of the child from the text extents, and store in an array
4766 // for drawing later
4768 if (rangeToUse
.GetStart() > GetRange().GetStart())
4769 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4770 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4771 int sz
= right
- left
;
4772 line
->GetObjectSizes().Add(sz
);
4774 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4775 // Can break out of inner loop now since we've passed this line's range
4778 node2
= node2
->GetNext();
4781 lineNode
= lineNode
->GetNext();
4790 /// Apply paragraph styles, such as centering, to wrapped lines
4791 /// TODO: take into account box attributes
4792 void wxRichTextParagraph::ApplyParagraphStyle(const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4794 if (!attr
.HasAlignment())
4797 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4800 wxRichTextLine
* line
= node
->GetData();
4802 wxPoint pos
= line
->GetPosition();
4803 wxSize size
= line
->GetSize();
4805 // centering, right-justification
4806 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4808 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4809 // Subtract paragraph position because lines are relative to
4811 pos
.x
= rect
.x
- GetPosition().x
+ (rect
.GetWidth() - rightIndent
- size
.x
)/2;
4812 line
->SetPosition(pos
);
4814 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4816 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4817 // Subtract paragraph position because lines are relative to
4819 pos
.x
= (rect
.x
- GetPosition().x
) + rect
.GetWidth() - size
.x
- rightIndent
;
4820 line
->SetPosition(pos
);
4823 node
= node
->GetNext();
4828 /// Apply paragraph styles, such as centering, to wrapped lines
4829 /// TODO: take into account box attributes, possibly
4830 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4832 if (!attr
.HasAlignment())
4835 wxPoint pos
= line
->GetPosition();
4836 wxSize size
= line
->GetSize();
4838 // centering, right-justification
4839 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4841 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4842 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
4843 line
->SetPosition(pos
);
4845 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4847 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4848 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
4849 line
->SetPosition(pos
);
4853 /// Insert text at the given position
4854 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
4856 wxRichTextObject
* childToUse
= NULL
;
4857 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
4859 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4862 wxRichTextObject
* child
= node
->GetData();
4863 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
4870 node
= node
->GetNext();
4875 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
4878 int posInString
= pos
- textObject
->GetRange().GetStart();
4880 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
4881 text
+ textObject
->GetText().Mid(posInString
);
4882 textObject
->SetText(newText
);
4884 int textLength
= text
.length();
4886 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
4887 textObject
->GetRange().GetEnd() + textLength
));
4889 // Increment the end range of subsequent fragments in this paragraph.
4890 // We'll set the paragraph range itself at a higher level.
4892 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
4895 wxRichTextObject
* child
= node
->GetData();
4896 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
4897 textObject
->GetRange().GetEnd() + textLength
));
4899 node
= node
->GetNext();
4906 // TODO: if not a text object, insert at closest position, e.g. in front of it
4912 // Don't pass parent initially to suppress auto-setting of parent range.
4913 // We'll do that at a higher level.
4914 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
4916 AppendChild(textObject
);
4923 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
4925 wxRichTextCompositeObject::Copy(obj
);
4928 /// Clear the cached lines
4929 void wxRichTextParagraph::ClearLines()
4931 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
4934 /// Get/set the object size for the given range. Returns false if the range
4935 /// is invalid for this object.
4936 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
4938 if (!range
.IsWithin(GetRange()))
4941 if (flags
& wxRICHTEXT_UNFORMATTED
)
4943 // Just use unformatted data, assume no line breaks
4944 // TODO: take into account line breaks
4948 wxArrayInt childExtents
;
4955 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4959 wxRichTextObject
* child
= node
->GetData();
4960 if (!child
->GetRange().IsOutside(range
))
4962 // Floating objects have a zero size within the paragraph.
4963 if (child
->IsFloating())
4968 if (partialExtents
->GetCount() > 0)
4969 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4973 partialExtents
->Add(0 /* zero size */ + lastSize
);
4980 wxRichTextRange rangeToUse
= range
;
4981 rangeToUse
.LimitTo(child
->GetRange());
4983 if (child
->IsTopLevel())
4984 rangeToUse
= child
->GetOwnRange();
4986 int childDescent
= 0;
4988 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4989 // but it's only going to be used after caching has taken place.
4990 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
4992 childDescent
= child
->GetDescent();
4993 childSize
= child
->GetCachedSize();
4995 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4996 sz
.x
+= childSize
.x
;
4997 descent
= wxMax(descent
, childDescent
);
4999 else if (child
->IsTopLevel())
5001 childDescent
= child
->GetDescent();
5002 childSize
= child
->GetCachedSize();
5004 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5005 sz
.x
+= childSize
.x
;
5006 descent
= wxMax(descent
, childDescent
);
5007 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5009 child
->SetCachedSize(childSize
);
5010 child
->SetDescent(childDescent
);
5016 if (partialExtents
->GetCount() > 0)
5017 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5021 partialExtents
->Add(childSize
.x
+ lastSize
);
5024 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5026 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5027 sz
.x
+= childSize
.x
;
5028 descent
= wxMax(descent
, childDescent
);
5030 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5032 child
->SetCachedSize(childSize
);
5033 child
->SetDescent(childDescent
);
5039 if (partialExtents
->GetCount() > 0)
5040 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5045 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5047 partialExtents
->Add(childExtents
[i
] + lastSize
);
5057 node
= node
->GetNext();
5063 // Use formatted data, with line breaks
5066 // We're going to loop through each line, and then for each line,
5067 // call GetRangeSize for the fragment that comprises that line.
5068 // Only we have to do that multiple times within the line, because
5069 // the line may be broken into pieces. For now ignore line break commands
5070 // (so we can assume that getting the unformatted size for a fragment
5071 // within a line is the actual size)
5073 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5076 wxRichTextLine
* line
= node
->GetData();
5077 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5078 if (!lineRange
.IsOutside(range
))
5082 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5085 wxRichTextObject
* child
= node2
->GetData();
5087 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5089 wxRichTextRange rangeToUse
= lineRange
;
5090 rangeToUse
.LimitTo(child
->GetRange());
5091 if (child
->IsTopLevel())
5092 rangeToUse
= child
->GetOwnRange();
5095 int childDescent
= 0;
5096 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5098 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5099 lineSize
.x
+= childSize
.x
;
5101 descent
= wxMax(descent
, childDescent
);
5104 node2
= node2
->GetNext();
5107 // Increase size by a line (TODO: paragraph spacing)
5109 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5111 node
= node
->GetNext();
5118 /// Finds the absolute position and row height for the given character position
5119 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5123 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5125 *height
= line
->GetSize().y
;
5127 *height
= dc
.GetCharHeight();
5129 // -1 means 'the start of the buffer'.
5132 pt
= pt
+ line
->GetPosition();
5137 // The final position in a paragraph is taken to mean the position
5138 // at the start of the next paragraph.
5139 if (index
== GetRange().GetEnd())
5141 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5142 wxASSERT( parent
!= NULL
);
5144 // Find the height at the next paragraph, if any
5145 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5148 *height
= line
->GetSize().y
;
5149 pt
= line
->GetAbsolutePosition();
5153 *height
= dc
.GetCharHeight();
5154 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5155 pt
= wxPoint(indent
, GetCachedSize().y
);
5161 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5164 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5167 wxRichTextLine
* line
= node
->GetData();
5168 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5169 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5171 // If this is the last point in the line, and we're forcing the
5172 // returned value to be the start of the next line, do the required
5174 if (index
== lineRange
.GetEnd() && forceLineStart
)
5176 if (node
->GetNext())
5178 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5179 *height
= nextLine
->GetSize().y
;
5180 pt
= nextLine
->GetAbsolutePosition();
5185 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5187 wxRichTextRange
r(lineRange
.GetStart(), index
);
5191 // We find the size of the line up to this point,
5192 // then we can add this size to the line start position and
5193 // paragraph start position to find the actual position.
5195 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5197 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5198 *height
= line
->GetSize().y
;
5205 node
= node
->GetNext();
5211 /// Hit-testing: returns a flag indicating hit test details, plus
5212 /// information about position
5213 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5216 return wxRICHTEXT_HITTEST_NONE
;
5218 // If we're in the top-level container, then we can return
5219 // a suitable hit test code even if the point is outside the container area,
5220 // so that we can position the caret sensibly even if we don't
5221 // click on valid content. If we're not at the top-level, and the point
5222 // is not within this paragraph object, then we don't want to stop more
5223 // precise hit-testing from working prematurely, so return immediately.
5224 // NEW STRATEGY: use the parent boundary to test whether we're in the
5225 // right region, not the paragraph, since the paragraph may be positioned
5226 // some way in from where the user clicks.
5229 wxRichTextObject
* tempObj
, *tempContextObj
;
5230 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5231 return wxRICHTEXT_HITTEST_NONE
;
5234 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5237 wxRichTextObject
* child
= objNode
->GetData();
5238 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5241 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5242 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5247 objNode
= objNode
->GetNext();
5250 wxPoint paraPos
= GetPosition();
5252 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5255 wxRichTextLine
* line
= node
->GetData();
5256 wxPoint linePos
= paraPos
+ line
->GetPosition();
5257 wxSize lineSize
= line
->GetSize();
5258 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5260 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5262 if (pt
.x
< linePos
.x
)
5264 textPosition
= lineRange
.GetStart();
5265 *obj
= FindObjectAtPosition(textPosition
);
5266 *contextObj
= GetContainer();
5267 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5269 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5271 textPosition
= lineRange
.GetEnd();
5272 *obj
= FindObjectAtPosition(textPosition
);
5273 *contextObj
= GetContainer();
5274 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5278 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5279 wxArrayInt partialExtents
;
5284 // This calculates the partial text extents
5285 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), & partialExtents
);
5287 int lastX
= linePos
.x
;
5289 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5291 int nextX
= partialExtents
[i
] + linePos
.x
;
5293 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5295 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5297 *obj
= FindObjectAtPosition(textPosition
);
5298 *contextObj
= GetContainer();
5300 // So now we know it's between i-1 and i.
5301 // Let's see if we can be more precise about
5302 // which side of the position it's on.
5304 int midPoint
= (nextX
+ lastX
)/2;
5305 if (pt
.x
>= midPoint
)
5306 return wxRICHTEXT_HITTEST_AFTER
;
5308 return wxRICHTEXT_HITTEST_BEFORE
;
5315 int lastX
= linePos
.x
;
5316 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5321 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5323 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5325 int nextX
= childSize
.x
+ linePos
.x
;
5327 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5331 *obj
= FindObjectAtPosition(textPosition
);
5332 *contextObj
= GetContainer();
5334 // So now we know it's between i-1 and i.
5335 // Let's see if we can be more precise about
5336 // which side of the position it's on.
5338 int midPoint
= (nextX
+ lastX
)/2;
5339 if (pt
.x
>= midPoint
)
5340 return wxRICHTEXT_HITTEST_AFTER
;
5342 return wxRICHTEXT_HITTEST_BEFORE
;
5353 node
= node
->GetNext();
5356 return wxRICHTEXT_HITTEST_NONE
;
5359 /// Split an object at this position if necessary, and return
5360 /// the previous object, or NULL if inserting at beginning.
5361 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5363 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5366 wxRichTextObject
* child
= node
->GetData();
5368 if (pos
== child
->GetRange().GetStart())
5372 if (node
->GetPrevious())
5373 *previousObject
= node
->GetPrevious()->GetData();
5375 *previousObject
= NULL
;
5381 if (child
->GetRange().Contains(pos
))
5383 // This should create a new object, transferring part of
5384 // the content to the old object and the rest to the new object.
5385 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5387 // If we couldn't split this object, just insert in front of it.
5390 // Maybe this is an empty string, try the next one
5395 // Insert the new object after 'child'
5396 if (node
->GetNext())
5397 m_children
.Insert(node
->GetNext(), newObject
);
5399 m_children
.Append(newObject
);
5400 newObject
->SetParent(this);
5403 *previousObject
= child
;
5409 node
= node
->GetNext();
5412 *previousObject
= NULL
;
5416 /// Move content to a list from obj on
5417 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5419 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5422 wxRichTextObject
* child
= node
->GetData();
5425 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5427 node
= node
->GetNext();
5429 m_children
.DeleteNode(oldNode
);
5433 /// Add content back from list
5434 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5436 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5438 AppendChild((wxRichTextObject
*) node
->GetData());
5443 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5445 wxRichTextCompositeObject::CalculateRange(start
, end
);
5447 // Add one for end of paragraph
5450 m_range
.SetRange(start
, end
);
5453 /// Find the object at the given position
5454 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5456 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5459 wxRichTextObject
* obj
= node
->GetData();
5460 if (obj
->GetRange().Contains(position
) ||
5461 obj
->GetRange().GetStart() == position
||
5462 obj
->GetRange().GetEnd() == position
)
5465 node
= node
->GetNext();
5470 /// Get the plain text searching from the start or end of the range.
5471 /// The resulting string may be shorter than the range given.
5472 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5474 text
= wxEmptyString
;
5478 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5481 wxRichTextObject
* obj
= node
->GetData();
5482 if (!obj
->GetRange().IsOutside(range
))
5484 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5487 text
+= textObj
->GetTextForRange(range
);
5495 node
= node
->GetNext();
5500 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5503 wxRichTextObject
* obj
= node
->GetData();
5504 if (!obj
->GetRange().IsOutside(range
))
5506 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5509 text
= textObj
->GetTextForRange(range
) + text
;
5513 text
= wxT(" ") + text
;
5517 node
= node
->GetPrevious();
5524 /// Find a suitable wrap position.
5525 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5527 if (range
.GetLength() <= 0)
5530 // Find the first position where the line exceeds the available space.
5532 long breakPosition
= range
.GetEnd();
5534 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5535 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5539 if (range
.GetStart() > GetRange().GetStart())
5540 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5545 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5547 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5549 if (widthFromStartOfThisRange
> availableSpace
)
5551 breakPosition
= i
-1;
5559 // Binary chop for speed
5560 long minPos
= range
.GetStart();
5561 long maxPos
= range
.GetEnd();
5564 if (minPos
== maxPos
)
5567 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5569 if (sz
.x
> availableSpace
)
5570 breakPosition
= minPos
- 1;
5573 else if ((maxPos
- minPos
) == 1)
5576 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5578 if (sz
.x
> availableSpace
)
5579 breakPosition
= minPos
- 1;
5582 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5583 if (sz
.x
> availableSpace
)
5584 breakPosition
= maxPos
-1;
5590 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5593 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5595 if (sz
.x
> availableSpace
)
5607 // Now we know the last position on the line.
5608 // Let's try to find a word break.
5611 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5613 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5614 if (newLinePos
!= wxNOT_FOUND
)
5616 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5620 int spacePos
= plainText
.Find(wxT(' '), true);
5621 int tabPos
= plainText
.Find(wxT('\t'), true);
5622 int pos
= wxMax(spacePos
, tabPos
);
5623 if (pos
!= wxNOT_FOUND
)
5625 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5626 breakPosition
= breakPosition
- positionsFromEndOfString
;
5631 wrapPosition
= breakPosition
;
5636 /// Get the bullet text for this paragraph.
5637 wxString
wxRichTextParagraph::GetBulletText()
5639 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5640 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5641 return wxEmptyString
;
5643 int number
= GetAttributes().GetBulletNumber();
5646 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5648 text
.Printf(wxT("%d"), number
);
5650 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5652 // TODO: Unicode, and also check if number > 26
5653 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5655 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5657 // TODO: Unicode, and also check if number > 26
5658 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5660 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5662 text
= wxRichTextDecimalToRoman(number
);
5664 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5666 text
= wxRichTextDecimalToRoman(number
);
5669 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5671 text
= GetAttributes().GetBulletText();
5674 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5676 // The outline style relies on the text being computed statically,
5677 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5678 // should be stored in the attributes; if not, just use the number for this
5679 // level, as previously computed.
5680 if (!GetAttributes().GetBulletText().IsEmpty())
5681 text
= GetAttributes().GetBulletText();
5684 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5686 text
= wxT("(") + text
+ wxT(")");
5688 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5690 text
= text
+ wxT(")");
5693 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5701 /// Allocate or reuse a line object
5702 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5704 if (pos
< (int) m_cachedLines
.GetCount())
5706 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5712 wxRichTextLine
* line
= new wxRichTextLine(this);
5713 m_cachedLines
.Append(line
);
5718 /// Clear remaining unused line objects, if any
5719 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5721 int cachedLineCount
= m_cachedLines
.GetCount();
5722 if ((int) cachedLineCount
> lineCount
)
5724 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5726 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5727 wxRichTextLine
* line
= node
->GetData();
5728 m_cachedLines
.Erase(node
);
5735 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5736 /// retrieve the actual style.
5737 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5739 wxRichTextAttr attr
;
5740 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5743 attr
= buf
->GetBasicStyle();
5744 if (!includingBoxAttr
)
5746 attr
.GetTextBoxAttr().Reset();
5747 // The background colour will be painted by the container, and we don't
5748 // want to unnecessarily overwrite the background when we're drawing text
5749 // because this may erase the guideline (which appears just under the text
5750 // if there's no padding).
5751 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5753 wxRichTextApplyStyle(attr
, GetAttributes());
5756 attr
= GetAttributes();
5758 wxRichTextApplyStyle(attr
, contentStyle
);
5762 /// Get combined attributes of the base style and paragraph style.
5763 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5765 wxRichTextAttr attr
;
5766 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5769 attr
= buf
->GetBasicStyle();
5770 if (!includingBoxAttr
)
5771 attr
.GetTextBoxAttr().Reset();
5772 wxRichTextApplyStyle(attr
, GetAttributes());
5775 attr
= GetAttributes();
5780 // Create default tabstop array
5781 void wxRichTextParagraph::InitDefaultTabs()
5783 // create a default tab list at 10 mm each.
5784 for (int i
= 0; i
< 20; ++i
)
5786 sm_defaultTabs
.Add(i
*100);
5790 // Clear default tabstop array
5791 void wxRichTextParagraph::ClearDefaultTabs()
5793 sm_defaultTabs
.Clear();
5796 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5798 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5801 wxRichTextObject
* anchored
= node
->GetData();
5802 if (anchored
&& anchored
->IsFloating())
5806 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5809 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5811 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5812 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5814 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5818 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5820 /* Update the offset */
5821 int newOffsetY
= pos
- rect
.y
;
5822 if (newOffsetY
!= offsetY
)
5824 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5825 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5826 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5829 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5831 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5832 x
= rect
.x
+ rect
.width
- size
.x
;
5834 anchored
->SetPosition(wxPoint(x
, pos
));
5835 anchored
->SetCachedSize(size
);
5836 floatCollector
->CollectFloat(this, anchored
);
5839 node
= node
->GetNext();
5843 // Get the first position from pos that has a line break character.
5844 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
5846 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5849 wxRichTextObject
* obj
= node
->GetData();
5850 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
5852 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5855 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
5860 node
= node
->GetNext();
5867 * This object represents a line in a paragraph, and stores
5868 * offsets from the start of the paragraph representing the
5869 * start and end positions of the line.
5872 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
5878 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
5881 m_range
.SetRange(-1, -1);
5882 m_pos
= wxPoint(0, 0);
5883 m_size
= wxSize(0, 0);
5885 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5886 m_objectSizes
.Clear();
5891 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
5893 m_range
= obj
.m_range
;
5894 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5895 m_objectSizes
= obj
.m_objectSizes
;
5899 /// Get the absolute object position
5900 wxPoint
wxRichTextLine::GetAbsolutePosition() const
5902 return m_parent
->GetPosition() + m_pos
;
5905 /// Get the absolute range
5906 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
5908 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
5909 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
5914 * wxRichTextPlainText
5915 * This object represents a single piece of text.
5918 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
5920 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
5921 wxRichTextObject(parent
)
5924 SetAttributes(*style
);
5929 #define USE_KERNING_FIX 1
5931 // If insufficient tabs are defined, this is the tab width used
5932 #define WIDTH_FOR_DEFAULT_TABS 50
5935 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
5937 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
5938 wxASSERT (para
!= NULL
);
5940 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5942 // Let's make the assumption for now that for content in a paragraph, including
5943 // text, we never have a discontinuous selection. So we only deal with a
5945 wxRichTextRange selectionRange
;
5946 if (selection
.IsValid())
5948 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
5949 if (selectionRanges
.GetCount() > 0)
5950 selectionRange
= selectionRanges
[0];
5952 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5955 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5957 int offset
= GetRange().GetStart();
5959 // Replace line break characters with spaces
5960 wxString str
= m_text
;
5961 wxString toRemove
= wxRichTextLineBreakChar
;
5962 str
.Replace(toRemove
, wxT(" "));
5963 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
5966 long len
= range
.GetLength();
5967 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
5969 // Test for the optimized situations where all is selected, or none
5972 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
5973 wxCheckSetFont(dc
, textFont
);
5974 int charHeight
= dc
.GetCharHeight();
5977 if ( textFont
.Ok() )
5979 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
5981 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5982 textFont
.SetPointSize( static_cast<int>(size
) );
5985 wxCheckSetFont(dc
, textFont
);
5987 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
5989 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5990 textFont
.SetPointSize( static_cast<int>(size
) );
5992 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
5993 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
5994 wxCheckSetFont(dc
, textFont
);
5999 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6005 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6008 // TODO: new selection code
6010 // (a) All selected.
6011 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6013 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6015 // (b) None selected.
6016 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6018 // Draw all unselected
6019 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6023 // (c) Part selected, part not
6024 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6026 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6028 // 1. Initial unselected chunk, if any, up until start of selection.
6029 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6031 int r1
= range
.GetStart();
6032 int s1
= selectionRange
.GetStart()-1;
6033 int fragmentLen
= s1
- r1
+ 1;
6034 if (fragmentLen
< 0)
6036 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6038 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6040 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6043 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6045 // Compensate for kerning difference
6046 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6047 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6049 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6050 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6051 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6052 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6054 int kerningDiff
= (w1
+ w3
) - w2
;
6055 x
= x
- kerningDiff
;
6060 // 2. Selected chunk, if any.
6061 if (selectionRange
.GetEnd() >= range
.GetStart())
6063 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6064 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6066 int fragmentLen
= s2
- s1
+ 1;
6067 if (fragmentLen
< 0)
6069 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6071 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6073 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6076 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6078 // Compensate for kerning difference
6079 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6080 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6082 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6083 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6084 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6085 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6087 int kerningDiff
= (w1
+ w3
) - w2
;
6088 x
= x
- kerningDiff
;
6093 // 3. Remaining unselected chunk, if any
6094 if (selectionRange
.GetEnd() < range
.GetEnd())
6096 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6097 int r2
= range
.GetEnd();
6099 int fragmentLen
= r2
- s2
+ 1;
6100 if (fragmentLen
< 0)
6102 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6104 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6106 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6113 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6115 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6117 wxArrayInt tabArray
;
6121 if (attr
.GetTabs().IsEmpty())
6122 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6124 tabArray
= attr
.GetTabs();
6125 tabCount
= tabArray
.GetCount();
6127 for (int i
= 0; i
< tabCount
; ++i
)
6129 int pos
= tabArray
[i
];
6130 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6137 int nextTabPos
= -1;
6143 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6144 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6146 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6147 wxCheckSetPen(dc
, wxPen(highlightColour
));
6148 dc
.SetTextForeground(highlightTextColour
);
6149 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6153 dc
.SetTextForeground(attr
.GetTextColour());
6155 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6157 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6158 dc
.SetTextBackground(attr
.GetBackgroundColour());
6161 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6164 wxCoord x_orig
= GetParent()->GetPosition().x
;
6167 // the string has a tab
6168 // break up the string at the Tab
6169 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6170 str
= str
.AfterFirst(wxT('\t'));
6171 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6173 bool not_found
= true;
6174 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6176 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6178 // Find the next tab position.
6179 // Even if we're at the end of the tab array, we must still draw the chunk.
6181 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6183 if (nextTabPos
<= tabPos
)
6185 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6186 nextTabPos
= tabPos
+ defaultTabWidth
;
6193 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6194 dc
.DrawRectangle(selRect
);
6196 dc
.DrawText(stringChunk
, x
, y
);
6198 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6200 wxPen oldPen
= dc
.GetPen();
6201 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6202 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6203 wxCheckSetPen(dc
, oldPen
);
6209 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6214 dc
.GetTextExtent(str
, & w
, & h
);
6217 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6218 dc
.DrawRectangle(selRect
);
6220 dc
.DrawText(str
, x
, y
);
6222 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6224 wxPen oldPen
= dc
.GetPen();
6225 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6226 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6227 wxCheckSetPen(dc
, oldPen
);
6236 /// Lay the item out
6237 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
))
6239 // Only lay out if we haven't already cached the size
6241 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6243 // Eventually we want to have a reasonable estimate of minimum size.
6244 m_minSize
= wxSize(0, 0);
6249 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6251 wxRichTextObject::Copy(obj
);
6253 m_text
= obj
.m_text
;
6256 /// Get/set the object size for the given range. Returns false if the range
6257 /// is invalid for this object.
6258 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6260 if (!range
.IsWithin(GetRange()))
6263 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6264 wxASSERT (para
!= NULL
);
6266 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6268 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6270 // Always assume unformatted text, since at this level we have no knowledge
6271 // of line breaks - and we don't need it, since we'll calculate size within
6272 // formatted text by doing it in chunks according to the line ranges
6274 bool bScript(false);
6275 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6278 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6279 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6281 wxFont textFont
= font
;
6282 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6283 textFont
.SetPointSize( static_cast<int>(size
) );
6284 wxCheckSetFont(dc
, textFont
);
6289 wxCheckSetFont(dc
, font
);
6293 bool haveDescent
= false;
6294 int startPos
= range
.GetStart() - GetRange().GetStart();
6295 long len
= range
.GetLength();
6297 wxString
str(m_text
);
6298 wxString toReplace
= wxRichTextLineBreakChar
;
6299 str
.Replace(toReplace
, wxT(" "));
6301 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6303 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6304 stringChunk
.MakeUpper();
6308 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6310 // the string has a tab
6311 wxArrayInt tabArray
;
6312 if (textAttr
.GetTabs().IsEmpty())
6313 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6315 tabArray
= textAttr
.GetTabs();
6317 int tabCount
= tabArray
.GetCount();
6319 for (int i
= 0; i
< tabCount
; ++i
)
6321 int pos
= tabArray
[i
];
6322 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6326 int nextTabPos
= -1;
6328 while (stringChunk
.Find(wxT('\t')) >= 0)
6330 int absoluteWidth
= 0;
6332 // the string has a tab
6333 // break up the string at the Tab
6334 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6335 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6340 if (partialExtents
->GetCount() > 0)
6341 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6345 // Add these partial extents
6347 dc
.GetPartialTextExtents(stringFragment
, p
);
6349 for (j
= 0; j
< p
.GetCount(); j
++)
6350 partialExtents
->Add(oldWidth
+ p
[j
]);
6352 if (partialExtents
->GetCount() > 0)
6353 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6355 absoluteWidth
= relativeX
;
6359 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6361 absoluteWidth
= width
+ relativeX
;
6365 bool notFound
= true;
6366 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6368 nextTabPos
= tabArray
.Item(i
);
6370 // Find the next tab position.
6371 // Even if we're at the end of the tab array, we must still process the chunk.
6373 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6375 if (nextTabPos
<= absoluteWidth
)
6377 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6378 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6382 width
= nextTabPos
- relativeX
;
6385 partialExtents
->Add(width
);
6391 if (!stringChunk
.IsEmpty())
6396 if (partialExtents
->GetCount() > 0)
6397 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6401 // Add these partial extents
6403 dc
.GetPartialTextExtents(stringChunk
, p
);
6405 for (j
= 0; j
< p
.GetCount(); j
++)
6406 partialExtents
->Add(oldWidth
+ p
[j
]);
6410 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6418 int charHeight
= dc
.GetCharHeight();
6419 if ((*partialExtents
).GetCount() > 0)
6420 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6423 size
= wxSize(w
, charHeight
);
6427 size
= wxSize(width
, dc
.GetCharHeight());
6431 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6439 /// Do a split, returning an object containing the second part, and setting
6440 /// the first part in 'this'.
6441 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6443 long index
= pos
- GetRange().GetStart();
6445 if (index
< 0 || index
>= (int) m_text
.length())
6448 wxString firstPart
= m_text
.Mid(0, index
);
6449 wxString secondPart
= m_text
.Mid(index
);
6453 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6454 newObject
->SetAttributes(GetAttributes());
6456 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6457 GetRange().SetEnd(pos
-1);
6463 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6465 end
= start
+ m_text
.length() - 1;
6466 m_range
.SetRange(start
, end
);
6470 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6472 wxRichTextRange r
= range
;
6474 r
.LimitTo(GetRange());
6476 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6482 long startIndex
= r
.GetStart() - GetRange().GetStart();
6483 long len
= r
.GetLength();
6485 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6489 /// Get text for the given range.
6490 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6492 wxRichTextRange r
= range
;
6494 r
.LimitTo(GetRange());
6496 long startIndex
= r
.GetStart() - GetRange().GetStart();
6497 long len
= r
.GetLength();
6499 return m_text
.Mid(startIndex
, len
);
6502 /// Returns true if this object can merge itself with the given one.
6503 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6505 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6506 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6509 /// Returns true if this object merged itself with the given one.
6510 /// The calling code will then delete the given object.
6511 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6513 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6514 wxASSERT( textObject
!= NULL
);
6518 m_text
+= textObject
->GetText();
6519 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6526 /// Dump to output stream for debugging
6527 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6529 wxRichTextObject::Dump(stream
);
6530 stream
<< m_text
<< wxT("\n");
6533 /// Get the first position from pos that has a line break character.
6534 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6537 int len
= m_text
.length();
6538 int startPos
= pos
- m_range
.GetStart();
6539 for (i
= startPos
; i
< len
; i
++)
6541 wxChar ch
= m_text
[i
];
6542 if (ch
== wxRichTextLineBreakChar
)
6544 return i
+ m_range
.GetStart();
6552 * This is a kind of box, used to represent the whole buffer
6555 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6557 wxList
wxRichTextBuffer::sm_handlers
;
6558 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6559 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6560 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6563 void wxRichTextBuffer::Init()
6565 m_commandProcessor
= new wxCommandProcessor
;
6566 m_styleSheet
= NULL
;
6568 m_batchedCommandDepth
= 0;
6569 m_batchedCommand
= NULL
;
6576 wxRichTextBuffer::~wxRichTextBuffer()
6578 delete m_commandProcessor
;
6579 delete m_batchedCommand
;
6582 ClearEventHandlers();
6585 void wxRichTextBuffer::ResetAndClearCommands()
6589 GetCommandProcessor()->ClearCommands();
6592 Invalidate(wxRICHTEXT_ALL
);
6595 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6597 wxRichTextParagraphLayoutBox::Copy(obj
);
6599 m_styleSheet
= obj
.m_styleSheet
;
6600 m_modified
= obj
.m_modified
;
6601 m_batchedCommandDepth
= 0;
6602 if (m_batchedCommand
)
6603 delete m_batchedCommand
;
6604 m_batchedCommand
= NULL
;
6605 m_suppressUndo
= obj
.m_suppressUndo
;
6606 m_invalidRange
= obj
.m_invalidRange
;
6609 /// Push style sheet to top of stack
6610 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6613 styleSheet
->InsertSheet(m_styleSheet
);
6615 SetStyleSheet(styleSheet
);
6620 /// Pop style sheet from top of stack
6621 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6625 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6626 m_styleSheet
= oldSheet
->GetNextSheet();
6635 /// Submit command to insert paragraphs
6636 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6638 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(pos
, paragraphs
, ctrl
, this, flags
);
6641 /// Submit command to insert paragraphs
6642 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6644 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6646 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6648 wxRichTextAttr
* p
= NULL
;
6649 wxRichTextAttr paraAttr
;
6650 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6652 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6653 if (!paraAttr
.IsDefault())
6659 action
->GetNewParagraphs() = paragraphs
;
6661 if (p
&& !p
->IsDefault())
6663 for (wxRichTextObjectList::compatibility_iterator node
= action
->GetNewParagraphs().GetChildren().GetFirst(); node
; node
= node
->GetNext())
6665 wxRichTextObject
* child
= node
->GetData();
6666 child
->SetAttributes(*p
);
6670 action
->SetPosition(pos
);
6672 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6673 if (!paragraphs
.GetPartialParagraph())
6674 range
.SetEnd(range
.GetEnd()+1);
6676 // Set the range we'll need to delete in Undo
6677 action
->SetRange(range
);
6679 buffer
->SubmitAction(action
);
6684 /// Submit command to insert the given text
6685 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6687 return ctrl
->GetFocusObject()->InsertTextWithUndo(pos
, text
, ctrl
, this, flags
);
6690 /// Submit command to insert the given text
6691 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6693 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6695 wxRichTextAttr
* p
= NULL
;
6696 wxRichTextAttr paraAttr
;
6697 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6699 // Get appropriate paragraph style
6700 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6701 if (!paraAttr
.IsDefault())
6705 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6707 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6709 if (text
.length() > 0 && text
.Last() != wxT('\n'))
6711 // Don't count the newline when undoing
6713 action
->GetNewParagraphs().SetPartialParagraph(true);
6715 else if (text
.length() > 0 && text
.Last() == wxT('\n'))
6718 action
->SetPosition(pos
);
6720 // Set the range we'll need to delete in Undo
6721 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6723 buffer
->SubmitAction(action
);
6728 /// Submit command to insert the given text
6729 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6731 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(pos
, ctrl
, this, flags
);
6734 /// Submit command to insert the given text
6735 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6737 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6739 wxRichTextAttr
* p
= NULL
;
6740 wxRichTextAttr paraAttr
;
6741 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6743 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6744 if (!paraAttr
.IsDefault())
6748 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6750 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6751 action
->GetNewParagraphs().AppendChild(newPara
);
6752 action
->GetNewParagraphs().UpdateRanges();
6753 action
->GetNewParagraphs().SetPartialParagraph(false);
6754 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6758 newPara
->SetAttributes(*p
);
6760 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6762 if (para
&& para
->GetRange().GetEnd() == pos
)
6765 // Now see if we need to number the paragraph.
6766 if (newPara
->GetAttributes().HasBulletNumber())
6768 wxRichTextAttr numberingAttr
;
6769 if (FindNextParagraphNumber(para
, numberingAttr
))
6770 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6774 action
->SetPosition(pos
);
6776 // Use the default character style
6777 // Use the default character style
6778 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6780 // Check whether the default style merely reflects the paragraph/basic style,
6781 // in which case don't apply it.
6782 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6783 wxRichTextAttr toApply
;
6786 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6787 wxRichTextAttr newAttr
;
6788 // This filters out attributes that are accounted for by the current
6789 // paragraph/basic style
6790 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6793 toApply
= defaultStyle
;
6795 if (!toApply
.IsDefault())
6796 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6799 // Set the range we'll need to delete in Undo
6800 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6802 buffer
->SubmitAction(action
);
6807 /// Submit command to insert the given image
6808 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6809 const wxRichTextAttr
& textAttr
)
6811 return ctrl
->GetFocusObject()->InsertImageWithUndo(pos
, imageBlock
, ctrl
, this, flags
, textAttr
);
6814 /// Submit command to insert the given image
6815 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
,
6816 wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
,
6817 const wxRichTextAttr
& textAttr
)
6819 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6821 wxRichTextAttr
* p
= NULL
;
6822 wxRichTextAttr paraAttr
;
6823 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6825 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6826 if (!paraAttr
.IsDefault())
6830 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6832 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6834 newPara
->SetAttributes(*p
);
6836 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6837 newPara
->AppendChild(imageObject
);
6838 imageObject
->SetAttributes(textAttr
);
6839 action
->GetNewParagraphs().AppendChild(newPara
);
6840 action
->GetNewParagraphs().UpdateRanges();
6842 action
->GetNewParagraphs().SetPartialParagraph(true);
6844 action
->SetPosition(pos
);
6846 // Set the range we'll need to delete in Undo
6847 action
->SetRange(wxRichTextRange(pos
, pos
));
6849 buffer
->SubmitAction(action
);
6854 // Insert an object with no change of it
6855 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6857 return ctrl
->GetFocusObject()->InsertObjectWithUndo(pos
, object
, ctrl
, this, flags
);
6860 // Insert an object with no change of it
6861 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6863 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6865 wxRichTextAttr
* p
= NULL
;
6866 wxRichTextAttr paraAttr
;
6867 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6869 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6870 if (!paraAttr
.IsDefault())
6874 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6876 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6878 newPara
->SetAttributes(*p
);
6880 newPara
->AppendChild(object
);
6881 action
->GetNewParagraphs().AppendChild(newPara
);
6882 action
->GetNewParagraphs().UpdateRanges();
6884 action
->GetNewParagraphs().SetPartialParagraph(true);
6886 action
->SetPosition(pos
);
6888 // Set the range we'll need to delete in Undo
6889 action
->SetRange(wxRichTextRange(pos
, pos
));
6891 buffer
->SubmitAction(action
);
6893 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
6897 /// Get the style that is appropriate for a new paragraph at this position.
6898 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6900 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
6902 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
6905 wxRichTextAttr attr
;
6906 bool foundAttributes
= false;
6908 // Look for a matching paragraph style
6909 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
6911 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
6914 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6915 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
6917 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
6920 foundAttributes
= true;
6921 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6925 // If we didn't find the 'next style', use this style instead.
6926 if (!foundAttributes
)
6928 foundAttributes
= true;
6929 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6934 // Also apply list style if present
6935 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
6937 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
6940 int thisIndent
= para
->GetAttributes().GetLeftIndent();
6941 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
6943 // Apply the overall list style, and item style for this level
6944 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
6945 wxRichTextApplyStyle(attr
, listStyle
);
6946 attr
.SetOutlineLevel(thisLevel
);
6947 if (para
->GetAttributes().HasBulletNumber())
6948 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
6952 if (!foundAttributes
)
6954 attr
= para
->GetAttributes();
6955 int flags
= attr
.GetFlags();
6957 // Eliminate character styles
6958 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
6959 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
6960 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
6961 attr
.SetFlags(flags
);
6967 return wxRichTextAttr();
6970 /// Submit command to delete this range
6971 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
6973 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
6976 /// Submit command to delete this range
6977 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
6979 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
6981 action
->SetPosition(ctrl
->GetCaretPosition());
6983 // Set the range to delete
6984 action
->SetRange(range
);
6986 // Copy the fragment that we'll need to restore in Undo
6987 CopyFragment(range
, action
->GetOldParagraphs());
6989 // See if we're deleting a paragraph marker, in which case we need to
6990 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6991 if (range
.GetStart() == range
.GetEnd())
6993 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
6994 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
6996 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
6997 if (nextPara
&& nextPara
!= para
)
6999 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7000 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7005 buffer
->SubmitAction(action
);
7010 /// Collapse undo/redo commands
7011 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7013 if (m_batchedCommandDepth
== 0)
7015 wxASSERT(m_batchedCommand
== NULL
);
7016 if (m_batchedCommand
)
7018 GetCommandProcessor()->Store(m_batchedCommand
);
7020 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7023 m_batchedCommandDepth
++;
7028 /// Collapse undo/redo commands
7029 bool wxRichTextBuffer::EndBatchUndo()
7031 m_batchedCommandDepth
--;
7033 wxASSERT(m_batchedCommandDepth
>= 0);
7034 wxASSERT(m_batchedCommand
!= NULL
);
7036 if (m_batchedCommandDepth
== 0)
7038 GetCommandProcessor()->Store(m_batchedCommand
);
7039 m_batchedCommand
= NULL
;
7045 /// Submit immediately, or delay according to whether collapsing is on
7046 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7048 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7050 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7051 cmd
->AddAction(action
);
7053 cmd
->GetActions().Clear();
7056 m_batchedCommand
->AddAction(action
);
7060 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7061 cmd
->AddAction(action
);
7063 // Only store it if we're not suppressing undo.
7064 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7070 /// Begin suppressing undo/redo commands.
7071 bool wxRichTextBuffer::BeginSuppressUndo()
7078 /// End suppressing undo/redo commands.
7079 bool wxRichTextBuffer::EndSuppressUndo()
7086 /// Begin using a style
7087 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7089 wxRichTextAttr
newStyle(GetDefaultStyle());
7091 // Save the old default style
7092 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7094 wxRichTextApplyStyle(newStyle
, style
);
7095 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7097 SetDefaultStyle(newStyle
);
7103 bool wxRichTextBuffer::EndStyle()
7105 if (!m_attributeStack
.GetFirst())
7107 wxLogDebug(_("Too many EndStyle calls!"));
7111 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7112 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7113 m_attributeStack
.Erase(node
);
7115 SetDefaultStyle(*attr
);
7122 bool wxRichTextBuffer::EndAllStyles()
7124 while (m_attributeStack
.GetCount() != 0)
7129 /// Clear the style stack
7130 void wxRichTextBuffer::ClearStyleStack()
7132 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7133 delete (wxRichTextAttr
*) node
->GetData();
7134 m_attributeStack
.Clear();
7137 /// Begin using bold
7138 bool wxRichTextBuffer::BeginBold()
7140 wxRichTextAttr attr
;
7141 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7143 return BeginStyle(attr
);
7146 /// Begin using italic
7147 bool wxRichTextBuffer::BeginItalic()
7149 wxRichTextAttr attr
;
7150 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7152 return BeginStyle(attr
);
7155 /// Begin using underline
7156 bool wxRichTextBuffer::BeginUnderline()
7158 wxRichTextAttr attr
;
7159 attr
.SetFontUnderlined(true);
7161 return BeginStyle(attr
);
7164 /// Begin using point size
7165 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7167 wxRichTextAttr attr
;
7168 attr
.SetFontSize(pointSize
);
7170 return BeginStyle(attr
);
7173 /// Begin using this font
7174 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7176 wxRichTextAttr attr
;
7179 return BeginStyle(attr
);
7182 /// Begin using this colour
7183 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7185 wxRichTextAttr attr
;
7186 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7187 attr
.SetTextColour(colour
);
7189 return BeginStyle(attr
);
7192 /// Begin using alignment
7193 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7195 wxRichTextAttr attr
;
7196 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7197 attr
.SetAlignment(alignment
);
7199 return BeginStyle(attr
);
7202 /// Begin left indent
7203 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7205 wxRichTextAttr attr
;
7206 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7207 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7209 return BeginStyle(attr
);
7212 /// Begin right indent
7213 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7215 wxRichTextAttr attr
;
7216 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7217 attr
.SetRightIndent(rightIndent
);
7219 return BeginStyle(attr
);
7222 /// Begin paragraph spacing
7223 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7227 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7229 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7231 wxRichTextAttr attr
;
7232 attr
.SetFlags(flags
);
7233 attr
.SetParagraphSpacingBefore(before
);
7234 attr
.SetParagraphSpacingAfter(after
);
7236 return BeginStyle(attr
);
7239 /// Begin line spacing
7240 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7242 wxRichTextAttr attr
;
7243 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7244 attr
.SetLineSpacing(lineSpacing
);
7246 return BeginStyle(attr
);
7249 /// Begin numbered bullet
7250 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7252 wxRichTextAttr attr
;
7253 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7254 attr
.SetBulletStyle(bulletStyle
);
7255 attr
.SetBulletNumber(bulletNumber
);
7256 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7258 return BeginStyle(attr
);
7261 /// Begin symbol bullet
7262 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7264 wxRichTextAttr attr
;
7265 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7266 attr
.SetBulletStyle(bulletStyle
);
7267 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7268 attr
.SetBulletText(symbol
);
7270 return BeginStyle(attr
);
7273 /// Begin standard bullet
7274 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7276 wxRichTextAttr attr
;
7277 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7278 attr
.SetBulletStyle(bulletStyle
);
7279 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7280 attr
.SetBulletName(bulletName
);
7282 return BeginStyle(attr
);
7285 /// Begin named character style
7286 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7288 if (GetStyleSheet())
7290 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7293 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7294 return BeginStyle(attr
);
7300 /// Begin named paragraph style
7301 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7303 if (GetStyleSheet())
7305 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7308 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7309 return BeginStyle(attr
);
7315 /// Begin named list style
7316 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7318 if (GetStyleSheet())
7320 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7323 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7325 attr
.SetBulletNumber(number
);
7327 return BeginStyle(attr
);
7334 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7336 wxRichTextAttr attr
;
7338 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7340 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7343 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7348 return BeginStyle(attr
);
7351 /// Adds a handler to the end
7352 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7354 sm_handlers
.Append(handler
);
7357 /// Inserts a handler at the front
7358 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7360 sm_handlers
.Insert( handler
);
7363 /// Removes a handler
7364 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7366 wxRichTextFileHandler
*handler
= FindHandler(name
);
7369 sm_handlers
.DeleteObject(handler
);
7377 /// Finds a handler by filename or, if supplied, type
7378 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7379 wxRichTextFileType imageType
)
7381 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7382 return FindHandler(imageType
);
7383 else if (!filename
.IsEmpty())
7385 wxString path
, file
, ext
;
7386 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7387 return FindHandler(ext
, imageType
);
7394 /// Finds a handler by name
7395 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7397 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7400 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7401 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7403 node
= node
->GetNext();
7408 /// Finds a handler by extension and type
7409 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7411 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7414 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7415 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7416 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7418 node
= node
->GetNext();
7423 /// Finds a handler by type
7424 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7426 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7429 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7430 if (handler
->GetType() == type
) return handler
;
7431 node
= node
->GetNext();
7436 void wxRichTextBuffer::InitStandardHandlers()
7438 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7439 AddHandler(new wxRichTextPlainTextHandler
);
7442 void wxRichTextBuffer::CleanUpHandlers()
7444 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7447 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7448 wxList::compatibility_iterator next
= node
->GetNext();
7453 sm_handlers
.Clear();
7456 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7463 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7467 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7468 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7473 wildcard
+= wxT(";");
7474 wildcard
+= wxT("*.") + handler
->GetExtension();
7479 wildcard
+= wxT("|");
7480 wildcard
+= handler
->GetName();
7481 wildcard
+= wxT(" ");
7482 wildcard
+= _("files");
7483 wildcard
+= wxT(" (*.");
7484 wildcard
+= handler
->GetExtension();
7485 wildcard
+= wxT(")|*.");
7486 wildcard
+= handler
->GetExtension();
7488 types
->Add(handler
->GetType());
7493 node
= node
->GetNext();
7497 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7502 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7504 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7507 SetDefaultStyle(wxRichTextAttr());
7508 handler
->SetFlags(GetHandlerFlags());
7509 bool success
= handler
->LoadFile(this, filename
);
7510 Invalidate(wxRICHTEXT_ALL
);
7518 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7520 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7523 handler
->SetFlags(GetHandlerFlags());
7524 return handler
->SaveFile(this, filename
);
7530 /// Load from a stream
7531 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7533 wxRichTextFileHandler
* handler
= FindHandler(type
);
7536 SetDefaultStyle(wxRichTextAttr());
7537 handler
->SetFlags(GetHandlerFlags());
7538 bool success
= handler
->LoadFile(this, stream
);
7539 Invalidate(wxRICHTEXT_ALL
);
7546 /// Save to a stream
7547 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7549 wxRichTextFileHandler
* handler
= FindHandler(type
);
7552 handler
->SetFlags(GetHandlerFlags());
7553 return handler
->SaveFile(this, stream
);
7559 /// Copy the range to the clipboard
7560 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7562 bool success
= false;
7563 wxRichTextParagraphLayoutBox
* container
= this;
7564 if (GetRichTextCtrl())
7565 container
= GetRichTextCtrl()->GetFocusObject();
7567 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7569 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7571 wxTheClipboard
->Clear();
7573 // Add composite object
7575 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7578 wxString text
= container
->GetTextForRange(range
);
7581 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7584 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7587 // Add rich text buffer data object. This needs the XML handler to be present.
7589 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7591 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7592 container
->CopyFragment(range
, *richTextBuf
);
7594 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7597 if (wxTheClipboard
->SetData(compositeObject
))
7600 wxTheClipboard
->Close();
7609 /// Paste the clipboard content to the buffer
7610 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7612 bool success
= false;
7613 wxRichTextParagraphLayoutBox
* container
= this;
7614 if (GetRichTextCtrl())
7615 container
= GetRichTextCtrl()->GetFocusObject();
7617 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7618 if (CanPasteFromClipboard())
7620 if (wxTheClipboard
->Open())
7622 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7624 wxRichTextBufferDataObject data
;
7625 wxTheClipboard
->GetData(data
);
7626 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7629 container
->InsertParagraphsWithUndo(position
+1, *richTextBuffer
, GetRichTextCtrl(), this, 0);
7630 if (GetRichTextCtrl())
7631 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7632 delete richTextBuffer
;
7635 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7637 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7641 wxTextDataObject data
;
7642 wxTheClipboard
->GetData(data
);
7643 wxString
text(data
.GetText());
7646 text2
.Alloc(text
.Length()+1);
7648 for (i
= 0; i
< text
.Length(); i
++)
7650 wxChar ch
= text
[i
];
7651 if (ch
!= wxT('\r'))
7655 wxString text2
= text
;
7657 container
->InsertTextWithUndo(position
+1, text2
, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7659 if (GetRichTextCtrl())
7660 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7664 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7666 wxBitmapDataObject data
;
7667 wxTheClipboard
->GetData(data
);
7668 wxBitmap
bitmap(data
.GetBitmap());
7669 wxImage
image(bitmap
.ConvertToImage());
7671 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7673 action
->GetNewParagraphs().AddImage(image
);
7675 if (action
->GetNewParagraphs().GetChildCount() == 1)
7676 action
->GetNewParagraphs().SetPartialParagraph(true);
7678 action
->SetPosition(position
+1);
7680 // Set the range we'll need to delete in Undo
7681 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7683 SubmitAction(action
);
7687 wxTheClipboard
->Close();
7691 wxUnusedVar(position
);
7696 /// Can we paste from the clipboard?
7697 bool wxRichTextBuffer::CanPasteFromClipboard() const
7699 bool canPaste
= false;
7700 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7701 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7703 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7705 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7707 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7708 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7712 wxTheClipboard
->Close();
7718 /// Dumps contents of buffer for debugging purposes
7719 void wxRichTextBuffer::Dump()
7723 wxStringOutputStream
stream(& text
);
7724 wxTextOutputStream
textStream(stream
);
7731 /// Add an event handler
7732 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7734 m_eventHandlers
.Append(handler
);
7738 /// Remove an event handler
7739 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7741 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7744 m_eventHandlers
.Erase(node
);
7754 /// Clear event handlers
7755 void wxRichTextBuffer::ClearEventHandlers()
7757 m_eventHandlers
.Clear();
7760 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7761 /// otherwise will stop at the first successful one.
7762 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7764 bool success
= false;
7765 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7767 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7768 if (handler
->ProcessEvent(event
))
7778 /// Set style sheet and notify of the change
7779 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7781 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7783 wxWindowID id
= wxID_ANY
;
7784 if (GetRichTextCtrl())
7785 id
= GetRichTextCtrl()->GetId();
7787 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, id
);
7788 event
.SetEventObject(GetRichTextCtrl());
7789 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7790 event
.SetOldStyleSheet(oldSheet
);
7791 event
.SetNewStyleSheet(sheet
);
7794 if (SendEvent(event
) && !event
.IsAllowed())
7796 if (sheet
!= oldSheet
)
7802 if (oldSheet
&& oldSheet
!= sheet
)
7805 SetStyleSheet(sheet
);
7807 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7808 event
.SetOldStyleSheet(NULL
);
7811 return SendEvent(event
);
7814 /// Set renderer, deleting old one
7815 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7819 sm_renderer
= renderer
;
7822 /// Hit-testing: returns a flag indicating hit test details, plus
7823 /// information about position
7824 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7826 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7827 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7833 textPosition
= m_ownRange
.GetEnd()-1;
7836 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7840 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7842 if (bulletAttr
.GetTextColour().Ok())
7844 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7845 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7849 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7850 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7854 if (bulletAttr
.HasFont())
7856 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
7859 font
= (*wxNORMAL_FONT
);
7861 wxCheckSetFont(dc
, font
);
7863 int charHeight
= dc
.GetCharHeight();
7865 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
7866 int bulletHeight
= bulletWidth
;
7870 // Calculate the top position of the character (as opposed to the whole line height)
7871 int y
= rect
.y
+ (rect
.height
- charHeight
);
7873 // Calculate where the bullet should be positioned
7874 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
7876 // The margin between a bullet and text.
7877 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7879 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7880 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
7881 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7882 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
7884 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
7886 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
7888 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
7891 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
7892 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
7893 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
7894 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
7896 dc
.DrawPolygon(4, pts
);
7898 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
7901 pts
[0].x
= x
; pts
[0].y
= y
;
7902 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
7903 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
7905 dc
.DrawPolygon(3, pts
);
7907 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
7909 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
7910 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7912 else // "standard/circle", and catch-all
7914 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7920 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
7925 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
7927 wxRichTextAttr fontAttr
;
7928 fontAttr
.SetFontSize(attr
.GetFontSize());
7929 fontAttr
.SetFontStyle(attr
.GetFontStyle());
7930 fontAttr
.SetFontWeight(attr
.GetFontWeight());
7931 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
7932 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
7933 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
7935 else if (attr
.HasFont())
7936 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
7938 font
= (*wxNORMAL_FONT
);
7940 wxCheckSetFont(dc
, font
);
7942 if (attr
.GetTextColour().Ok())
7943 dc
.SetTextForeground(attr
.GetTextColour());
7945 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
7947 int charHeight
= dc
.GetCharHeight();
7949 dc
.GetTextExtent(text
, & tw
, & th
);
7953 // Calculate the top position of the character (as opposed to the whole line height)
7954 int y
= rect
.y
+ (rect
.height
- charHeight
);
7956 // The margin between a bullet and text.
7957 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7959 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7960 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
7961 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7962 x
= x
+ (rect
.width
)/2 - tw
/2;
7964 dc
.DrawText(text
, x
, y
);
7972 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
7974 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7975 // with the buffer. The store will allow retrieval from memory, disk or other means.
7979 /// Enumerate the standard bullet names currently supported
7980 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
7982 bulletNames
.Add(wxTRANSLATE("standard/circle"));
7983 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
7984 bulletNames
.Add(wxTRANSLATE("standard/square"));
7985 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
7986 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
7995 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
7997 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
7998 wxRichTextParagraphLayoutBox(parent
)
8003 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8008 // TODO: if the active object in the control, draw an indication.
8009 // We need to add the concept of active object, and not just focus object,
8010 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8011 // Ultimately we would like to be able to interactively resize an active object
8012 // using drag handles.
8013 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8017 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8019 wxRichTextParagraphLayoutBox::Copy(obj
);
8022 // Edit properties via a GUI
8023 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8025 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8026 boxDlg
.SetAttributes(GetAttributes());
8028 if (boxDlg
.ShowModal() == wxID_OK
)
8030 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8031 // indeterminate in the object.
8032 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8039 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8041 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8042 wxRichTextBox(parent
)
8047 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8049 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8053 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8055 wxRichTextBox::Copy(obj
);
8058 // Edit properties via a GUI
8059 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8061 // We need to gather common attributes for all selected cells.
8063 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8064 bool multipleCells
= false;
8065 wxRichTextAttr attr
;
8067 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8068 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8070 wxRichTextAttr clashingAttr
, absentAttr
;
8071 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8073 int selectedCellCount
= 0;
8074 for (i
= 0; i
< sel
.GetCount(); i
++)
8076 const wxRichTextRange
& range
= sel
[i
];
8077 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8080 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8082 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8084 selectedCellCount
++;
8087 multipleCells
= selectedCellCount
> 1;
8091 attr
= GetAttributes();
8096 caption
= _("Multiple Cell Properties");
8098 caption
= _("Cell Properties");
8100 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8101 cellDlg
.SetAttributes(attr
);
8103 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8106 // We don't want position and floating controls for a cell.
8107 sizePage
->ShowPositionControls(false);
8108 sizePage
->ShowFloatingControls(false);
8111 if (cellDlg
.ShowModal() == wxID_OK
)
8115 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8116 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8117 // since it may represent clashing attributes across multiple objects.
8118 table
->SetCellStyle(sel
, attr
);
8121 // For a single object, indeterminate attributes set by the user should be reflected in the
8122 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8123 // the style directly instead of applying (which ignores indeterminate attributes,
8124 // leaving them as they were).
8125 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8132 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8134 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8136 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8142 // Draws the object.
8143 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8145 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8148 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8149 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8151 // Lays the object out. rect is the space available for layout. Often it will
8152 // be the specified overall space for this object, if trying to constrain
8153 // layout to a particular size, or it could be the total space available in the
8154 // parent. rect is the overall size, so we must subtract margins and padding.
8155 // to get the actual available space.
8156 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
8158 SetPosition(rect
.GetPosition());
8160 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8161 // minimum size if within alloted size, then divide up remaining size
8162 // between rows/cols.
8165 wxRichTextBuffer
* buffer
= GetBuffer();
8166 if (buffer
) scale
= buffer
->GetScale();
8168 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8169 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8171 // If we have no fixed table size, and assuming we're not pushed for
8172 // space, then we don't have to try to stretch the table to fit the contents.
8173 bool stretchToFitTableWidth
= false;
8175 int tableWidth
= rect
.width
;
8176 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8178 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8180 // Fixed table width, so we do want to stretch columns out if necessary.
8181 stretchToFitTableWidth
= true;
8183 // Shouldn't be able to exceed the size passed to this function
8184 tableWidth
= wxMin(rect
.width
, tableWidth
);
8187 // Get internal padding
8188 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
8189 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8190 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8191 if (GetAttributes().GetTextBoxAttr().GetPadding().GetRight().IsValid())
8192 paddingRight
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetRight());
8193 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8194 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8195 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8196 paddingBottom
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetBottom());
8198 // Assume that left and top padding are also used for inter-cell padding.
8199 int paddingX
= paddingLeft
;
8200 int paddingY
= paddingTop
;
8202 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8203 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8205 // Internal table width - the area for content
8206 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8208 int rowCount
= m_cells
.GetCount();
8209 if (m_colCount
== 0 || rowCount
== 0)
8211 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8212 SetCachedSize(overallRect
.GetSize());
8214 // Zero content size
8215 SetMinSize(overallRect
.GetSize());
8216 SetMaxSize(GetMinSize());
8220 // The final calculated widths
8221 wxArrayInt
colWidths(m_colCount
);
8223 wxArrayInt
absoluteColWidths(m_colCount
);
8224 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8225 wxArrayInt
percentageColWidths(m_colCount
);
8226 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8227 // These are only relevant when the first column contains spanning information.
8228 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8229 wxArrayInt
maxColWidths(m_colCount
);
8230 wxArrayInt
minColWidths(m_colCount
);
8232 wxSize
tableSize(tableWidth
, 0);
8236 for (i
= 0; i
< m_colCount
; i
++)
8238 absoluteColWidths
[i
] = 0;
8239 // absoluteColWidthsSpanning[i] = 0;
8240 percentageColWidths
[i
] = -1;
8241 // percentageColWidthsSpanning[i] = -1;
8243 maxColWidths
[i
] = 0;
8244 minColWidths
[i
] = 0;
8245 // columnSpans[i] = 1;
8248 // (0) Determine which cells are visible according to spans
8250 // __________________
8255 // |------------------|
8256 // |__________________| 4
8258 // To calculate cell visibility:
8259 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8260 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8261 // that cell, hide the cell.
8263 // We can also use this array to match the size of spanning cells to the grid. Or just do
8264 // this when we iterate through all cells.
8266 // 0.1: add spanning cells to an array
8267 wxRichTextRectArray rectArray
;
8268 for (j
= 0; j
< m_rowCount
; j
++)
8270 for (i
= 0; i
< m_colCount
; i
++)
8272 wxRichTextBox
* cell
= GetCell(j
, i
);
8273 int colSpan
= 1, rowSpan
= 1;
8274 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8275 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8276 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8277 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8278 if (colSpan
> 1 || rowSpan
> 1)
8280 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8284 // 0.2: find which cells are subsumed by a spanning cell
8285 for (j
= 0; j
< m_rowCount
; j
++)
8287 for (i
= 0; i
< m_colCount
; i
++)
8289 wxRichTextBox
* cell
= GetCell(j
, i
);
8290 if (rectArray
.GetCount() == 0)
8296 int colSpan
= 1, rowSpan
= 1;
8297 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8298 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8299 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8300 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8301 if (colSpan
> 1 || rowSpan
> 1)
8303 // Assume all spanning cells are shown
8309 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8311 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8323 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8324 // overlap with a spanned cell starting at a previous column position.
8325 // This means we need to keep an array of rects so we can check. However
8326 // it does also mean that some spans simply may not be taken into account
8327 // where there are different spans happening on different rows. In these cases,
8328 // they will simply be as wide as their constituent columns.
8330 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8331 // the absolute or percentage width of each column.
8333 for (j
= 0; j
< m_rowCount
; j
++)
8335 // First get the overall margins so we can calculate percentage widths based on
8336 // the available content space for all cells on the row
8338 int overallRowContentMargin
= 0;
8339 int visibleCellCount
= 0;
8341 for (i
= 0; i
< m_colCount
; i
++)
8343 wxRichTextBox
* cell
= GetCell(j
, i
);
8344 if (cell
->IsShown())
8346 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8347 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8349 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8350 visibleCellCount
++;
8354 // Add in inter-cell padding
8355 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8357 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8358 wxSize
rowTableSize(rowContentWidth
, 0);
8359 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8361 for (i
= 0; i
< m_colCount
; i
++)
8363 wxRichTextBox
* cell
= GetCell(j
, i
);
8364 if (cell
->IsShown())
8367 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8368 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8370 // Lay out cell to find min/max widths
8371 cell
->Invalidate(wxRICHTEXT_ALL
);
8372 cell
->Layout(dc
, availableSpace
, style
);
8376 int absoluteCellWidth
= -1;
8377 int percentageCellWidth
= -1;
8379 // I think we need to calculate percentages from the internal table size,
8380 // minus the padding between cells which we'll need to calculate from the
8381 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8382 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8383 // so if we want to conform to that we'll need to add in the overall cell margins.
8384 // However, this will make it difficult to specify percentages that add up to
8385 // 100% and still fit within the table width.
8386 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8387 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8388 // If we're using internal content size for the width, we would calculate the
8389 // the overall cell width for n cells as:
8390 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8391 // + thisOverallCellMargin
8392 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8393 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8395 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8397 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8398 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8400 percentageCellWidth
= w
;
8404 absoluteCellWidth
= w
;
8406 // Override absolute width with minimum width if necessary
8407 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8408 absoluteCellWidth
= cell
->GetMinSize().x
;
8411 if (absoluteCellWidth
!= -1)
8413 if (absoluteCellWidth
> absoluteColWidths
[i
])
8414 absoluteColWidths
[i
] = absoluteCellWidth
;
8417 if (percentageCellWidth
!= -1)
8419 if (percentageCellWidth
> percentageColWidths
[i
])
8420 percentageColWidths
[i
] = percentageCellWidth
;
8423 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8424 minColWidths
[i
] = cell
->GetMinSize().x
;
8425 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8426 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8432 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8433 // TODO: simply merge this into (1).
8434 for (i
= 0; i
< m_colCount
; i
++)
8436 if (absoluteColWidths
[i
] > 0)
8438 colWidths
[i
] = absoluteColWidths
[i
];
8440 else if (percentageColWidths
[i
] > 0)
8442 colWidths
[i
] = percentageColWidths
[i
];
8444 // This is rubbish - we calculated the absolute widths from percentages, so
8445 // we can't do it again here.
8446 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8450 // (3) Process absolute or proportional widths of spanning columns,
8451 // now that we know what our fixed column widths are going to be.
8452 // Spanned cells will try to adjust columns so the span will fit.
8453 // Even existing fixed column widths can be expanded if necessary.
8454 // Actually, currently fixed columns widths aren't adjusted; instead,
8455 // the algorithm favours earlier rows and adjusts unspecified column widths
8456 // the first time only. After that, we can't know whether the column has been
8457 // specified explicitly or not. (We could make a note if necessary.)
8458 for (j
= 0; j
< m_rowCount
; j
++)
8460 // First get the overall margins so we can calculate percentage widths based on
8461 // the available content space for all cells on the row
8463 int overallRowContentMargin
= 0;
8464 int visibleCellCount
= 0;
8466 for (i
= 0; i
< m_colCount
; i
++)
8468 wxRichTextBox
* cell
= GetCell(j
, i
);
8469 if (cell
->IsShown())
8471 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8472 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8474 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8475 visibleCellCount
++;
8479 // Add in inter-cell padding
8480 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8482 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8483 wxSize
rowTableSize(rowContentWidth
, 0);
8484 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8486 for (i
= 0; i
< m_colCount
; i
++)
8488 wxRichTextBox
* cell
= GetCell(j
, i
);
8489 if (cell
->IsShown())
8492 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8493 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8497 int spans
= wxMin(colSpan
, m_colCount
- i
);
8501 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8503 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8504 // Override absolute width with minimum width if necessary
8505 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8506 cellWidth
= cell
->GetMinSize().x
;
8510 // Do we want to do this? It's the only chance we get to
8511 // use the cell's min/max sizes, so we need to work out
8512 // how we're going to balance the unspecified spanning cell
8513 // width with the possibility more-constrained constituent cell widths.
8514 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8515 // don't want to constraint all the spanned columns to fit into this cell.
8516 // OK, let's say that if any of the constituent columns don't fit,
8517 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8518 // cells to the columns later.
8519 cellWidth
= cell
->GetMinSize().x
;
8520 if (cell
->GetMaxSize().x
> cellWidth
)
8521 cellWidth
= cell
->GetMaxSize().x
;
8524 // Subtract the padding between cells
8525 int spanningWidth
= cellWidth
;
8526 spanningWidth
-= paddingX
* (spans
-1);
8528 if (spanningWidth
> 0)
8530 // Now share the spanning width between columns within that span
8531 // TODO: take into account min widths of columns within the span
8532 int spanningWidthLeft
= spanningWidth
;
8533 int stretchColCount
= 0;
8534 for (k
= i
; k
< (i
+spans
); k
++)
8536 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8537 spanningWidthLeft
-= colWidths
[k
];
8541 // Now divide what's left between the remaining columns
8543 if (stretchColCount
> 0)
8544 colShare
= spanningWidthLeft
/ stretchColCount
;
8545 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8547 // If fixed-width columns are currently too big, then we'll later
8548 // stretch the spanned cell to fit.
8550 if (spanningWidthLeft
> 0)
8552 for (k
= i
; k
< (i
+spans
); k
++)
8554 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8556 int newWidth
= colShare
;
8557 if (k
== (i
+spans
-1))
8558 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8559 colWidths
[k
] = newWidth
;
8570 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8571 // TODO: take into account min widths of columns within the span
8572 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8573 int widthLeft
= tableWidthMinusPadding
;
8574 int stretchColCount
= 0;
8575 for (i
= 0; i
< m_colCount
; i
++)
8577 // TODO: we need to take into account min widths.
8578 // Subtract min width from width left, then
8579 // add the colShare to the min width
8580 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8581 widthLeft
-= colWidths
[i
];
8584 if (minColWidths
[i
] > 0)
8585 widthLeft
-= minColWidths
[i
];
8591 // Now divide what's left between the remaining columns
8593 if (stretchColCount
> 0)
8594 colShare
= widthLeft
/ stretchColCount
;
8595 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8597 // Check we don't have enough space, in which case shrink all columns, overriding
8598 // any absolute/proportional widths
8599 // TODO: actually we would like to divide up the shrinkage according to size.
8600 // How do we calculate the proportions that will achieve this?
8601 // Could first choose an arbitrary value for stretching cells, and then calculate
8602 // factors to multiply each width by.
8603 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8604 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8606 colShare
= tableWidthMinusPadding
/ m_colCount
;
8607 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8608 for (i
= 0; i
< m_colCount
; i
++)
8611 minColWidths
[i
] = 0;
8615 // We have to adjust the columns if either we need to shrink the
8616 // table to fit the parent/table width, or we explicitly set the
8617 // table width and need to stretch out the table.
8618 if (widthLeft
< 0 || stretchToFitTableWidth
)
8620 for (i
= 0; i
< m_colCount
; i
++)
8622 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8624 if (minColWidths
[i
] > 0)
8625 colWidths
[i
] = minColWidths
[i
] + colShare
;
8627 colWidths
[i
] = colShare
;
8628 if (i
== (m_colCount
-1))
8629 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8634 // TODO: if spanned cells have no specified or max width, make them the
8635 // as big as the columns they span. Do this for all spanned cells in all
8636 // rows, of course. Size any spanned cells left over at the end - even if they
8637 // have width > 0, make sure they're limited to the appropriate column edge.
8641 Sort out confusion between content width
8642 and overall width later. For now, assume we specify overall width.
8644 So, now we've laid out the table to fit into the given space
8645 and have used specified widths and minimum widths.
8647 Now we need to consider how we will try to take maximum width into account.
8651 // (??) TODO: take max width into account
8653 // (6) Lay out all cells again with the current values
8656 int y
= availableSpace
.y
;
8657 for (j
= 0; j
< m_rowCount
; j
++)
8659 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8660 int maxCellHeight
= 0;
8661 int maxSpecifiedCellHeight
= 0;
8663 wxArrayInt
actualWidths(m_colCount
);
8665 wxTextAttrDimensionConverter
converter(dc
, scale
);
8666 for (i
= 0; i
< m_colCount
; i
++)
8668 wxRichTextCell
* cell
= GetCell(j
, i
);
8669 if (cell
->IsShown())
8671 wxASSERT(colWidths
[i
] > 0);
8673 // Get max specified cell height
8674 // Don't handle percentages for height
8675 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8677 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8678 if (h
> maxSpecifiedCellHeight
)
8679 maxSpecifiedCellHeight
= h
;
8682 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8685 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8686 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8688 wxRect availableCellSpace
;
8690 // TODO: take into acount spans
8693 // Calculate the size of this spanning cell from its constituent columns
8695 int spans
= wxMin(colSpan
, m_colCount
- i
);
8696 for (k
= i
; k
< spans
; k
++)
8702 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8705 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8707 // Store actual width so we can force cell to be the appropriate width on the final loop
8708 actualWidths
[i
] = availableCellSpace
.GetWidth();
8711 cell
->Invalidate(wxRICHTEXT_ALL
);
8712 cell
->Layout(dc
, availableCellSpace
, style
);
8714 // TODO: use GetCachedSize().x to compute 'natural' size
8716 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8717 if (cell
->GetCachedSize().y
> maxCellHeight
)
8718 maxCellHeight
= cell
->GetCachedSize().y
;
8723 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8725 for (i
= 0; i
< m_colCount
; i
++)
8727 wxRichTextCell
* cell
= GetCell(j
, i
);
8728 if (cell
->IsShown())
8730 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8731 // Lay out cell with new height
8732 cell
->Invalidate(wxRICHTEXT_ALL
);
8733 cell
->Layout(dc
, availableCellSpace
, style
);
8735 // Make sure the cell size really is the appropriate size,
8736 // not the calculated box size
8737 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8739 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8744 if (j
< (m_rowCount
-1))
8748 // We need to add back the margins etc.
8750 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8751 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8752 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8753 SetCachedSize(marginRect
.GetSize());
8756 // TODO: calculate max size
8758 SetMaxSize(GetCachedSize());
8761 // TODO: calculate min size
8763 SetMinSize(GetCachedSize());
8766 // TODO: currently we use either a fixed table width or the parent's size.
8767 // We also want to be able to calculate the table width from its content,
8768 // whether using fixed column widths or cell content min/max width.
8769 // Probably need a boolean flag to say whether we need to stretch cells
8770 // to fit the table width, or to simply use min/max cell widths. The
8771 // trouble with this is that if cell widths are not specified, they
8772 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8773 // Anyway, ignoring that problem, we probably need to factor layout into a function
8774 // that can can calculate the maximum unconstrained layout in case table size is
8775 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8776 // constrain Layout(), or the previously-calculated max size to constraint layout.
8781 // Finds the absolute position and row height for the given character position
8782 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8784 wxRichTextCell
* child
= GetCell(index
+1);
8787 // Find the position at the start of the child cell, since the table doesn't
8788 // have any caret position of its own.
8789 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8795 // Get the cell at the given character position (in the range of the table).
8796 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8798 int row
= 0, col
= 0;
8799 if (GetCellRowColumnPosition(pos
, row
, col
))
8801 return GetCell(row
, col
);
8807 // Get the row/column for a given character position
8808 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8810 if (m_colCount
== 0 || m_rowCount
== 0)
8813 row
= (int) (pos
/ m_colCount
);
8814 col
= pos
- (row
* m_colCount
);
8816 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8818 if (row
< m_rowCount
&& col
< m_colCount
)
8824 // Calculate range, taking row/cell ordering into account instead of relying
8825 // on list ordering.
8826 void wxRichTextTable::CalculateRange(long start
, long& end
)
8828 long current
= start
;
8829 long lastEnd
= current
;
8838 for (i
= 0; i
< m_rowCount
; i
++)
8840 for (j
= 0; j
< m_colCount
; j
++)
8842 wxRichTextCell
* child
= GetCell(i
, j
);
8847 child
->CalculateRange(current
, childEnd
);
8850 current
= childEnd
+ 1;
8855 // A top-level object always has a range of size 1,
8856 // because its children don't count at this level.
8858 m_range
.SetRange(start
, start
);
8860 // An object with no children has zero length
8861 if (m_children
.GetCount() == 0)
8863 m_ownRange
.SetRange(0, lastEnd
);
8866 // Gets the range size.
8867 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8869 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
8872 // Deletes content in the given range.
8873 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
8875 // TODO: implement deletion of cells
8879 // Gets any text in this object for the given range.
8880 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
8882 return wxRichTextBox::GetTextForRange(range
);
8885 // Copies this object.
8886 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
8888 wxRichTextBox::Copy(obj
);
8892 m_rowCount
= obj
.m_rowCount
;
8893 m_colCount
= obj
.m_colCount
;
8895 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
8898 for (i
= 0; i
< m_rowCount
; i
++)
8900 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8901 for (j
= 0; j
< m_colCount
; j
++)
8903 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
8911 void wxRichTextTable::ClearTable()
8917 bool wxRichTextTable::CreateTable(int rows
, int cols
)
8924 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
8927 for (i
= 0; i
< rows
; i
++)
8929 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8930 for (j
= 0; j
< cols
; j
++)
8932 wxRichTextCell
* cell
= new wxRichTextCell
;
8934 cell
->AddParagraph(wxEmptyString
);
8943 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
8945 wxASSERT(row
< m_rowCount
);
8946 wxASSERT(col
< m_colCount
);
8948 if (row
< m_rowCount
&& col
< m_colCount
)
8950 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
8951 wxRichTextObject
* obj
= colArray
[col
];
8952 return wxDynamicCast(obj
, wxRichTextCell
);
8958 // Returns a selection object specifying the selections between start and end character positions.
8959 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8960 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
8962 wxRichTextSelection selection
;
8963 selection
.SetContainer((wxRichTextTable
*) this);
8972 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
8974 if (end
>= (m_colCount
* m_rowCount
))
8977 // We need to find the rectangle of cells that is described by the rectangle
8978 // with start, end as the diagonal. Make sure we don't add cells that are
8979 // not currenty visible because they are overlapped by spanning cells.
8981 --------------------------
8982 | 0 | 1 | 2 | 3 | 4 |
8983 --------------------------
8984 | 5 | 6 | 7 | 8 | 9 |
8985 --------------------------
8986 | 10 | 11 | 12 | 13 | 14 |
8987 --------------------------
8988 | 15 | 16 | 17 | 18 | 19 |
8989 --------------------------
8991 Let's say we select 6 -> 18.
8993 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8994 which is left and which is right.
8996 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8998 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9004 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9005 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9007 int topRow
= int(start
/m_colCount
);
9008 int bottomRow
= int(end
/m_colCount
);
9010 if (leftCol
> rightCol
)
9017 if (topRow
> bottomRow
)
9019 int tmp
= bottomRow
;
9025 for (i
= topRow
; i
<= bottomRow
; i
++)
9027 for (j
= leftCol
; j
<= rightCol
; j
++)
9029 wxRichTextCell
* cell
= GetCell(i
, j
);
9030 if (cell
&& cell
->IsShown())
9031 selection
.Add(cell
->GetRange());
9038 // Sets the attributes for the cells specified by the selection.
9039 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9041 if (selection
.GetContainer() != this)
9044 wxRichTextBuffer
* buffer
= GetBuffer();
9045 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9046 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9049 buffer
->BeginBatchUndo(_("Set Cell Style"));
9051 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9054 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9055 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9056 SetStyle(cell
, style
, flags
);
9057 node
= node
->GetNext();
9060 // Do action, or delay it until end of batch.
9062 buffer
->EndBatchUndo();
9067 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9069 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9070 if ((startRow
+ noRows
) >= m_rowCount
)
9074 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9076 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9077 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9079 wxRichTextObject
* cell
= colArray
[j
];
9080 RemoveChild(cell
, true);
9083 // Keep deleting at the same position, since we move all
9085 m_cells
.RemoveAt(startRow
);
9088 m_rowCount
= m_rowCount
- noRows
;
9093 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9095 wxASSERT((startCol
+ noCols
) < m_colCount
);
9096 if ((startCol
+ noCols
) >= m_colCount
)
9099 bool deleteRows
= (noCols
== m_colCount
);
9102 for (i
= 0; i
< m_rowCount
; i
++)
9104 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9105 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9107 wxRichTextObject
* cell
= colArray
[j
];
9108 RemoveChild(cell
, true);
9112 m_cells
.RemoveAt(0);
9117 m_colCount
= m_colCount
- noCols
;
9122 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9124 wxASSERT(startRow
<= m_rowCount
);
9125 if (startRow
> m_rowCount
)
9129 for (i
= 0; i
< noRows
; i
++)
9132 if (startRow
== m_rowCount
)
9134 m_cells
.Add(wxRichTextObjectPtrArray());
9135 idx
= m_cells
.GetCount() - 1;
9139 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9143 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9144 for (j
= 0; j
< m_colCount
; j
++)
9146 wxRichTextCell
* cell
= new wxRichTextCell
;
9147 cell
->GetAttributes() = attr
;
9154 m_rowCount
= m_rowCount
+ noRows
;
9158 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9160 wxASSERT(startCol
<= m_colCount
);
9161 if (startCol
> m_colCount
)
9165 for (i
= 0; i
< m_rowCount
; i
++)
9167 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9168 for (j
= 0; j
< noCols
; j
++)
9170 wxRichTextCell
* cell
= new wxRichTextCell
;
9171 cell
->GetAttributes() = attr
;
9175 if (startCol
== m_colCount
)
9178 colArray
.Insert(cell
, startCol
+j
);
9182 m_colCount
= m_colCount
+ noCols
;
9187 // Edit properties via a GUI
9188 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9190 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9191 boxDlg
.SetAttributes(GetAttributes());
9193 if (boxDlg
.ShowModal() == wxID_OK
)
9195 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9203 * Module to initialise and clean up handlers
9206 class wxRichTextModule
: public wxModule
9208 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9210 wxRichTextModule() {}
9213 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9214 wxRichTextBuffer::InitStandardHandlers();
9215 wxRichTextParagraph::InitDefaultTabs();
9220 wxRichTextBuffer::CleanUpHandlers();
9221 wxRichTextDecimalToRoman(-1);
9222 wxRichTextParagraph::ClearDefaultTabs();
9223 wxRichTextCtrl::ClearAvailableFontNames();
9224 wxRichTextBuffer::SetRenderer(NULL
);
9228 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9231 // If the richtext lib is dynamically loaded after the app has already started
9232 // (such as from wxPython) then the built-in module system will not init this
9233 // module. Provide this function to do it manually.
9234 void wxRichTextModuleInit()
9236 wxModule
* module = new wxRichTextModule
;
9238 wxModule::RegisterModule(module);
9243 * Commands for undo/redo
9247 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9248 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9250 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9253 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9257 wxRichTextCommand::~wxRichTextCommand()
9262 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9264 if (!m_actions
.Member(action
))
9265 m_actions
.Append(action
);
9268 bool wxRichTextCommand::Do()
9270 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9272 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9279 bool wxRichTextCommand::Undo()
9281 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9283 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9290 void wxRichTextCommand::ClearActions()
9292 WX_CLEAR_LIST(wxList
, m_actions
);
9300 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9301 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9302 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9306 m_containerAddress
.Create(buffer
, container
);
9307 m_ignoreThis
= ignoreFirstTime
;
9312 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9313 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9315 cmd
->AddAction(this);
9318 wxRichTextAction::~wxRichTextAction()
9324 // Returns the container that this action refers to, using the container address and top-level buffer.
9325 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9327 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9332 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9334 // Store a list of line start character and y positions so we can figure out which area
9335 // we need to refresh
9337 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9338 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9339 wxASSERT(container
!= NULL
);
9343 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9344 // If we had several actions, which only invalidate and leave layout until the
9345 // paint handler is called, then this might not be true. So we may need to switch
9346 // optimisation on only when we're simply adding text and not simultaneously
9347 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9348 // first, but of course this means we'll be doing it twice.
9349 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9351 wxSize clientSize
= m_ctrl
->GetClientSize();
9352 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9353 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9355 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9356 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9359 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9360 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9363 wxRichTextLine
* line
= node2
->GetData();
9364 wxPoint pt
= line
->GetAbsolutePosition();
9365 wxRichTextRange range
= line
->GetAbsoluteRange();
9369 node2
= wxRichTextLineList::compatibility_iterator();
9370 node
= wxRichTextObjectList::compatibility_iterator();
9372 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9374 optimizationLineCharPositions
.Add(range
.GetStart());
9375 optimizationLineYPositions
.Add(pt
.y
);
9379 node2
= node2
->GetNext();
9383 node
= node
->GetNext();
9389 bool wxRichTextAction::Do()
9391 m_buffer
->Modify(true);
9393 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9394 wxASSERT(container
!= NULL
);
9400 case wxRICHTEXT_INSERT
:
9402 // Store a list of line start character and y positions so we can figure out which area
9403 // we need to refresh
9404 wxArrayInt optimizationLineCharPositions
;
9405 wxArrayInt optimizationLineYPositions
;
9407 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9408 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9411 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9412 container
->UpdateRanges();
9414 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9415 // Layout() would stop prematurely at the top level.
9416 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9418 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9420 // Character position to caret position
9421 newCaretPosition
--;
9423 // Don't take into account the last newline
9424 if (m_newParagraphs
.GetPartialParagraph())
9425 newCaretPosition
--;
9427 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9429 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9430 if (p
->GetRange().GetLength() == 1)
9431 newCaretPosition
--;
9434 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9436 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9438 wxRichTextEvent
cmdEvent(
9439 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9440 m_ctrl
? m_ctrl
->GetId() : -1);
9441 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9442 cmdEvent
.SetRange(GetRange());
9443 cmdEvent
.SetPosition(GetRange().GetStart());
9444 cmdEvent
.SetContainer(container
);
9446 m_buffer
->SendEvent(cmdEvent
);
9450 case wxRICHTEXT_DELETE
:
9452 wxArrayInt optimizationLineCharPositions
;
9453 wxArrayInt optimizationLineYPositions
;
9455 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9456 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9459 container
->DeleteRange(GetRange());
9460 container
->UpdateRanges();
9461 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9462 // Layout() would stop prematurely at the top level.
9463 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9465 long caretPos
= GetRange().GetStart()-1;
9466 if (caretPos
>= container
->GetOwnRange().GetEnd())
9469 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9471 wxRichTextEvent
cmdEvent(
9472 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9473 m_ctrl
? m_ctrl
->GetId() : -1);
9474 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9475 cmdEvent
.SetRange(GetRange());
9476 cmdEvent
.SetPosition(GetRange().GetStart());
9477 cmdEvent
.SetContainer(container
);
9479 m_buffer
->SendEvent(cmdEvent
);
9483 case wxRICHTEXT_CHANGE_STYLE
:
9485 ApplyParagraphs(GetNewParagraphs());
9487 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9488 // Layout() would stop prematurely at the top level.
9489 container
->InvalidateHierarchy(GetRange());
9491 UpdateAppearance(GetPosition());
9493 wxRichTextEvent
cmdEvent(
9494 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9495 m_ctrl
? m_ctrl
->GetId() : -1);
9496 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9497 cmdEvent
.SetRange(GetRange());
9498 cmdEvent
.SetPosition(GetRange().GetStart());
9499 cmdEvent
.SetContainer(container
);
9501 m_buffer
->SendEvent(cmdEvent
);
9505 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9507 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9510 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9511 obj
->GetAttributes() = m_attributes
;
9512 m_attributes
= oldAttr
;
9515 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9516 // Layout() would stop prematurely at the top level.
9517 container
->InvalidateHierarchy(GetRange());
9519 UpdateAppearance(GetPosition());
9521 wxRichTextEvent
cmdEvent(
9522 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9523 m_ctrl
? m_ctrl
->GetId() : -1);
9524 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9525 cmdEvent
.SetRange(GetRange());
9526 cmdEvent
.SetPosition(GetRange().GetStart());
9527 cmdEvent
.SetContainer(container
);
9529 m_buffer
->SendEvent(cmdEvent
);
9533 case wxRICHTEXT_CHANGE_OBJECT
:
9535 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9536 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9537 if (obj
&& m_object
)
9539 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9542 wxRichTextObject
* obj
= node
->GetData();
9543 node
->SetData(m_object
);
9548 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9549 // Layout() would stop prematurely at the top level.
9550 container
->InvalidateHierarchy(GetRange());
9552 UpdateAppearance(GetPosition());
9554 // TODO: send new kind of modification event
9565 bool wxRichTextAction::Undo()
9567 m_buffer
->Modify(true);
9569 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9570 wxASSERT(container
!= NULL
);
9576 case wxRICHTEXT_INSERT
:
9578 wxArrayInt optimizationLineCharPositions
;
9579 wxArrayInt optimizationLineYPositions
;
9581 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9582 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9585 container
->DeleteRange(GetRange());
9586 container
->UpdateRanges();
9587 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9588 // Layout() would stop prematurely at the top level.
9589 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9591 long newCaretPosition
= GetPosition() - 1;
9593 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9595 wxRichTextEvent
cmdEvent(
9596 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9597 m_ctrl
? m_ctrl
->GetId() : -1);
9598 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9599 cmdEvent
.SetRange(GetRange());
9600 cmdEvent
.SetPosition(GetRange().GetStart());
9601 cmdEvent
.SetContainer(container
);
9603 m_buffer
->SendEvent(cmdEvent
);
9607 case wxRICHTEXT_DELETE
:
9609 wxArrayInt optimizationLineCharPositions
;
9610 wxArrayInt optimizationLineYPositions
;
9612 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9613 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9616 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9617 container
->UpdateRanges();
9618 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9619 // Layout() would stop prematurely at the top level.
9620 container
->InvalidateHierarchy(GetRange());
9622 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9624 wxRichTextEvent
cmdEvent(
9625 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9626 m_ctrl
? m_ctrl
->GetId() : -1);
9627 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9628 cmdEvent
.SetRange(GetRange());
9629 cmdEvent
.SetPosition(GetRange().GetStart());
9630 cmdEvent
.SetContainer(container
);
9632 m_buffer
->SendEvent(cmdEvent
);
9636 case wxRICHTEXT_CHANGE_STYLE
:
9638 ApplyParagraphs(GetOldParagraphs());
9639 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9640 // Layout() would stop prematurely at the top level.
9641 container
->InvalidateHierarchy(GetRange());
9643 UpdateAppearance(GetPosition());
9645 wxRichTextEvent
cmdEvent(
9646 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9647 m_ctrl
? m_ctrl
->GetId() : -1);
9648 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9649 cmdEvent
.SetRange(GetRange());
9650 cmdEvent
.SetPosition(GetRange().GetStart());
9651 cmdEvent
.SetContainer(container
);
9653 m_buffer
->SendEvent(cmdEvent
);
9657 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9658 case wxRICHTEXT_CHANGE_OBJECT
:
9669 /// Update the control appearance
9670 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9672 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9673 wxASSERT(container
!= NULL
);
9679 m_ctrl
->SetFocusObject(container
);
9680 m_ctrl
->SetCaretPosition(caretPosition
);
9682 if (!m_ctrl
->IsFrozen())
9684 wxRect containerRect
= container
->GetRect();
9686 m_ctrl
->LayoutContent();
9688 // Refresh everything if there were floating objects or the container changed size
9689 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9690 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9692 m_ctrl
->Refresh(false);
9696 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9697 // Find refresh rectangle if we are in a position to optimise refresh
9698 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9702 wxSize clientSize
= m_ctrl
->GetClientSize();
9703 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9705 // Start/end positions
9707 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9709 bool foundEnd
= false;
9711 // position offset - how many characters were inserted
9712 int positionOffset
= GetRange().GetLength();
9714 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9715 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9716 positionOffset
= - positionOffset
;
9718 // find the first line which is being drawn at the same position as it was
9719 // before. Since we're talking about a simple insertion, we can assume
9720 // that the rest of the window does not need to be redrawn.
9722 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9723 // Since we support floating layout, we should redraw the whole para instead of just
9724 // the first line touching the invalid range.
9727 firstY
= para
->GetPosition().y
;
9730 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9733 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9734 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9737 wxRichTextLine
* line
= node2
->GetData();
9738 wxPoint pt
= line
->GetAbsolutePosition();
9739 wxRichTextRange range
= line
->GetAbsoluteRange();
9741 // we want to find the first line that is in the same position
9742 // as before. This will mean we're at the end of the changed text.
9744 if (pt
.y
> lastY
) // going past the end of the window, no more info
9746 node2
= wxRichTextLineList::compatibility_iterator();
9747 node
= wxRichTextObjectList::compatibility_iterator();
9749 // Detect last line in the buffer
9750 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9752 // If deleting text, make sure we refresh below as well as above
9753 if (positionOffset
>= 0)
9756 lastY
= pt
.y
+ line
->GetSize().y
;
9759 node2
= wxRichTextLineList::compatibility_iterator();
9760 node
= wxRichTextObjectList::compatibility_iterator();
9766 // search for this line being at the same position as before
9767 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9769 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9770 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9772 // Stop, we're now the same as we were
9777 node2
= wxRichTextLineList::compatibility_iterator();
9778 node
= wxRichTextObjectList::compatibility_iterator();
9786 node2
= node2
->GetNext();
9790 node
= node
->GetNext();
9793 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9795 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9797 // Convert to device coordinates
9798 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9799 m_ctrl
->RefreshRect(rect
);
9803 m_ctrl
->Refresh(false);
9805 m_ctrl
->PositionCaret();
9806 m_ctrl
->SetDefaultStyleToCursorStyle();
9808 if (sendUpdateEvent
)
9809 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9814 /// Replace the buffer paragraphs with the new ones.
9815 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9817 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9818 wxASSERT(container
!= NULL
);
9822 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9825 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9826 wxASSERT (para
!= NULL
);
9828 // We'll replace the existing paragraph by finding the paragraph at this position,
9829 // delete its node data, and setting a copy as the new node data.
9830 // TODO: make more efficient by simply swapping old and new paragraph objects.
9832 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9835 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9838 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9839 newPara
->SetParent(container
);
9841 bufferParaNode
->SetData(newPara
);
9843 delete existingPara
;
9847 node
= node
->GetNext();
9854 * This stores beginning and end positions for a range of data.
9857 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
9859 /// Limit this range to be within 'range'
9860 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
9862 if (m_start
< range
.m_start
)
9863 m_start
= range
.m_start
;
9865 if (m_end
> range
.m_end
)
9866 m_end
= range
.m_end
;
9872 * wxRichTextImage implementation
9873 * This object represents an image.
9876 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
9878 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9879 wxRichTextObject(parent
)
9881 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
9883 SetAttributes(*charStyle
);
9886 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9887 wxRichTextObject(parent
)
9889 m_imageBlock
= imageBlock
;
9891 SetAttributes(*charStyle
);
9894 /// Create a cached image at the required size
9895 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
9897 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9899 if (!m_imageBlock
.IsOk())
9903 m_imageBlock
.Load(image
);
9907 int width
= image
.GetWidth();
9908 int height
= image
.GetHeight();
9910 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9912 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9913 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9915 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9917 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9919 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9920 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9922 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9925 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
9926 m_imageCache
= wxBitmap(image
);
9929 // If the original width and height is small, e.g. 400 or below,
9930 // scale up and then down to improve image quality. This can make
9931 // a big difference, with not much performance hit.
9932 int upscaleThreshold
= 400;
9934 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
9936 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
9937 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9940 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9941 m_imageCache
= wxBitmap(img
);
9945 return m_imageCache
.IsOk();
9949 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
9954 // Don't need cached size AFAIK
9955 // wxSize size = GetCachedSize();
9956 if (!LoadImageCache(dc
))
9959 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9962 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
9964 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
9967 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9968 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9969 marginRect
= rect
; // outer rectangle, will calculate contentRect
9970 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9972 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
9974 if (selection
.WithinSelection(range
.GetStart(), this))
9976 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9977 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9978 dc
.SetLogicalFunction(wxINVERT
);
9979 dc
.DrawRectangle(contentRect
);
9980 dc
.SetLogicalFunction(wxCOPY
);
9986 /// Lay the item out
9987 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(style
))
9989 if (!LoadImageCache(dc
))
9992 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9993 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9994 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9995 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9997 wxSize overallSize
= marginRect
.GetSize();
9999 SetCachedSize(overallSize
);
10000 SetMaxSize(overallSize
);
10001 SetMinSize(overallSize
);
10002 SetPosition(rect
.GetPosition());
10007 /// Get/set the object size for the given range. Returns false if the range
10008 /// is invalid for this object.
10009 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10011 if (!range
.IsWithin(GetRange()))
10014 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10016 size
.x
= 0; size
.y
= 0;
10017 if (partialExtents
)
10018 partialExtents
->Add(0);
10022 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10023 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10024 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10025 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10027 wxSize overallSize
= marginRect
.GetSize();
10029 if (partialExtents
)
10030 partialExtents
->Add(overallSize
.x
);
10032 size
= overallSize
;
10037 // Get the 'natural' size for an object. For an image, it would be the
10039 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10041 wxTextAttrSize size
;
10042 if (GetImageCache().IsOk())
10044 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10045 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10052 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10054 wxRichTextObject::Copy(obj
);
10056 m_imageBlock
= obj
.m_imageBlock
;
10059 /// Edit properties via a GUI
10060 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10062 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10063 imageDlg
.SetAttributes(GetAttributes());
10065 if (imageDlg
.ShowModal() == wxID_OK
)
10067 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10068 // indeterminate in the object.
10069 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10081 /// Compare two attribute objects
10082 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10084 return (attr1
== attr2
);
10087 // Partial equality test taking flags into account
10088 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10090 return attr1
.EqPartial(attr2
);
10094 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10096 if (tabs1
.GetCount() != tabs2
.GetCount())
10100 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10102 if (tabs1
[i
] != tabs2
[i
])
10108 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10110 return destStyle
.Apply(style
, compareWith
);
10113 // Remove attributes
10114 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10116 return destStyle
.RemoveStyle(style
);
10119 /// Combine two bitlists, specifying the bits of interest with separate flags.
10120 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10122 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10125 /// Compare two bitlists
10126 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10128 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10131 /// Split into paragraph and character styles
10132 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10134 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10137 /// Convert a decimal to Roman numerals
10138 wxString
wxRichTextDecimalToRoman(long n
)
10140 static wxArrayInt decimalNumbers
;
10141 static wxArrayString romanNumbers
;
10146 decimalNumbers
.Clear();
10147 romanNumbers
.Clear();
10148 return wxEmptyString
;
10151 if (decimalNumbers
.GetCount() == 0)
10153 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10155 wxRichTextAddDecRom(1000, wxT("M"));
10156 wxRichTextAddDecRom(900, wxT("CM"));
10157 wxRichTextAddDecRom(500, wxT("D"));
10158 wxRichTextAddDecRom(400, wxT("CD"));
10159 wxRichTextAddDecRom(100, wxT("C"));
10160 wxRichTextAddDecRom(90, wxT("XC"));
10161 wxRichTextAddDecRom(50, wxT("L"));
10162 wxRichTextAddDecRom(40, wxT("XL"));
10163 wxRichTextAddDecRom(10, wxT("X"));
10164 wxRichTextAddDecRom(9, wxT("IX"));
10165 wxRichTextAddDecRom(5, wxT("V"));
10166 wxRichTextAddDecRom(4, wxT("IV"));
10167 wxRichTextAddDecRom(1, wxT("I"));
10173 while (n
> 0 && i
< 13)
10175 if (n
>= decimalNumbers
[i
])
10177 n
-= decimalNumbers
[i
];
10178 roman
+= romanNumbers
[i
];
10185 if (roman
.IsEmpty())
10191 * wxRichTextFileHandler
10192 * Base class for file handlers
10195 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10197 #if wxUSE_FFILE && wxUSE_STREAMS
10198 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10200 wxFFileInputStream
stream(filename
);
10202 return LoadFile(buffer
, stream
);
10207 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10209 wxFFileOutputStream
stream(filename
);
10211 return SaveFile(buffer
, stream
);
10215 #endif // wxUSE_FFILE && wxUSE_STREAMS
10217 /// Can we handle this filename (if using files)? By default, checks the extension.
10218 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10220 wxString path
, file
, ext
;
10221 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10223 return (ext
.Lower() == GetExtension());
10227 * wxRichTextTextHandler
10228 * Plain text handler
10231 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10234 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10236 if (!stream
.IsOk())
10242 while (!stream
.Eof())
10244 int ch
= stream
.GetC();
10248 if (ch
== 10 && lastCh
!= 13)
10251 if (ch
> 0 && ch
!= 10)
10258 buffer
->ResetAndClearCommands();
10260 buffer
->AddParagraphs(str
);
10261 buffer
->UpdateRanges();
10266 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10268 if (!stream
.IsOk())
10271 wxString text
= buffer
->GetText();
10273 wxString newLine
= wxRichTextLineBreakChar
;
10274 text
.Replace(newLine
, wxT("\n"));
10276 wxCharBuffer buf
= text
.ToAscii();
10278 stream
.Write((const char*) buf
, text
.length());
10281 #endif // wxUSE_STREAMS
10284 * Stores information about an image, in binary in-memory form
10287 wxRichTextImageBlock::wxRichTextImageBlock()
10292 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10298 wxRichTextImageBlock::~wxRichTextImageBlock()
10303 void wxRichTextImageBlock::Init()
10307 m_imageType
= wxBITMAP_TYPE_INVALID
;
10310 void wxRichTextImageBlock::Clear()
10314 m_imageType
= wxBITMAP_TYPE_INVALID
;
10318 // Load the original image into a memory block.
10319 // If the image is not a JPEG, we must convert it into a JPEG
10320 // to conserve space.
10321 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10322 // load the image a 2nd time.
10324 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10325 wxImage
& image
, bool convertToJPEG
)
10327 m_imageType
= imageType
;
10329 wxString
filenameToRead(filename
);
10330 bool removeFile
= false;
10332 if (imageType
== wxBITMAP_TYPE_INVALID
)
10333 return false; // Could not determine image type
10335 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10337 wxString tempFile
=
10338 wxFileName::CreateTempFileName(_("image"));
10340 wxASSERT(!tempFile
.IsEmpty());
10342 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10343 filenameToRead
= tempFile
;
10346 m_imageType
= wxBITMAP_TYPE_JPEG
;
10349 if (!file
.Open(filenameToRead
))
10352 m_dataSize
= (size_t) file
.Length();
10357 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10360 wxRemoveFile(filenameToRead
);
10362 return (m_data
!= NULL
);
10365 // Make an image block from the wxImage in the given
10367 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10369 image
.SetOption(wxT("quality"), quality
);
10371 if (imageType
== wxBITMAP_TYPE_INVALID
)
10372 return false; // Could not determine image type
10374 return DoMakeImageBlock(image
, imageType
);
10377 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10378 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10380 if (imageType
== wxBITMAP_TYPE_INVALID
)
10381 return false; // Could not determine image type
10383 return DoMakeImageBlock(image
, imageType
);
10386 // Makes the image block
10387 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10389 wxMemoryOutputStream memStream
;
10390 if (!image
.SaveFile(memStream
, imageType
))
10395 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10403 m_imageType
= imageType
;
10404 m_dataSize
= memStream
.GetSize();
10406 memStream
.CopyTo(m_data
, m_dataSize
);
10408 return (m_data
!= NULL
);
10412 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10414 return WriteBlock(filename
, m_data
, m_dataSize
);
10417 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10419 m_imageType
= block
.m_imageType
;
10421 m_dataSize
= block
.m_dataSize
;
10422 if (m_dataSize
== 0)
10425 m_data
= new unsigned char[m_dataSize
];
10427 for (i
= 0; i
< m_dataSize
; i
++)
10428 m_data
[i
] = block
.m_data
[i
];
10432 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10437 // Load a wxImage from the block
10438 bool wxRichTextImageBlock::Load(wxImage
& image
)
10443 // Read in the image.
10445 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10446 bool success
= image
.LoadFile(mstream
, GetImageType());
10448 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10449 wxASSERT(!tempFile
.IsEmpty());
10451 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10455 success
= image
.LoadFile(tempFile
, GetImageType());
10456 wxRemoveFile(tempFile
);
10462 // Write data in hex to a stream
10463 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10465 const int bufSize
= 512;
10466 char buf
[bufSize
+1];
10468 int left
= m_dataSize
;
10473 if (left
*2 > bufSize
)
10475 n
= bufSize
; left
-= (bufSize
/2);
10479 n
= left
*2; left
= 0;
10483 for (i
= 0; i
< (n
/2); i
++)
10485 wxDecToHex(m_data
[j
], b
, b
+1);
10490 stream
.Write((const char*) buf
, n
);
10495 // Read data in hex from a stream
10496 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10498 int dataSize
= length
/2;
10503 // create a null terminated temporary string:
10507 m_data
= new unsigned char[dataSize
];
10509 for (i
= 0; i
< dataSize
; i
++)
10511 str
[0] = (char)stream
.GetC();
10512 str
[1] = (char)stream
.GetC();
10514 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10517 m_dataSize
= dataSize
;
10518 m_imageType
= imageType
;
10523 // Allocate and read from stream as a block of memory
10524 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10526 unsigned char* block
= new unsigned char[size
];
10530 stream
.Read(block
, size
);
10535 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10537 wxFileInputStream
stream(filename
);
10541 return ReadBlock(stream
, size
);
10544 // Write memory block to stream
10545 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10547 stream
.Write((void*) block
, size
);
10548 return stream
.IsOk();
10552 // Write memory block to file
10553 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10555 wxFileOutputStream
outStream(filename
);
10556 if (!outStream
.Ok())
10559 return WriteBlock(outStream
, block
, size
);
10562 // Gets the extension for the block's type
10563 wxString
wxRichTextImageBlock::GetExtension() const
10565 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10567 return handler
->GetExtension();
10569 return wxEmptyString
;
10575 * The data object for a wxRichTextBuffer
10578 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10580 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10582 m_richTextBuffer
= richTextBuffer
;
10584 // this string should uniquely identify our format, but is otherwise
10586 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10588 SetFormat(m_formatRichTextBuffer
);
10591 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10593 delete m_richTextBuffer
;
10596 // after a call to this function, the richTextBuffer is owned by the caller and it
10597 // is responsible for deleting it!
10598 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10600 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10601 m_richTextBuffer
= NULL
;
10603 return richTextBuffer
;
10606 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10608 return m_formatRichTextBuffer
;
10611 size_t wxRichTextBufferDataObject::GetDataSize() const
10613 if (!m_richTextBuffer
)
10619 wxStringOutputStream
stream(& bufXML
);
10620 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10622 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10628 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10629 return strlen(buffer
) + 1;
10631 return bufXML
.Length()+1;
10635 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10637 if (!pBuf
|| !m_richTextBuffer
)
10643 wxStringOutputStream
stream(& bufXML
);
10644 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10646 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10652 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10653 size_t len
= strlen(buffer
);
10654 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10655 ((char*) pBuf
)[len
] = 0;
10657 size_t len
= bufXML
.Length();
10658 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10659 ((char*) pBuf
)[len
] = 0;
10665 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10667 wxDELETE(m_richTextBuffer
);
10669 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10671 m_richTextBuffer
= new wxRichTextBuffer
;
10673 wxStringInputStream
stream(bufXML
);
10674 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10676 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10678 wxDELETE(m_richTextBuffer
);
10690 * wxRichTextFontTable
10691 * Manages quick access to a pool of fonts for rendering rich text
10694 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10696 class wxRichTextFontTableData
: public wxObjectRefData
10699 wxRichTextFontTableData() {}
10701 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10703 wxRichTextFontTableHashMap m_hashMap
;
10706 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10708 wxString
facename(fontSpec
.GetFontFaceName());
10709 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()));
10710 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10712 if ( entry
== m_hashMap
.end() )
10714 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10715 m_hashMap
[spec
] = font
;
10720 return entry
->second
;
10724 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10726 wxRichTextFontTable::wxRichTextFontTable()
10728 m_refData
= new wxRichTextFontTableData
;
10731 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10737 wxRichTextFontTable::~wxRichTextFontTable()
10742 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10744 return (m_refData
== table
.m_refData
);
10747 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10752 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10754 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10756 return data
->FindFont(fontSpec
);
10761 void wxRichTextFontTable::Clear()
10763 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10765 data
->m_hashMap
.clear();
10771 void wxTextBoxAttr::Reset()
10774 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10775 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10776 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10777 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10781 m_position
.Reset();
10790 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10793 m_flags
== attr
.m_flags
&&
10794 m_floatMode
== attr
.m_floatMode
&&
10795 m_clearMode
== attr
.m_clearMode
&&
10796 m_collapseMode
== attr
.m_collapseMode
&&
10797 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10799 m_margins
== attr
.m_margins
&&
10800 m_padding
== attr
.m_padding
&&
10801 m_position
== attr
.m_position
&&
10803 m_size
== attr
.m_size
&&
10805 m_border
== attr
.m_border
&&
10806 m_outline
== attr
.m_outline
10810 // Partial equality test
10811 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10813 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10816 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10819 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10822 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10827 if (!m_position
.EqPartial(attr
.m_position
))
10832 if (!m_margins
.EqPartial(attr
.m_margins
))
10837 if (!m_padding
.EqPartial(attr
.m_padding
))
10842 if (!GetBorder().EqPartial(attr
.GetBorder()))
10847 if (!GetOutline().EqPartial(attr
.GetOutline()))
10853 // Merges the given attributes. If compareWith
10854 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10855 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10856 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
10858 if (attr
.HasFloatMode())
10860 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
10861 SetFloatMode(attr
.GetFloatMode());
10864 if (attr
.HasClearMode())
10866 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
10867 SetClearMode(attr
.GetClearMode());
10870 if (attr
.HasCollapseBorders())
10872 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
10873 SetCollapseBorders(attr
.GetCollapseBorders());
10876 if (attr
.HasVerticalAlignment())
10878 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
10879 SetVerticalAlignment(attr
.GetVerticalAlignment());
10882 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
10883 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
10884 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
10886 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
10888 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
10889 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
10894 // Remove specified attributes from this object
10895 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
10897 if (attr
.HasFloatMode())
10898 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10900 if (attr
.HasClearMode())
10901 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10903 if (attr
.HasCollapseBorders())
10904 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10906 if (attr
.HasVerticalAlignment())
10907 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10909 m_margins
.RemoveStyle(attr
.m_margins
);
10910 m_padding
.RemoveStyle(attr
.m_padding
);
10911 m_position
.RemoveStyle(attr
.m_position
);
10913 m_size
.RemoveStyle(attr
.m_size
);
10915 m_border
.RemoveStyle(attr
.m_border
);
10916 m_outline
.RemoveStyle(attr
.m_outline
);
10921 // Collects the attributes that are common to a range of content, building up a note of
10922 // which attributes are absent in some objects and which clash in some objects.
10923 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
10925 if (attr
.HasFloatMode())
10927 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
10929 if (HasFloatMode())
10931 if (GetFloatMode() != attr
.GetFloatMode())
10933 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10934 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10938 SetFloatMode(attr
.GetFloatMode());
10942 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10944 if (attr
.HasClearMode())
10946 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
10948 if (HasClearMode())
10950 if (GetClearMode() != attr
.GetClearMode())
10952 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10953 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10957 SetClearMode(attr
.GetClearMode());
10961 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10963 if (attr
.HasCollapseBorders())
10965 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
10967 if (HasCollapseBorders())
10969 if (GetCollapseBorders() != attr
.GetCollapseBorders())
10971 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10972 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10976 SetCollapseBorders(attr
.GetCollapseBorders());
10980 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10982 if (attr
.HasVerticalAlignment())
10984 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
10986 if (HasVerticalAlignment())
10988 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
10990 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10991 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10995 SetVerticalAlignment(attr
.GetVerticalAlignment());
10999 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11001 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11002 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11003 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11005 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11007 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11008 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11013 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11015 wxTextAttr::Copy(attr
);
11017 m_textBoxAttr
= attr
.m_textBoxAttr
;
11020 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11022 if (!(wxTextAttr::operator==(attr
)))
11025 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11028 // Partial equality test taking comparison object into account
11029 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11031 if (!(wxTextAttr::EqPartial(attr
)))
11034 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11037 // Merges the given attributes. If compareWith
11038 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11039 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11040 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11042 wxTextAttr::Apply(style
, compareWith
);
11044 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11047 // Remove specified attributes from this object
11048 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11050 wxTextAttr::RemoveStyle(*this, attr
);
11052 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11055 // Collects the attributes that are common to a range of content, building up a note of
11056 // which attributes are absent in some objects and which clash in some objects.
11057 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11059 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11061 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11064 // Partial equality test
11065 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11067 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11070 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11073 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11079 // Apply border to 'this', but not if the same as compareWith
11080 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11082 if (border
.HasStyle())
11084 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11085 SetStyle(border
.GetStyle());
11087 if (border
.HasColour())
11089 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11090 SetColour(border
.GetColourLong());
11092 if (border
.HasWidth())
11094 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11095 SetWidth(border
.GetWidth());
11101 // Remove specified attributes from this object
11102 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11104 if (attr
.HasStyle() && HasStyle())
11105 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11106 if (attr
.HasColour() && HasColour())
11107 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11108 if (attr
.HasWidth() && HasWidth())
11109 m_borderWidth
.Reset();
11114 // Collects the attributes that are common to a range of content, building up a note of
11115 // which attributes are absent in some objects and which clash in some objects.
11116 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11118 if (attr
.HasStyle())
11120 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11124 if (GetStyle() != attr
.GetStyle())
11126 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11127 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11131 SetStyle(attr
.GetStyle());
11135 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11137 if (attr
.HasColour())
11139 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11143 if (GetColour() != attr
.GetColour())
11145 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11146 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11150 SetColour(attr
.GetColourLong());
11154 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11156 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11159 // Partial equality test
11160 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11162 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11163 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11166 // Apply border to 'this', but not if the same as compareWith
11167 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11169 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11170 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11171 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11172 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11176 // Remove specified attributes from this object
11177 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11179 m_left
.RemoveStyle(attr
.m_left
);
11180 m_right
.RemoveStyle(attr
.m_right
);
11181 m_top
.RemoveStyle(attr
.m_top
);
11182 m_bottom
.RemoveStyle(attr
.m_bottom
);
11186 // Collects the attributes that are common to a range of content, building up a note of
11187 // which attributes are absent in some objects and which clash in some objects.
11188 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11190 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11191 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11192 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11193 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11196 // Set style of all borders
11197 void wxTextAttrBorders::SetStyle(int style
)
11199 m_left
.SetStyle(style
);
11200 m_right
.SetStyle(style
);
11201 m_top
.SetStyle(style
);
11202 m_bottom
.SetStyle(style
);
11205 // Set colour of all borders
11206 void wxTextAttrBorders::SetColour(unsigned long colour
)
11208 m_left
.SetColour(colour
);
11209 m_right
.SetColour(colour
);
11210 m_top
.SetColour(colour
);
11211 m_bottom
.SetColour(colour
);
11214 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11216 m_left
.SetColour(colour
);
11217 m_right
.SetColour(colour
);
11218 m_top
.SetColour(colour
);
11219 m_bottom
.SetColour(colour
);
11222 // Set width of all borders
11223 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11225 m_left
.SetWidth(width
);
11226 m_right
.SetWidth(width
);
11227 m_top
.SetWidth(width
);
11228 m_bottom
.SetWidth(width
);
11231 // Partial equality test
11232 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11234 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11240 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11244 if (!(compareWith
&& dim
== (*compareWith
)))
11251 // Collects the attributes that are common to a range of content, building up a note of
11252 // which attributes are absent in some objects and which clash in some objects.
11253 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11255 if (attr
.IsValid())
11257 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11261 if (!((*this) == attr
))
11263 clashingAttr
.SetValid(true);
11272 absentAttr
.SetValid(true);
11275 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11277 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11280 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11282 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11285 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11287 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11290 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11292 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11295 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11297 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11298 return ConvertTenthsMMToPixels(dim
.GetValue());
11299 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11300 return dim
.GetValue();
11301 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11303 wxASSERT(m_parentSize
!= wxDefaultSize
);
11304 if (direction
== wxHORIZONTAL
)
11305 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11307 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11316 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11318 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11319 return dim
.GetValue();
11320 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11321 return ConvertPixelsToTenthsMM(dim
.GetValue());
11329 // Partial equality test
11330 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11332 if (!m_left
.EqPartial(dims
.m_left
))
11335 if (!m_right
.EqPartial(dims
.m_right
))
11338 if (!m_top
.EqPartial(dims
.m_top
))
11341 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11347 // Apply border to 'this', but not if the same as compareWith
11348 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11350 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11351 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11352 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11353 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11358 // Remove specified attributes from this object
11359 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11361 if (attr
.m_left
.IsValid())
11363 if (attr
.m_right
.IsValid())
11365 if (attr
.m_top
.IsValid())
11367 if (attr
.m_bottom
.IsValid())
11373 // Collects the attributes that are common to a range of content, building up a note of
11374 // which attributes are absent in some objects and which clash in some objects.
11375 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11377 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11378 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11379 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11380 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11383 // Partial equality test
11384 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11386 if (!m_width
.EqPartial(size
.m_width
))
11389 if (!m_height
.EqPartial(size
.m_height
))
11395 // Apply border to 'this', but not if the same as compareWith
11396 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11398 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11399 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11404 // Remove specified attributes from this object
11405 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11407 if (attr
.m_width
.IsValid())
11409 if (attr
.m_height
.IsValid())
11415 // Collects the attributes that are common to a range of content, building up a note of
11416 // which attributes are absent in some objects and which clash in some objects.
11417 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11419 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11420 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11423 // Collects the attributes that are common to a range of content, building up a note of
11424 // which attributes are absent in some objects and which clash in some objects.
11425 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11427 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11428 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11430 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11432 if (attr
.HasFont())
11434 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11436 if (currentStyle
.HasFontSize())
11438 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11440 // Clash of attr - mark as such
11441 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11442 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11446 currentStyle
.SetFontSize(attr
.GetFontSize());
11449 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11451 if (currentStyle
.HasFontItalic())
11453 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11455 // Clash of attr - mark as such
11456 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11457 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11461 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11464 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11466 if (currentStyle
.HasFontFamily())
11468 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11470 // Clash of attr - mark as such
11471 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11472 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11476 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11479 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11481 if (currentStyle
.HasFontWeight())
11483 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11485 // Clash of attr - mark as such
11486 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11487 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11491 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11494 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11496 if (currentStyle
.HasFontFaceName())
11498 wxString
faceName1(currentStyle
.GetFontFaceName());
11499 wxString
faceName2(attr
.GetFontFaceName());
11501 if (faceName1
!= faceName2
)
11503 // Clash of attr - mark as such
11504 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11505 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11509 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11512 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11514 if (currentStyle
.HasFontUnderlined())
11516 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11518 // Clash of attr - mark as such
11519 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11520 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11524 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11528 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11530 if (currentStyle
.HasTextColour())
11532 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11534 // Clash of attr - mark as such
11535 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11536 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11540 currentStyle
.SetTextColour(attr
.GetTextColour());
11543 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11545 if (currentStyle
.HasBackgroundColour())
11547 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11549 // Clash of attr - mark as such
11550 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11551 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11555 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11558 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11560 if (currentStyle
.HasAlignment())
11562 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11564 // Clash of attr - mark as such
11565 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11566 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11570 currentStyle
.SetAlignment(attr
.GetAlignment());
11573 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11575 if (currentStyle
.HasTabs())
11577 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11579 // Clash of attr - mark as such
11580 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11581 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11585 currentStyle
.SetTabs(attr
.GetTabs());
11588 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11590 if (currentStyle
.HasLeftIndent())
11592 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11594 // Clash of attr - mark as such
11595 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11596 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11600 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11603 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11605 if (currentStyle
.HasRightIndent())
11607 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11609 // Clash of attr - mark as such
11610 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11611 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11615 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11618 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11620 if (currentStyle
.HasParagraphSpacingAfter())
11622 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11624 // Clash of attr - mark as such
11625 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11626 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11630 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11633 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11635 if (currentStyle
.HasParagraphSpacingBefore())
11637 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11639 // Clash of attr - mark as such
11640 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11641 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11645 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11648 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11650 if (currentStyle
.HasLineSpacing())
11652 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11654 // Clash of attr - mark as such
11655 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11656 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11660 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11663 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11665 if (currentStyle
.HasCharacterStyleName())
11667 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11669 // Clash of attr - mark as such
11670 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11671 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11675 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11678 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11680 if (currentStyle
.HasParagraphStyleName())
11682 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11684 // Clash of attr - mark as such
11685 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11686 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11690 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11693 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11695 if (currentStyle
.HasListStyleName())
11697 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11699 // Clash of attr - mark as such
11700 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11701 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11705 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11708 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11710 if (currentStyle
.HasBulletStyle())
11712 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11714 // Clash of attr - mark as such
11715 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11716 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11720 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11723 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11725 if (currentStyle
.HasBulletNumber())
11727 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11729 // Clash of attr - mark as such
11730 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11731 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11735 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11738 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11740 if (currentStyle
.HasBulletText())
11742 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11744 // Clash of attr - mark as such
11745 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11746 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11751 currentStyle
.SetBulletText(attr
.GetBulletText());
11752 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11756 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11758 if (currentStyle
.HasBulletName())
11760 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11762 // Clash of attr - mark as such
11763 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11764 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11769 currentStyle
.SetBulletName(attr
.GetBulletName());
11773 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11775 if (currentStyle
.HasURL())
11777 if (currentStyle
.GetURL() != attr
.GetURL())
11779 // Clash of attr - mark as such
11780 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11781 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
11786 currentStyle
.SetURL(attr
.GetURL());
11790 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
11792 if (currentStyle
.HasTextEffects())
11794 // We need to find the bits in the new attr that are different:
11795 // just look at those bits that are specified by the new attr.
11797 // We need to remove the bits and flags that are not common between current attr
11798 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11799 // previous styles.
11801 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
11802 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
11804 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
11806 // Find the text effects that were different, using XOR
11807 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
11809 // Clash of attr - mark as such
11810 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
11811 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
11816 currentStyle
.SetTextEffects(attr
.GetTextEffects());
11817 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
11820 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11821 // that we've looked at so far
11822 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
11823 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
11825 if (currentStyle
.GetTextEffectFlags() == 0)
11826 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
11829 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
11831 if (currentStyle
.HasOutlineLevel())
11833 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
11835 // Clash of attr - mark as such
11836 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11837 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11841 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
11845 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
11847 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
11849 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
11851 if (m_properties
.GetCount() != props
.GetCount())
11855 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11857 const wxVariant
& var1
= m_properties
[i
];
11858 int idx
= props
.Find(var1
.GetName());
11861 const wxVariant
& var2
= props
.m_properties
[idx
];
11862 if (!(var1
== var2
))
11869 wxArrayString
wxRichTextProperties::GetPropertyNames() const
11873 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11875 arr
.Add(m_properties
[i
].GetName());
11880 int wxRichTextProperties::Find(const wxString
& name
) const
11883 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11885 if (m_properties
[i
].GetName() == name
)
11891 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
11893 int idx
= Find(name
);
11894 if (idx
== wxNOT_FOUND
)
11895 SetProperty(name
, wxString());
11897 if (idx
!= wxNOT_FOUND
)
11899 return & (*this)[idx
];
11905 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
11907 static const wxVariant nullVariant
;
11908 int idx
= Find(name
);
11910 return m_properties
[idx
];
11912 return nullVariant
;
11915 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
11917 return GetProperty(name
).GetString();
11920 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
11922 return GetProperty(name
).GetLong();
11925 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
11927 return GetProperty(name
).GetBool();
11930 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
11932 return GetProperty(name
).GetDouble();
11935 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
11937 wxASSERT(!variant
.GetName().IsEmpty());
11939 int idx
= Find(variant
.GetName());
11942 m_properties
.Add(variant
);
11944 m_properties
[idx
] = variant
;
11947 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
11949 int idx
= Find(name
);
11950 wxVariant
var(variant
);
11954 m_properties
.Add(var
);
11956 m_properties
[idx
] = var
;
11959 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
11961 SetProperty(name
, wxVariant(value
, name
));
11964 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
11966 SetProperty(name
, wxVariant(value
, name
));
11969 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
11971 SetProperty(name
, wxVariant(value
, name
));
11974 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
11976 SetProperty(name
, wxVariant(value
, name
));
11979 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
11981 if (m_address
.GetCount() == 0)
11982 return topLevelContainer
;
11984 wxRichTextCompositeObject
* p
= topLevelContainer
;
11986 while (p
&& i
< m_address
.GetCount())
11988 int pos
= m_address
[i
];
11989 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
11990 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
11993 wxRichTextObject
* p1
= p
->GetChild(pos
);
11994 if (i
== (m_address
.GetCount()-1))
11997 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12003 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12007 if (topLevelContainer
== obj
)
12010 wxRichTextObject
* o
= obj
;
12013 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12017 int pos
= p
->GetChildren().IndexOf(o
);
12021 m_address
.Insert(pos
, 0);
12023 if (p
== topLevelContainer
)
12032 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12034 if (m_container
!= sel
.m_container
)
12036 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12039 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12040 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12045 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12046 // or none at the level of the object's container.
12047 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12051 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12053 if (container
== m_container
)
12056 container
= obj
->GetContainer();
12059 if (container
->GetParent())
12061 // If we found that our object's container is within the range of
12062 // a selection higher up, then assume the whole original object
12063 // is also selected.
12064 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12065 if (parentContainer
== m_container
)
12067 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12069 wxRichTextRangeArray ranges
;
12070 ranges
.Add(obj
->GetRange());
12075 container
= parentContainer
;
12084 return wxRichTextRangeArray();
12087 // Is the given position within the selection?
12088 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12094 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12095 return WithinSelection(pos
, selectionRanges
);
12099 // Is the given position within the selection range?
12100 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12103 for (i
= 0; i
< ranges
.GetCount(); i
++)
12105 const wxRichTextRange
& range
= ranges
[i
];
12106 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12112 // Is the given range completely within the selection range?
12113 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12116 for (i
= 0; i
< ranges
.GetCount(); i
++)
12118 const wxRichTextRange
& eachRange
= ranges
[i
];
12119 if (range
.IsWithin(eachRange
))