1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
46 #include "wx/listimpl.cpp"
47 #include "wx/arrimpl.cpp"
49 WX_DEFINE_LIST(wxRichTextObjectList
)
50 WX_DEFINE_LIST(wxRichTextLineList
)
52 // Switch off if the platform doesn't like it for some reason
53 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55 // Use GetPartialTextExtents for platforms that support it natively
56 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
60 // Helper classes for floating layout
61 struct wxRichTextFloatRectMap
63 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
73 wxRichTextObject
* anchor
;
76 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
78 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
80 return r1
->startY
- r2
->startY
;
83 class wxRichTextFloatCollector
86 wxRichTextFloatCollector(const wxRect
& availableRect
);
87 ~wxRichTextFloatCollector();
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph
* para
);
91 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
93 // Return the last paragraph we collected
94 wxRichTextParagraph
* LastParagraph();
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect
GetAvailableRect(int startY
, int endY
);
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction
, int start
, int height
) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
104 // Find the last y position
105 int GetLastRectBottom();
107 // Draw the floats inside a rect
108 void Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
120 bool DeleteFloat(wxRichTextObject
* obj
);
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject
* obj
);
125 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
133 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
138 wxRichTextFloatRectMapArray m_left
;
139 wxRichTextFloatRectMapArray m_right
;
141 wxRect m_availableRect
;
142 wxRichTextParagraph
* m_para
;
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
149 for (i
= 0; i
< m_left
.GetCount(); i
++)
151 if (m_left
[i
]->anchor
== obj
)
157 for (i
= 0; i
< m_right
.GetCount(); i
++)
159 if (m_right
[i
]->anchor
== obj
)
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
172 for (i
= 0; i
< m_left
.GetCount(); i
++)
174 if (m_left
[i
]->anchor
== obj
)
179 for (i
= 0; i
< m_right
.GetCount(); i
++)
181 if (m_right
[i
]->anchor
== obj
)
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
193 for (i
= 0; i
< m_left
.GetCount(); i
++)
194 objects
.Append(m_left
[i
]->anchor
);
195 for (i
= 0; i
< m_right
.GetCount(); i
++)
196 objects
.Append(m_right
[i
]->anchor
);
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
209 int end
= array
.GetCount() - 1;
222 int mid
= (start
+ end
) / 2;
223 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
225 else if (array
[mid
]->startY
> point
)
230 else if (array
[mid
]->endY
< point
)
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
243 int len
= array
.GetCount();
245 wxASSERT(index
>= 0 && index
< len
);
247 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
248 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
249 while (index
< len
&& array
[index
]->startY
<= endY
)
251 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
260 m_availableRect
= rect
;
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
266 int len
= array
.GetCount();
267 for (int i
= 0; i
< len
; i
++)
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 FreeFloatRectMapArray(m_left
);
274 FreeFloatRectMapArray(m_right
);
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
279 if (array
.GetCount() == 0)
282 int i
= SearchAdjacentRect(array
, start
);
284 while (i
< (int) array
.GetCount())
286 if (array
[i
]->startY
- last
>= height
)
288 last
= array
[i
]->endY
;
295 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
297 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
298 return GetFitPosition(m_left
, start
, height
);
299 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
300 return GetFitPosition(m_right
, start
, height
);
303 wxASSERT("Never should be here");
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
312 int direction
= floating
->GetFloatDirection();
314 wxPoint pos
= floating
->GetPosition();
315 wxSize size
= floating
->GetCachedSize();
316 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
319 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
328 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
333 wxASSERT("Unrecognised float attribute.");
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
341 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
344 wxRichTextObject
* floating
= node
->GetData();
346 if (floating
->IsFloating())
348 CollectFloat(para
, floating
);
351 node
= node
->GetNext();
357 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
362 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
364 int widthLeft
= 0, widthRight
= 0;
365 if (m_left
.GetCount() != 0)
367 int i
= SearchAdjacentRect(m_left
, startY
);
368 if (i
< (int) m_left
.GetCount())
369 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
371 if (m_right
.GetCount() != 0)
373 int j
= SearchAdjacentRect(m_right
, startY
);
374 if (j
< (int) m_right
.GetCount())
375 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
384 int wxRichTextFloatCollector::GetLastRectBottom()
387 int len
= m_left
.GetCount();
389 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
391 len
= m_right
.GetCount();
393 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
402 int end
= rect
.y
+ rect
.height
;
404 i
= SearchAdjacentRect(array
, start
);
405 if (i
< 0 || i
>= (int) array
.GetCount())
407 j
= SearchAdjacentRect(array
, end
);
408 if (j
< 0 || j
>= (int) array
.GetCount())
409 j
= array
.GetCount() - 1;
412 wxRichTextObject
* obj
= array
[i
]->anchor
;
413 wxRichTextRange r
= obj
->GetRange();
414 obj
->Draw(dc
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
430 if (array
.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE
;
432 i
= SearchAdjacentRect(array
, pt
.y
);
433 if (i
< 0 || i
>= (int) array
.GetCount())
434 return wxRICHTEXT_HITTEST_NONE
;
435 if (!array
[i
]->anchor
->IsShown())
436 return wxRICHTEXT_HITTEST_NONE
;
438 wxPoint point
= array
[i
]->anchor
->GetPosition();
439 wxSize size
= array
[i
]->anchor
->GetCachedSize();
440 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
441 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
443 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
444 * obj
= array
[i
]->anchor
;
445 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE
;
448 return wxRICHTEXT_HITTEST_AFTER
;
451 return wxRICHTEXT_HITTEST_NONE
;
454 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, pt
, textPosition
, obj
, flags
);
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
467 // JACS: did I do this some time ago when testing? Should we re-enable it?
469 const wxFont
& font1
= dc
.GetFont();
470 if (font1
.IsOk() && font
.IsOk())
472 if (font1
.GetPointSize() == font
.GetPointSize() &&
473 font1
.GetFamily() == font
.GetFamily() &&
474 font1
.GetStyle() == font
.GetStyle() &&
475 font1
.GetWeight() == font
.GetWeight() &&
476 font1
.GetUnderlined() == font
.GetUnderlined() &&
477 font1
.GetFamily() == font
.GetFamily() &&
478 font1
.GetFaceName() == font
.GetFaceName())
485 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
487 const wxPen
& pen1
= dc
.GetPen();
488 if (pen1
.IsOk() && pen
.IsOk())
490 if (pen1
.GetWidth() == pen
.GetWidth() &&
491 pen1
.GetStyle() == pen
.GetStyle() &&
492 pen1
.GetColour() == pen
.GetColour())
498 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
500 const wxBrush
& brush1
= dc
.GetBrush();
501 if (brush1
.IsOk() && brush
.IsOk())
503 if (brush1
.GetStyle() == brush
.GetStyle() &&
504 brush1
.GetColour() == brush
.GetColour())
512 * This is the base for drawable objects.
515 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
517 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
525 wxRichTextObject::~wxRichTextObject()
529 void wxRichTextObject::Dereference()
537 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
540 m_maxSize
= obj
.m_maxSize
;
541 m_minSize
= obj
.m_minSize
;
543 m_range
= obj
.m_range
;
544 m_ownRange
= obj
.m_ownRange
;
545 m_attributes
= obj
.m_attributes
;
546 m_properties
= obj
.m_properties
;
547 m_descent
= obj
.m_descent
;
551 // Get/set the top-level container of this object.
552 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
554 const wxRichTextObject
* p
= this;
559 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
566 void wxRichTextObject::SetMargins(int margin
)
568 SetMargins(margin
, margin
, margin
, margin
);
571 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
573 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
574 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
579 int wxRichTextObject::GetLeftMargin() const
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
584 int wxRichTextObject::GetRightMargin() const
586 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
589 int wxRichTextObject::GetTopMargin() const
591 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
594 int wxRichTextObject::GetBottomMargin() const
596 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
599 // Calculate the available content space in the given rectangle, given the
600 // margins, border and padding specified in the object's attributes.
601 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, const wxRect
& outerRect
) const
603 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
604 marginRect
= outerRect
;
605 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
609 // Invalidate the buffer. With no argument, invalidates whole buffer.
610 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
612 if (invalidRange
!= wxRICHTEXT_NONE
)
614 SetCachedSize(wxDefaultSize
);
615 SetMaxSize(wxDefaultSize
);
616 SetMinSize(wxDefaultSize
);
620 // Convert units in tenths of a millimetre to device units
621 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
626 scale
= GetBuffer()->GetScale();
627 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
632 // Convert units in tenths of a millimetre to device units
633 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
635 // There are ppi pixels in 254.1 "1/10 mm"
637 double pixels
= ((double) units
* (double)ppi
) / 254.1;
641 // If the result is very small, make it at least one pixel in size.
642 if (pixels
== 0 && units
> 0)
648 // Convert units in pixels to tenths of a millimetre
649 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
654 scale
= GetBuffer()->GetScale();
656 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
659 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
661 // There are ppi pixels in 254.1 "1/10 mm"
663 double p
= double(pixels
);
668 int units
= int( p
* 254.1 / (double) ppi
);
672 // Draw the borders and background for the given rectangle and attributes.
673 // Width and height are taken to be the outer margin size, not the content.
674 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
676 // Assume boxRect is the area around the content
677 wxRect marginRect
= boxRect
;
678 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
680 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
682 // Margin is transparent. Draw background from margin.
683 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
686 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
688 // TODO: get selection colour from control?
689 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
692 colour
= attr
.GetBackgroundColour();
695 wxBrush
brush(colour
);
699 dc
.DrawRectangle(marginRect
);
702 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
704 wxRichTextAttr editBorderAttr
= attr
;
705 // TODO: make guideline colour configurable
706 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
707 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
708 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
710 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
713 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
714 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
716 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
717 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
723 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
725 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
726 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
728 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
730 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
731 wxColour
col(attr
.GetLeft().GetColour());
733 // If pen width is > 1, resorts to a solid rectangle.
736 int penStyle
= wxSOLID
;
737 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
739 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
740 penStyle
= wxLONG_DASH
;
741 wxPen
pen(col
, 1, penStyle
);
743 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
746 else if (borderLeft
> 1)
752 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
756 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
758 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
760 wxColour
col(attr
.GetRight().GetColour());
762 // If pen width is > 1, resorts to a solid rectangle.
763 if (borderRight
== 1)
765 int penStyle
= wxSOLID
;
766 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
768 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
769 penStyle
= wxLONG_DASH
;
770 wxPen
pen(col
, 1, penStyle
);
772 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
775 else if (borderRight
> 1)
781 dc
.DrawRectangle(rect
.x
- borderRight
, rect
.y
, borderRight
, rect
.height
);
785 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
787 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
789 wxColour
col(attr
.GetTop().GetColour());
791 // If pen width is > 1, resorts to a solid rectangle.
794 int penStyle
= wxSOLID
;
795 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
797 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
798 penStyle
= wxLONG_DASH
;
799 wxPen
pen(col
, 1, penStyle
);
801 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
804 else if (borderTop
> 1)
810 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
814 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
816 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
817 wxColour
col(attr
.GetTop().GetColour());
819 // If pen width is > 1, resorts to a solid rectangle.
820 if (borderBottom
== 1)
822 int penStyle
= wxSOLID
;
823 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
825 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
826 penStyle
= wxLONG_DASH
;
827 wxPen
pen(col
, 1, penStyle
);
829 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
832 else if (borderBottom
> 1)
838 dc
.DrawRectangle(rect
.x
, rect
.y
- rect
.height
- borderBottom
, rect
.width
, borderBottom
);
845 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
846 // or marginRect (outer), and the other must be the default rectangle (no width or height).
847 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
850 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
852 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
854 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
855 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
856 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
857 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
859 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
861 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
862 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
863 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
864 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
865 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
866 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
867 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
868 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
870 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
871 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
872 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
873 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
874 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
875 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
876 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
877 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
879 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
880 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
881 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
882 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
883 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
884 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
885 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
886 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
888 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
889 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
890 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
891 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
892 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
893 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
894 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
895 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
897 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
898 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
899 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
900 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
902 if (marginRect
!= wxRect())
904 contentRect
.x
= marginRect
.x
+ leftTotal
;
905 contentRect
.y
= marginRect
.y
+ topTotal
;
906 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
907 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
911 marginRect
.x
= contentRect
.x
- leftTotal
;
912 marginRect
.y
= contentRect
.y
- topTotal
;
913 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
914 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
917 borderRect
.x
= marginRect
.x
+ marginLeft
;
918 borderRect
.y
= marginRect
.y
+ marginTop
;
919 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
920 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
922 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
923 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
924 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
925 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
927 // The outline is outside the margin and doesn't influence the overall box position or content size.
928 outlineRect
.x
= marginRect
.x
- outlineLeft
;
929 outlineRect
.y
= marginRect
.y
- outlineTop
;
930 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
931 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
936 // Get the total margin for the object in pixels, taking into account margin, padding and border size
937 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
938 int& topMargin
, int& bottomMargin
)
940 // Assume boxRect is the area around the content
941 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
942 marginRect
= wxRect(0, 0, 1000, 1000);
944 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
946 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
947 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
948 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
949 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
954 // Returns the rectangle which the child has available to it given restrictions specified in the
955 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
956 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
)
958 wxRect rect
= availableParentSpace
;
961 scale
= buffer
->GetScale();
963 wxTextAttrDimensionConverter
converter(dc
, scale
, availableParentSpace
.GetSize());
965 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
966 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
968 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
969 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
971 // Can specify either left or right for the position (we're assuming we can't
972 // set the left and right edges to effectively set the size. Would we want to do that?)
973 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
975 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
977 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
979 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
981 rect
.x
= availableParentSpace
.x
+ availableParentSpace
.width
- rect
.width
;
986 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
988 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
990 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
992 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
993 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
994 rect
.y
= availableParentSpace
.y
+ availableParentSpace
.height
- rect
.height
;
1002 // Dump to output stream for debugging
1003 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1005 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1006 stream
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n");
1007 stream
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n");
1010 // Gets the containing buffer
1011 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1013 const wxRichTextObject
* obj
= this;
1014 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1015 obj
= obj
->GetParent();
1016 return wxDynamicCast(obj
, wxRichTextBuffer
);
1019 // Get the absolute object position, by traversing up the child/parent hierarchy
1020 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1022 wxPoint pt
= GetPosition();
1024 wxRichTextObject
* p
= GetParent();
1027 pt
= pt
+ p
->GetPosition();
1034 // Hit-testing: returns a flag indicating hit test details, plus
1035 // information about position
1036 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1039 return wxRICHTEXT_HITTEST_NONE
;
1041 wxRect rect
= GetRect();
1042 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1043 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1046 *contextObj
= GetParentContainer();
1047 textPosition
= GetRange().GetStart();
1048 return wxRICHTEXT_HITTEST_ON
;
1051 return wxRICHTEXT_HITTEST_NONE
;
1054 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1055 // lays out the object again using the maximum ('best') size
1056 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextBuffer
* buffer
,
1057 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
, const wxRect
& availableParentSpace
,
1060 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
);
1061 wxRect originalAvailableRect
= availableChildRect
;
1062 Layout(dc
, availableChildRect
, style
);
1064 wxSize maxSize
= GetMaxSize();
1066 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1068 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
/* && maxSize.x > 0 */)
1070 // Redo the layout with a fixed, minimum size this time.
1071 Invalidate(wxRICHTEXT_ALL
);
1072 wxRichTextAttr
newAttr(attr
);
1073 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1074 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1076 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
);
1078 // If a paragraph, align the whole paragraph.
1079 // Problem with this: if we're limited by a floating object, a line may be centered
1080 // w.r.t. the smaller resulting box rather than the actual available width.
1081 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1083 // centering, right-justification
1084 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1086 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1088 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1090 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1094 Layout(dc
, availableChildRect
, style
);
1108 // Move the object recursively, by adding the offset from old to new
1109 void wxRichTextObject::Move(const wxPoint
& pt
)
1116 * wxRichTextCompositeObject
1117 * This is the base for drawable objects.
1120 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1122 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1123 wxRichTextObject(parent
)
1127 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1132 /// Get the nth child
1133 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1135 wxASSERT ( n
< m_children
.GetCount() );
1137 return m_children
.Item(n
)->GetData();
1140 /// Append a child, returning the position
1141 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1143 m_children
.Append(child
);
1144 child
->SetParent(this);
1145 return m_children
.GetCount() - 1;
1148 /// Insert the child in front of the given object, or at the beginning
1149 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1153 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1154 m_children
.Insert(node
, child
);
1157 m_children
.Insert(child
);
1158 child
->SetParent(this);
1163 /// Delete the child
1164 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1166 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1169 wxRichTextObject
* obj
= node
->GetData();
1170 m_children
.Erase(node
);
1179 /// Delete all children
1180 bool wxRichTextCompositeObject::DeleteChildren()
1182 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1185 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1187 wxRichTextObject
* child
= node
->GetData();
1188 child
->Dereference(); // Only delete if reference count is zero
1190 node
= node
->GetNext();
1191 m_children
.Erase(oldNode
);
1197 /// Get the child count
1198 size_t wxRichTextCompositeObject::GetChildCount() const
1200 return m_children
.GetCount();
1204 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1206 wxRichTextObject::Copy(obj
);
1210 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1213 wxRichTextObject
* child
= node
->GetData();
1214 wxRichTextObject
* newChild
= child
->Clone();
1215 newChild
->SetParent(this);
1216 m_children
.Append(newChild
);
1218 node
= node
->GetNext();
1222 /// Hit-testing: returns a flag indicating hit test details, plus
1223 /// information about position
1224 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1227 return wxRICHTEXT_HITTEST_NONE
;
1229 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1232 wxRichTextObject
* child
= node
->GetData();
1234 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1236 // Just check if we hit the overall object
1237 int ret
= child
->wxRichTextObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1238 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1241 else if (child
->IsShown())
1243 int ret
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1244 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1248 node
= node
->GetNext();
1251 return wxRICHTEXT_HITTEST_NONE
;
1254 /// Finds the absolute position and row height for the given character position
1255 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1257 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1260 wxRichTextObject
* child
= node
->GetData();
1262 // Don't recurse if the child is a top-level object,
1263 // such as a text box, because the character position will no longer
1264 // apply. By definition, a top-level object has its own range of
1265 // character positions.
1266 if (!child
->IsTopLevel() && child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
))
1269 node
= node
->GetNext();
1276 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1278 long current
= start
;
1279 long lastEnd
= current
;
1287 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1290 wxRichTextObject
* child
= node
->GetData();
1293 child
->CalculateRange(current
, childEnd
);
1296 current
= childEnd
+ 1;
1298 node
= node
->GetNext();
1303 // A top-level object always has a range of size 1,
1304 // because its children don't count at this level.
1306 m_range
.SetRange(start
, start
);
1308 // An object with no children has zero length
1309 if (m_children
.GetCount() == 0)
1311 m_ownRange
.SetRange(0, lastEnd
);
1317 // An object with no children has zero length
1318 if (m_children
.GetCount() == 0)
1321 m_range
.SetRange(start
, end
);
1325 /// Delete range from layout.
1326 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1328 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1332 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1333 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1335 // Delete the range in each paragraph
1337 // When a chunk has been deleted, internally the content does not
1338 // now match the ranges.
1339 // However, so long as deletion is not done on the same object twice this is OK.
1340 // If you may delete content from the same object twice, recalculate
1341 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1342 // adjust the range you're deleting accordingly.
1344 if (!obj
->GetRange().IsOutside(range
))
1346 // No need to delete within a top-level object; just removing this object will do fine
1347 if (!obj
->IsTopLevel())
1348 obj
->DeleteRange(range
);
1350 // Delete an empty object, or paragraph within this range.
1351 if (obj
->IsEmpty() ||
1352 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1354 // An empty paragraph has length 1, so won't be deleted unless the
1355 // whole range is deleted.
1356 RemoveChild(obj
, true);
1366 /// Get any text in this object for the given range
1367 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1370 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1373 wxRichTextObject
* child
= node
->GetData();
1374 wxRichTextRange childRange
= range
;
1375 if (!child
->GetRange().IsOutside(range
))
1377 childRange
.LimitTo(child
->GetRange());
1379 wxString childText
= child
->GetTextForRange(childRange
);
1383 node
= node
->GetNext();
1389 /// Get the child object at the given character position
1390 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1392 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1395 wxRichTextObject
* child
= node
->GetData();
1396 if (child
->GetRange().GetStart() == pos
)
1398 node
= node
->GetNext();
1403 /// Recursively merge all pieces that can be merged.
1404 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1406 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1409 wxRichTextObject
* child
= node
->GetData();
1410 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1412 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1414 composite
->Defragment();
1416 if (node
->GetNext())
1418 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1419 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1421 nextChild
->Dereference();
1422 m_children
.Erase(node
->GetNext());
1424 // Don't set node -- we'll see if we can merge again with the next
1428 node
= node
->GetNext();
1431 node
= node
->GetNext();
1434 node
= node
->GetNext();
1437 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1438 if (GetChildCount() > 1)
1440 node
= m_children
.GetFirst();
1443 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1444 wxRichTextObject
* child
= node
->GetData();
1445 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1447 if (child
->IsEmpty())
1449 child
->Dereference();
1450 m_children
.Erase(node
);
1455 node
= node
->GetNext();
1462 /// Dump to output stream for debugging
1463 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1465 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1468 wxRichTextObject
* child
= node
->GetData();
1469 child
->Dump(stream
);
1470 node
= node
->GetNext();
1474 /// Get/set the object size for the given range. Returns false if the range
1475 /// is invalid for this object.
1476 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1478 if (!range
.IsWithin(GetRange()))
1483 wxArrayInt childExtents
;
1490 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1493 wxRichTextObject
* child
= node
->GetData();
1494 if (!child
->GetRange().IsOutside(range
))
1496 // Floating objects have a zero size within the paragraph.
1497 if (child
->IsFloating())
1502 if (partialExtents
->GetCount() > 0)
1503 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1507 partialExtents
->Add(0 /* zero size */ + lastSize
);
1514 wxRichTextRange rangeToUse
= range
;
1515 rangeToUse
.LimitTo(child
->GetRange());
1516 if (child
->IsTopLevel())
1517 rangeToUse
= child
->GetOwnRange();
1519 int childDescent
= 0;
1521 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1522 // but it's only going to be used after caching has taken place.
1523 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1525 childDescent
= child
->GetDescent();
1526 childSize
= child
->GetCachedSize();
1528 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1529 sz
.x
+= childSize
.x
;
1530 descent
= wxMax(descent
, childDescent
);
1532 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1534 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1535 sz
.x
+= childSize
.x
;
1536 descent
= wxMax(descent
, childDescent
);
1538 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1540 child
->SetCachedSize(childSize
);
1541 child
->SetDescent(childDescent
);
1547 if (partialExtents
->GetCount() > 0)
1548 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1553 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1555 partialExtents
->Add(childExtents
[i
] + lastSize
);
1565 node
= node
->GetNext();
1571 // Invalidate the buffer. With no argument, invalidates whole buffer.
1572 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1574 wxRichTextObject::Invalidate(invalidRange
);
1576 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1579 wxRichTextObject
* child
= node
->GetData();
1580 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1584 else if (child
->IsTopLevel())
1586 if (invalidRange
== wxRICHTEXT_NONE
)
1587 child
->Invalidate(wxRICHTEXT_NONE
);
1589 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1592 child
->Invalidate(invalidRange
);
1593 node
= node
->GetNext();
1597 // Move the object recursively, by adding the offset from old to new
1598 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1600 wxPoint oldPos
= GetPosition();
1602 wxPoint offset
= pt
- oldPos
;
1604 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1607 wxRichTextObject
* child
= node
->GetData();
1608 wxPoint childPos
= child
->GetPosition() + offset
;
1609 child
->Move(childPos
);
1610 node
= node
->GetNext();
1616 * wxRichTextParagraphLayoutBox
1617 * This box knows how to lay out paragraphs.
1620 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1622 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1623 wxRichTextCompositeObject(parent
)
1628 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1630 if (m_floatCollector
)
1632 delete m_floatCollector
;
1633 m_floatCollector
= NULL
;
1637 /// Initialize the object.
1638 void wxRichTextParagraphLayoutBox::Init()
1642 // For now, assume is the only box and has no initial size.
1643 m_range
= wxRichTextRange(0, -1);
1644 m_ownRange
= wxRichTextRange(0, -1);
1646 m_invalidRange
= wxRICHTEXT_ALL
;
1649 m_partialParagraph
= false;
1650 m_floatCollector
= NULL
;
1653 void wxRichTextParagraphLayoutBox::Clear()
1657 if (m_floatCollector
)
1658 delete m_floatCollector
;
1659 m_floatCollector
= NULL
;
1660 m_partialParagraph
= false;
1664 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1668 wxRichTextCompositeObject::Copy(obj
);
1670 m_partialParagraph
= obj
.m_partialParagraph
;
1671 m_defaultAttributes
= obj
.m_defaultAttributes
;
1674 // Gather information about floating objects; only gather floats for those paragraphs that
1675 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1677 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1679 if (m_floatCollector
!= NULL
)
1680 delete m_floatCollector
;
1681 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1682 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1683 // Only gather floats up to the point we'll start formatting paragraphs.
1684 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1686 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1687 wxASSERT (child
!= NULL
);
1689 m_floatCollector
->CollectFloat(child
);
1690 node
= node
->GetNext();
1696 // Returns the style sheet associated with the overall buffer.
1697 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1699 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1702 // Get the number of floating objects at this level
1703 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1705 if (m_floatCollector
)
1706 return m_floatCollector
->GetFloatingObjectCount();
1711 // Get a list of floating objects
1712 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1714 if (m_floatCollector
)
1716 return m_floatCollector
->GetFloatingObjects(objects
);
1723 void wxRichTextParagraphLayoutBox::UpdateRanges()
1727 start
= GetRange().GetStart();
1729 CalculateRange(start
, end
);
1733 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1736 return wxRICHTEXT_HITTEST_NONE
;
1738 int ret
= wxRICHTEXT_HITTEST_NONE
;
1739 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1740 ret
= m_floatCollector
->HitTest(dc
, pt
, textPosition
, obj
, flags
);
1742 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1743 return wxRichTextCompositeObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1751 /// Draw the floating objects
1752 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1754 if (m_floatCollector
)
1755 m_floatCollector
->Draw(dc
, range
, selection
, rect
, descent
, style
);
1758 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1763 from
->RemoveChild(obj
);
1764 to
->AppendChild(obj
);
1768 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1773 wxRect
thisRect(GetPosition(), GetCachedSize());
1776 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1779 // Don't draw guidelines if at top level
1780 int theseFlags
= flags
;
1782 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1783 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), thisRect
, theseFlags
);
1785 DrawFloats(dc
, range
, selection
, rect
, descent
, style
);
1786 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1789 wxRichTextObject
* child
= node
->GetData();
1791 if (child
&& !child
->GetRange().IsOutside(range
))
1793 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1794 wxRichTextRange childRange
= range
;
1795 if (child
->IsTopLevel())
1797 childRange
= child
->GetOwnRange();
1800 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1805 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1810 child
->Draw(dc
, childRange
, selection
, rect
, descent
, style
);
1813 node
= node
->GetNext();
1818 /// Lay the item out
1819 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
1821 SetPosition(rect
.GetPosition());
1826 wxRect availableSpace
;
1827 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1829 // If only laying out a specific area, the passed rect has a different meaning:
1830 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1831 // so that during a size, only the visible part will be relaid out, or
1832 // it would take too long causing flicker. As an approximation, we assume that
1833 // everything up to the start of the visible area is laid out correctly.
1836 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1837 availableSpace
= GetAvailableContentArea(dc
, rect2
);
1839 // Invalidate the part of the buffer from the first visible line
1840 // to the end. If other parts of the buffer are currently invalid,
1841 // then they too will be taken into account if they are above
1842 // the visible point.
1844 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1846 startPos
= line
->GetAbsoluteRange().GetStart();
1848 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1852 availableSpace
= GetAvailableContentArea(dc
, rect
);
1855 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1856 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), GetAttributes(), leftMargin
, rightMargin
,
1857 topMargin
, bottomMargin
);
1862 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1863 int maxMaxWidth
= 0;
1865 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1866 int maxMinWidth
= 0;
1868 // If we have vertical alignment, we must recalculate everything.
1869 bool hasVerticalAlignment
= (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1870 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1872 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1874 bool layoutAll
= true;
1876 // Get invalid range, rounding to paragraph start/end.
1877 wxRichTextRange invalidRange
= GetInvalidRange(true);
1879 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1882 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1884 else // If we know what range is affected, start laying out from that point on.
1885 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1887 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1890 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1891 wxRichTextObjectList::compatibility_iterator previousNode
;
1893 previousNode
= firstNode
->GetPrevious();
1898 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1899 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1902 // Now we're going to start iterating from the first affected paragraph.
1910 // Gather information about only those floating objects that will not be formatted,
1911 // after which floats will be gathered per-paragraph during layout.
1912 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1914 // A way to force speedy rest-of-buffer layout (the 'else' below)
1915 bool forceQuickLayout
= false;
1919 // Assume this box only contains paragraphs
1921 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1922 // Unsure if this is needed
1923 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1925 if (child
&& child
->IsShown())
1927 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1928 if ( !forceQuickLayout
&&
1930 child
->GetLines().IsEmpty() ||
1931 !child
->GetRange().IsOutside(invalidRange
)) )
1933 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1934 // lays out the object again using the minimum size
1935 child
->LayoutToBestSize(dc
, GetBuffer(),
1936 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1938 // Layout must set the cached size
1939 availableSpace
.y
+= child
->GetCachedSize().y
;
1940 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1941 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1942 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1944 // If we're just formatting the visible part of the buffer,
1945 // and we're now past the bottom of the window, and we don't have any
1946 // floating objects (since they may cause wrapping to change for the rest of the
1947 // the buffer), start quick layout.
1948 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1949 forceQuickLayout
= true;
1953 // We're outside the immediately affected range, so now let's just
1954 // move everything up or down. This assumes that all the children have previously
1955 // been laid out and have wrapped line lists associated with them.
1956 // TODO: check all paragraphs before the affected range.
1958 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1962 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1965 if (child
->GetLines().GetCount() == 0)
1967 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1968 // lays out the object again using the minimum size
1969 child
->LayoutToBestSize(dc
, GetBuffer(),
1970 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1972 //child->Layout(dc, availableChildRect, style);
1975 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1977 availableSpace
.y
+= child
->GetCachedSize().y
;
1978 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1979 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1980 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1983 node
= node
->GetNext();
1989 node
= node
->GetNext();
1992 node
= m_children
.GetLast();
1993 if (node
&& node
->GetData()->IsShown())
1995 wxRichTextObject
* child
= node
->GetData();
1996 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
1997 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2000 maxHeight
= 0; // topMargin + bottomMargin;
2002 // TODO: (also in para layout) should set the
2003 // object's size to an absolute one if specified,
2004 // but if not specified, calculate it from content.
2006 // We need to add back the margins etc.
2008 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2009 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2010 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2011 SetCachedSize(marginRect
.GetSize());
2014 // The maximum size is the greatest of all maximum widths for all paragraphs.
2016 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2017 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2018 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2019 SetMaxSize(marginRect
.GetSize());
2022 // The minimum size is the greatest of all minimum widths for all paragraphs.
2024 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2025 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2026 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2027 SetMinSize(marginRect
.GetSize());
2030 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2031 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2034 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2035 if (leftOverSpace
> 0)
2037 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2039 yOffset
= (leftOverSpace
/2);
2041 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2043 yOffset
= leftOverSpace
;
2047 // Move all the children to vertically align the content
2048 // This doesn't take into account floating objects, unfortunately.
2051 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2054 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2056 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2058 node
= node
->GetNext();
2063 m_invalidRange
= wxRICHTEXT_NONE
;
2068 /// Get/set the size for the given range.
2069 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2073 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2074 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2076 // First find the first paragraph whose starting position is within the range.
2077 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2080 // child is a paragraph
2081 wxRichTextObject
* child
= node
->GetData();
2082 const wxRichTextRange
& r
= child
->GetRange();
2084 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2090 node
= node
->GetNext();
2093 // Next find the last paragraph containing part of the range
2094 node
= m_children
.GetFirst();
2097 // child is a paragraph
2098 wxRichTextObject
* child
= node
->GetData();
2099 const wxRichTextRange
& r
= child
->GetRange();
2101 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2107 node
= node
->GetNext();
2110 if (!startPara
|| !endPara
)
2113 // Now we can add up the sizes
2114 for (node
= startPara
; node
; node
= node
->GetNext())
2116 // child is a paragraph
2117 wxRichTextObject
* child
= node
->GetData();
2118 const wxRichTextRange
& childRange
= child
->GetRange();
2119 wxRichTextRange rangeToFind
= range
;
2120 rangeToFind
.LimitTo(childRange
);
2122 if (child
->IsTopLevel())
2123 rangeToFind
= child
->GetOwnRange();
2127 int childDescent
= 0;
2128 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2130 descent
= wxMax(childDescent
, descent
);
2132 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2133 sz
.y
+= childSize
.y
;
2135 if (node
== endPara
)
2144 /// Get the paragraph at the given position
2145 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2150 // First find the first paragraph whose starting position is within the range.
2151 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2154 // child is a paragraph
2155 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2156 // wxASSERT (child != NULL);
2160 // Return first child in buffer if position is -1
2164 if (child
->GetRange().Contains(pos
))
2168 node
= node
->GetNext();
2173 /// Get the line at the given position
2174 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2179 // First find the first paragraph whose starting position is within the range.
2180 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2183 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2184 if (obj
->GetRange().Contains(pos
))
2186 // child is a paragraph
2187 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2188 // wxASSERT (child != NULL);
2192 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2195 wxRichTextLine
* line
= node2
->GetData();
2197 wxRichTextRange range
= line
->GetAbsoluteRange();
2199 if (range
.Contains(pos
) ||
2201 // If the position is end-of-paragraph, then return the last line of
2202 // of the paragraph.
2203 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2206 node2
= node2
->GetNext();
2211 node
= node
->GetNext();
2214 int lineCount
= GetLineCount();
2216 return GetLineForVisibleLineNumber(lineCount
-1);
2221 /// Get the line at the given y pixel position, or the last line.
2222 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2224 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2227 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2228 // wxASSERT (child != NULL);
2232 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2235 wxRichTextLine
* line
= node2
->GetData();
2237 wxRect
rect(line
->GetRect());
2239 if (y
<= rect
.GetBottom())
2242 node2
= node2
->GetNext();
2246 node
= node
->GetNext();
2250 int lineCount
= GetLineCount();
2252 return GetLineForVisibleLineNumber(lineCount
-1);
2257 /// Get the number of visible lines
2258 int wxRichTextParagraphLayoutBox::GetLineCount() const
2262 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2265 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2266 // wxASSERT (child != NULL);
2269 count
+= child
->GetLines().GetCount();
2271 node
= node
->GetNext();
2277 /// Get the paragraph for a given line
2278 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2280 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2283 /// Get the line size at the given position
2284 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2286 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2289 return line
->GetSize();
2292 return wxSize(0, 0);
2296 /// Convenience function to add a paragraph of text
2297 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2299 // Don't use the base style, just the default style, and the base style will
2300 // be combined at display time.
2301 // Divide into paragraph and character styles.
2303 wxRichTextAttr defaultCharStyle
;
2304 wxRichTextAttr defaultParaStyle
;
2306 // If the default style is a named paragraph style, don't apply any character formatting
2307 // to the initial text string.
2308 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2310 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2312 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2315 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2317 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2318 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2320 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2326 return para
->GetRange();
2329 /// Adds multiple paragraphs, based on newlines.
2330 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2332 // Don't use the base style, just the default style, and the base style will
2333 // be combined at display time.
2334 // Divide into paragraph and character styles.
2336 wxRichTextAttr defaultCharStyle
;
2337 wxRichTextAttr defaultParaStyle
;
2339 // If the default style is a named paragraph style, don't apply any character formatting
2340 // to the initial text string.
2341 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2343 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2345 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2348 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2350 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2351 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2353 wxRichTextParagraph
* firstPara
= NULL
;
2354 wxRichTextParagraph
* lastPara
= NULL
;
2356 wxRichTextRange
range(-1, -1);
2359 size_t len
= text
.length();
2361 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2370 wxChar ch
= text
[i
];
2371 if (ch
== wxT('\n') || ch
== wxT('\r'))
2375 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2376 plainText
->SetText(line
);
2378 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2383 line
= wxEmptyString
;
2394 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2395 plainText
->SetText(line
);
2400 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2403 /// Convenience function to add an image
2404 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2406 // Don't use the base style, just the default style, and the base style will
2407 // be combined at display time.
2408 // Divide into paragraph and character styles.
2410 wxRichTextAttr defaultCharStyle
;
2411 wxRichTextAttr defaultParaStyle
;
2413 // If the default style is a named paragraph style, don't apply any character formatting
2414 // to the initial text string.
2415 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2417 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2419 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2422 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2424 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2425 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2427 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2429 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2433 return para
->GetRange();
2437 /// Insert fragment into this box at the given position. If partialParagraph is true,
2438 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2441 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2443 // First, find the first paragraph whose starting position is within the range.
2444 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2447 wxRichTextAttr originalAttr
= para
->GetAttributes();
2449 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2451 // Now split at this position, returning the object to insert the new
2452 // ones in front of.
2453 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2455 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2456 // text, for example, so let's optimize.
2458 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2460 // Add the first para to this para...
2461 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2465 // Iterate through the fragment paragraph inserting the content into this paragraph.
2466 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2467 wxASSERT (firstPara
!= NULL
);
2469 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2472 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2477 para
->AppendChild(newObj
);
2481 // Insert before nextObject
2482 para
->InsertChild(newObj
, nextObject
);
2485 objectNode
= objectNode
->GetNext();
2492 // Procedure for inserting a fragment consisting of a number of
2495 // 1. Remove and save the content that's after the insertion point, for adding
2496 // back once we've added the fragment.
2497 // 2. Add the content from the first fragment paragraph to the current
2499 // 3. Add remaining fragment paragraphs after the current paragraph.
2500 // 4. Add back the saved content from the first paragraph. If partialParagraph
2501 // is true, add it to the last paragraph added and not a new one.
2503 // 1. Remove and save objects after split point.
2504 wxList savedObjects
;
2506 para
->MoveToList(nextObject
, savedObjects
);
2508 // 2. Add the content from the 1st fragment paragraph.
2509 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2513 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2514 wxASSERT(firstPara
!= NULL
);
2516 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2517 para
->SetAttributes(firstPara
->GetAttributes());
2519 // Save empty paragraph attributes for appending later
2520 // These are character attributes deliberately set for a new paragraph. Without this,
2521 // we couldn't pass default attributes when appending a new paragraph.
2522 wxRichTextAttr emptyParagraphAttributes
;
2524 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2526 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2527 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2531 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2534 para
->AppendChild(newObj
);
2536 objectNode
= objectNode
->GetNext();
2539 // 3. Add remaining fragment paragraphs after the current paragraph.
2540 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2541 wxRichTextObject
* nextParagraph
= NULL
;
2542 if (nextParagraphNode
)
2543 nextParagraph
= nextParagraphNode
->GetData();
2545 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2546 wxRichTextParagraph
* finalPara
= para
;
2548 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2550 // If there was only one paragraph, we need to insert a new one.
2553 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2554 wxASSERT( para
!= NULL
);
2556 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2559 InsertChild(finalPara
, nextParagraph
);
2561 AppendChild(finalPara
);
2566 // If there was only one paragraph, or we have full paragraphs in our fragment,
2567 // we need to insert a new one.
2570 finalPara
= new wxRichTextParagraph
;
2573 InsertChild(finalPara
, nextParagraph
);
2575 AppendChild(finalPara
);
2578 // 4. Add back the remaining content.
2582 finalPara
->MoveFromList(savedObjects
);
2584 // Ensure there's at least one object
2585 if (finalPara
->GetChildCount() == 0)
2587 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2588 text
->SetAttributes(emptyParagraphAttributes
);
2590 finalPara
->AppendChild(text
);
2594 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2595 finalPara
->SetAttributes(firstPara
->GetAttributes());
2596 else if (finalPara
&& finalPara
!= para
)
2597 finalPara
->SetAttributes(originalAttr
);
2605 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2608 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2609 wxASSERT( para
!= NULL
);
2611 AppendChild(para
->Clone());
2620 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2621 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2622 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2624 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2627 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2628 wxASSERT( para
!= NULL
);
2630 if (!para
->GetRange().IsOutside(range
))
2632 fragment
.AppendChild(para
->Clone());
2637 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2638 if (!fragment
.IsEmpty())
2640 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2641 wxASSERT( firstPara
!= NULL
);
2643 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2644 wxASSERT( lastPara
!= NULL
);
2646 if (!firstPara
|| !lastPara
)
2649 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2651 long firstPos
= firstPara
->GetRange().GetStart();
2653 // Adjust for renumbering from zero
2654 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2657 fragment
.CalculateRange(0, end
);
2659 // Chop off the start of the paragraph
2660 if (topTailRange
.GetStart() > 0)
2662 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2663 firstPara
->DeleteRange(r
);
2665 // Make sure the numbering is correct
2666 fragment
.CalculateRange(0, end
);
2668 // Now, we've deleted some positions, so adjust the range
2670 topTailRange
.SetStart(range
.GetLength());
2671 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2675 topTailRange
.SetStart(range
.GetLength());
2676 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2679 if (topTailRange
.GetStart() < (lastPara
->GetRange().GetEnd()-1))
2681 lastPara
->DeleteRange(topTailRange
);
2683 // Make sure the numbering is correct
2685 fragment
.CalculateRange(0, end
);
2687 // We only have part of a paragraph at the end
2688 fragment
.SetPartialParagraph(true);
2692 // We have a partial paragraph (don't save last new paragraph marker)
2693 // or complete paragraph
2694 fragment
.SetPartialParagraph(isFragment
);
2701 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2702 /// starting from zero at the start of the buffer.
2703 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2710 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2713 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2714 // wxASSERT( child != NULL );
2718 if (child
->GetRange().Contains(pos
))
2720 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2723 wxRichTextLine
* line
= node2
->GetData();
2724 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2726 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2728 // If the caret is displayed at the end of the previous wrapped line,
2729 // we want to return the line it's _displayed_ at (not the actual line
2730 // containing the position).
2731 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2732 return lineCount
- 1;
2739 node2
= node2
->GetNext();
2741 // If we didn't find it in the lines, it must be
2742 // the last position of the paragraph. So return the last line.
2746 lineCount
+= child
->GetLines().GetCount();
2749 node
= node
->GetNext();
2756 /// Given a line number, get the corresponding wxRichTextLine object.
2757 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2761 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2764 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2765 // wxASSERT(child != NULL);
2769 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2771 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2774 wxRichTextLine
* line
= node2
->GetData();
2776 if (lineCount
== lineNumber
)
2781 node2
= node2
->GetNext();
2785 lineCount
+= child
->GetLines().GetCount();
2788 node
= node
->GetNext();
2795 /// Delete range from layout.
2796 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2798 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2800 wxRichTextParagraph
* firstPara
= NULL
;
2803 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2804 // wxASSERT (obj != NULL);
2806 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2810 // Delete the range in each paragraph
2812 if (!obj
->GetRange().IsOutside(range
))
2814 // Deletes the content of this object within the given range
2815 obj
->DeleteRange(range
);
2817 wxRichTextRange thisRange
= obj
->GetRange();
2818 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2820 // If the whole paragraph is within the range to delete,
2821 // delete the whole thing.
2822 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2824 // Delete the whole object
2825 RemoveChild(obj
, true);
2828 else if (!firstPara
)
2831 // If the range includes the paragraph end, we need to join this
2832 // and the next paragraph.
2833 if (range
.GetEnd() <= thisRange
.GetEnd())
2835 // We need to move the objects from the next paragraph
2836 // to this paragraph
2838 wxRichTextParagraph
* nextParagraph
= NULL
;
2839 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2840 nextParagraph
= obj
;
2843 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2845 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2848 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2850 wxRichTextAttr nextParaAttr
;
2851 if (applyFinalParagraphStyle
)
2853 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2854 // not the next one.
2855 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2856 nextParaAttr
= thisAttr
;
2858 nextParaAttr
= nextParagraph
->GetAttributes();
2861 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2863 // Move the objects to the previous para
2864 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2868 wxRichTextObject
* obj1
= node1
->GetData();
2870 firstPara
->AppendChild(obj1
);
2872 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2873 nextParagraph
->GetChildren().Erase(node1
);
2878 // Delete the paragraph
2879 RemoveChild(nextParagraph
, true);
2882 // Avoid empty paragraphs
2883 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2885 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2886 firstPara
->AppendChild(text
);
2889 if (applyFinalParagraphStyle
)
2890 firstPara
->SetAttributes(nextParaAttr
);
2903 /// Get any text in this object for the given range
2904 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2908 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2911 wxRichTextObject
* child
= node
->GetData();
2912 if (!child
->GetRange().IsOutside(range
))
2914 wxRichTextRange childRange
= range
;
2915 childRange
.LimitTo(child
->GetRange());
2917 wxString childText
= child
->GetTextForRange(childRange
);
2921 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2926 node
= node
->GetNext();
2932 /// Get all the text
2933 wxString
wxRichTextParagraphLayoutBox::GetText() const
2935 return GetTextForRange(GetOwnRange());
2938 /// Get the paragraph by number
2939 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2941 if ((size_t) paragraphNumber
>= GetChildCount())
2944 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2947 /// Get the length of the paragraph
2948 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2950 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2952 return para
->GetRange().GetLength() - 1; // don't include newline
2957 /// Get the text of the paragraph
2958 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2960 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2962 return para
->GetTextForRange(para
->GetRange());
2964 return wxEmptyString
;
2967 /// Convert zero-based line column and paragraph number to a position.
2968 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
2970 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
2973 return para
->GetRange().GetStart() + x
;
2979 /// Convert zero-based position to line column and paragraph number
2980 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
2982 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
2986 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2989 wxRichTextObject
* child
= node
->GetData();
2993 node
= node
->GetNext();
2997 *x
= pos
- para
->GetRange().GetStart();
3005 /// Get the leaf object in a paragraph at this position.
3006 /// Given a line number, get the corresponding wxRichTextLine object.
3007 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3009 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3012 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3016 wxRichTextObject
* child
= node
->GetData();
3017 if (child
->GetRange().Contains(position
))
3020 node
= node
->GetNext();
3022 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3023 return para
->GetChildren().GetLast()->GetData();
3028 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3029 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3031 bool characterStyle
= false;
3032 bool paragraphStyle
= false;
3034 if (style
.IsCharacterStyle())
3035 characterStyle
= true;
3036 if (style
.IsParagraphStyle())
3037 paragraphStyle
= true;
3039 wxRichTextBuffer
* buffer
= GetBuffer();
3041 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3042 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3043 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3044 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3045 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3046 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3048 // Apply paragraph style first, if any
3049 wxRichTextAttr
wholeStyle(style
);
3051 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3053 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3055 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3058 // Limit the attributes to be set to the content to only character attributes.
3059 wxRichTextAttr
characterAttributes(wholeStyle
);
3060 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3062 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3064 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3066 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3069 // If we are associated with a control, make undoable; otherwise, apply immediately
3072 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3074 wxRichTextAction
* action
= NULL
;
3076 if (haveControl
&& withUndo
)
3078 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3079 action
->SetRange(range
);
3080 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3083 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3086 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3087 // wxASSERT (para != NULL);
3089 if (para
&& para
->GetChildCount() > 0)
3091 // Stop searching if we're beyond the range of interest
3092 if (para
->GetRange().GetStart() > range
.GetEnd())
3095 if (!para
->GetRange().IsOutside(range
))
3097 // We'll be using a copy of the paragraph to make style changes,
3098 // not updating the buffer directly.
3099 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3101 if (haveControl
&& withUndo
)
3103 newPara
= new wxRichTextParagraph(*para
);
3104 action
->GetNewParagraphs().AppendChild(newPara
);
3106 // Also store the old ones for Undo
3107 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3112 // If we're specifying paragraphs only, then we really mean character formatting
3113 // to be included in the paragraph style
3114 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3118 // Removes the given style from the paragraph
3119 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3121 else if (resetExistingStyle
)
3122 newPara
->GetAttributes() = wholeStyle
;
3127 // Only apply attributes that will make a difference to the combined
3128 // style as seen on the display
3129 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3130 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3133 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3137 // When applying paragraph styles dynamically, don't change the text objects' attributes
3138 // since they will computed as needed. Only apply the character styling if it's _only_
3139 // character styling. This policy is subject to change and might be put under user control.
3141 // Hm. we might well be applying a mix of paragraph and character styles, in which
3142 // case we _do_ want to apply character styles regardless of what para styles are set.
3143 // But if we're applying a paragraph style, which has some character attributes, but
3144 // we only want the paragraphs to hold this character style, then we _don't_ want to
3145 // apply the character style. So we need to be able to choose.
3147 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3149 wxRichTextRange
childRange(range
);
3150 childRange
.LimitTo(newPara
->GetRange());
3152 // Find the starting position and if necessary split it so
3153 // we can start applying a different style.
3154 // TODO: check that the style actually changes or is different
3155 // from style outside of range
3156 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3157 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3159 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3160 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3162 firstObject
= newPara
->SplitAt(range
.GetStart());
3164 // Increment by 1 because we're apply the style one _after_ the split point
3165 long splitPoint
= childRange
.GetEnd();
3166 if (splitPoint
!= newPara
->GetRange().GetEnd())
3170 if (splitPoint
== newPara
->GetRange().GetEnd())
3171 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3173 // lastObject is set as a side-effect of splitting. It's
3174 // returned as the object before the new object.
3175 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3177 wxASSERT(firstObject
!= NULL
);
3178 wxASSERT(lastObject
!= NULL
);
3180 if (!firstObject
|| !lastObject
)
3183 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3184 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3186 wxASSERT(firstNode
);
3189 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3193 wxRichTextObject
* child
= node2
->GetData();
3197 // Removes the given style from the paragraph
3198 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3200 else if (resetExistingStyle
)
3201 child
->GetAttributes() = characterAttributes
;
3206 // Only apply attributes that will make a difference to the combined
3207 // style as seen on the display
3208 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3209 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3212 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3215 if (node2
== lastNode
)
3218 node2
= node2
->GetNext();
3224 node
= node
->GetNext();
3227 // Do action, or delay it until end of batch.
3228 if (haveControl
&& withUndo
)
3229 buffer
->SubmitAction(action
);
3234 // Just change the attributes for this single object.
3235 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3237 wxRichTextBuffer
* buffer
= GetBuffer();
3238 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3239 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3240 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3242 wxRichTextAction
*action
= NULL
;
3243 wxRichTextAttr newAttr
= obj
->GetAttributes();
3244 if (resetExistingStyle
)
3247 newAttr
.Apply(textAttr
);
3249 if (haveControl
&& withUndo
)
3251 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3252 action
->SetRange(obj
->GetRange().FromInternal());
3253 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3254 action
->MakeObject(obj
);
3256 action
->GetAttributes() = newAttr
;
3259 obj
->GetAttributes() = newAttr
;
3261 if (haveControl
&& withUndo
)
3262 buffer
->SubmitAction(action
);
3265 /// Get the text attributes for this position.
3266 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3268 return DoGetStyle(position
, style
, true);
3271 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3273 return DoGetStyle(position
, style
, false);
3276 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3277 /// context attributes.
3278 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3280 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3282 if (style
.IsParagraphStyle())
3284 obj
= GetParagraphAtPosition(position
);
3289 // Start with the base style
3290 style
= GetAttributes();
3292 // Apply the paragraph style
3293 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3296 style
= obj
->GetAttributes();
3303 obj
= GetLeafObjectAtPosition(position
);
3308 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3309 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3312 style
= obj
->GetAttributes();
3320 static bool wxHasStyle(long flags
, long style
)
3322 return (flags
& style
) != 0;
3325 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3327 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3329 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3334 /// Get the combined style for a range - if any attribute is different within the range,
3335 /// that attribute is not present within the flags.
3336 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3338 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3340 style
= wxRichTextAttr();
3342 wxRichTextAttr clashingAttr
;
3343 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3345 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3348 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3349 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3351 if (para
->GetChildren().GetCount() == 0)
3353 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3355 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3359 wxRichTextRange
paraRange(para
->GetRange());
3360 paraRange
.LimitTo(range
);
3362 // First collect paragraph attributes only
3363 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3364 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3365 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3367 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3371 wxRichTextObject
* child
= childNode
->GetData();
3372 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3374 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3376 // Now collect character attributes only
3377 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3379 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3382 childNode
= childNode
->GetNext();
3386 node
= node
->GetNext();
3391 /// Set default style
3392 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3394 m_defaultAttributes
= style
;
3398 /// Test if this whole range has character attributes of the specified kind. If any
3399 /// of the attributes are different within the range, the test fails. You
3400 /// can use this to implement, for example, bold button updating. style must have
3401 /// flags indicating which attributes are of interest.
3402 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3405 int matchingCount
= 0;
3407 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3410 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3411 // wxASSERT (para != NULL);
3415 // Stop searching if we're beyond the range of interest
3416 if (para
->GetRange().GetStart() > range
.GetEnd())
3417 return foundCount
== matchingCount
&& foundCount
!= 0;
3419 if (!para
->GetRange().IsOutside(range
))
3421 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3425 wxRichTextObject
* child
= node2
->GetData();
3426 // Allow for empty string if no buffer
3427 wxRichTextRange childRange
= child
->GetRange();
3428 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3429 childRange
.SetEnd(childRange
.GetEnd()+1);
3431 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3434 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3436 if (wxTextAttrEqPartial(textAttr
, style
))
3440 node2
= node2
->GetNext();
3445 node
= node
->GetNext();
3448 return foundCount
== matchingCount
&& foundCount
!= 0;
3451 /// Test if this whole range has paragraph attributes of the specified kind. If any
3452 /// of the attributes are different within the range, the test fails. You
3453 /// can use this to implement, for example, centering button updating. style must have
3454 /// flags indicating which attributes are of interest.
3455 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3458 int matchingCount
= 0;
3460 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3463 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3464 // wxASSERT (para != NULL);
3468 // Stop searching if we're beyond the range of interest
3469 if (para
->GetRange().GetStart() > range
.GetEnd())
3470 return foundCount
== matchingCount
&& foundCount
!= 0;
3472 if (!para
->GetRange().IsOutside(range
))
3474 wxRichTextAttr textAttr
= GetAttributes();
3475 // Apply the paragraph style
3476 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3479 if (wxTextAttrEqPartial(textAttr
, style
))
3484 node
= node
->GetNext();
3486 return foundCount
== matchingCount
&& foundCount
!= 0;
3489 void wxRichTextParagraphLayoutBox::Reset()
3493 wxRichTextBuffer
* buffer
= GetBuffer();
3494 if (buffer
&& buffer
->GetRichTextCtrl())
3496 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3497 event
.SetEventObject(buffer
->GetRichTextCtrl());
3498 event
.SetContainer(this);
3500 buffer
->SendEvent(event
, true);
3503 AddParagraph(wxEmptyString
);
3505 InvalidateHierarchy(wxRICHTEXT_ALL
);
3508 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3509 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3511 wxRichTextCompositeObject::Invalidate(invalidRange
);
3513 DoInvalidate(invalidRange
);
3516 // Do the (in)validation for this object only
3517 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3519 if (invalidRange
== wxRICHTEXT_ALL
)
3521 m_invalidRange
= wxRICHTEXT_ALL
;
3523 // Already invalidating everything
3524 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3529 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3530 m_invalidRange
.SetStart(invalidRange
.GetStart());
3531 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3532 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3536 // Do the (in)validation both up and down the hierarchy
3537 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3539 Invalidate(invalidRange
);
3541 if (invalidRange
!= wxRICHTEXT_NONE
)
3543 // Now go up the hierarchy
3544 wxRichTextObject
* thisObj
= this;
3545 wxRichTextObject
* p
= GetParent();
3548 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3550 l
->DoInvalidate(thisObj
->GetRange());
3558 /// Get invalid range, rounding to entire paragraphs if argument is true.
3559 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3561 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3562 return m_invalidRange
;
3564 wxRichTextRange range
= m_invalidRange
;
3566 if (wholeParagraphs
)
3568 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3570 range
.SetStart(para1
->GetRange().GetStart());
3571 // floating layout make all child should be relayout
3572 range
.SetEnd(GetOwnRange().GetEnd());
3577 /// Apply the style sheet to the buffer, for example if the styles have changed.
3578 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3580 wxASSERT(styleSheet
!= NULL
);
3586 wxRichTextAttr
attr(GetBasicStyle());
3587 if (GetBasicStyle().HasParagraphStyleName())
3589 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3592 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3593 SetBasicStyle(attr
);
3598 if (GetBasicStyle().HasCharacterStyleName())
3600 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3603 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3604 SetBasicStyle(attr
);
3609 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3612 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3613 // wxASSERT (para != NULL);
3617 // Combine paragraph and list styles. If there is a list style in the original attributes,
3618 // the current indentation overrides anything else and is used to find the item indentation.
3619 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3620 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3621 // exception as above).
3622 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3623 // So when changing a list style interactively, could retrieve level based on current style, then
3624 // set appropriate indent and apply new style.
3628 if (para
->GetAttributes().HasOutlineLevel())
3629 outline
= para
->GetAttributes().GetOutlineLevel();
3630 if (para
->GetAttributes().HasBulletNumber())
3631 num
= para
->GetAttributes().GetBulletNumber();
3633 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3635 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3637 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3638 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3639 if (paraDef
&& !listDef
)
3641 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3644 else if (listDef
&& !paraDef
)
3646 // Set overall style defined for the list style definition
3647 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3649 // Apply the style for this level
3650 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3653 else if (listDef
&& paraDef
)
3655 // Combines overall list style, style for level, and paragraph style
3656 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3660 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3662 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3664 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3666 // Overall list definition style
3667 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3669 // Style for this level
3670 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3674 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3676 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3679 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3685 para
->GetAttributes().SetOutlineLevel(outline
);
3687 para
->GetAttributes().SetBulletNumber(num
);
3690 node
= node
->GetNext();
3692 return foundCount
!= 0;
3696 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3698 wxRichTextBuffer
* buffer
= GetBuffer();
3699 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3701 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3702 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3703 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3704 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3706 // Current number, if numbering
3709 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3711 // If we are associated with a control, make undoable; otherwise, apply immediately
3714 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3716 wxRichTextAction
* action
= NULL
;
3718 if (haveControl
&& withUndo
)
3720 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3721 action
->SetRange(range
);
3722 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3725 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3728 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3729 // wxASSERT (para != NULL);
3731 if (para
&& para
->GetChildCount() > 0)
3733 // Stop searching if we're beyond the range of interest
3734 if (para
->GetRange().GetStart() > range
.GetEnd())
3737 if (!para
->GetRange().IsOutside(range
))
3739 // We'll be using a copy of the paragraph to make style changes,
3740 // not updating the buffer directly.
3741 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3743 if (haveControl
&& withUndo
)
3745 newPara
= new wxRichTextParagraph(*para
);
3746 action
->GetNewParagraphs().AppendChild(newPara
);
3748 // Also store the old ones for Undo
3749 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3756 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3757 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3759 // How is numbering going to work?
3760 // If we are renumbering, or numbering for the first time, we need to keep
3761 // track of the number for each level. But we might be simply applying a different
3763 // In Word, applying a style to several paragraphs, even if at different levels,
3764 // reverts the level back to the same one. So we could do the same here.
3765 // Renumbering will need to be done when we promote/demote a paragraph.
3767 // Apply the overall list style, and item style for this level
3768 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3769 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3771 // Now we need to do numbering
3774 newPara
->GetAttributes().SetBulletNumber(n
);
3779 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3781 // if def is NULL, remove list style, applying any associated paragraph style
3782 // to restore the attributes
3784 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3785 newPara
->GetAttributes().SetLeftIndent(0, 0);
3786 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3788 // Eliminate the main list-related attributes
3789 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
);
3791 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3793 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3796 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3803 node
= node
->GetNext();
3806 // Do action, or delay it until end of batch.
3807 if (haveControl
&& withUndo
)
3808 buffer
->SubmitAction(action
);
3813 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3815 wxRichTextBuffer
* buffer
= GetBuffer();
3816 if (buffer
&& buffer
->GetStyleSheet())
3818 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3820 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
3825 /// Clear list for given range
3826 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3828 return SetListStyle(range
, NULL
, flags
);
3831 /// Number/renumber any list elements in the given range
3832 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3834 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
3837 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3838 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
3839 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3841 wxRichTextBuffer
* buffer
= GetBuffer();
3842 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3844 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3845 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3847 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3850 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3852 // Max number of levels
3853 const int maxLevels
= 10;
3855 // The level we're looking at now
3856 int currentLevel
= -1;
3858 // The item number for each level
3859 int levels
[maxLevels
];
3862 // Reset all numbering
3863 for (i
= 0; i
< maxLevels
; i
++)
3865 if (startFrom
!= -1)
3866 levels
[i
] = startFrom
-1;
3867 else if (renumber
) // start again
3870 levels
[i
] = -1; // start from the number we found, if any
3873 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3875 // If we are associated with a control, make undoable; otherwise, apply immediately
3878 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3880 wxRichTextAction
* action
= NULL
;
3882 if (haveControl
&& withUndo
)
3884 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3885 action
->SetRange(range
);
3886 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3889 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3892 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3893 // wxASSERT (para != NULL);
3895 if (para
&& para
->GetChildCount() > 0)
3897 // Stop searching if we're beyond the range of interest
3898 if (para
->GetRange().GetStart() > range
.GetEnd())
3901 if (!para
->GetRange().IsOutside(range
))
3903 // We'll be using a copy of the paragraph to make style changes,
3904 // not updating the buffer directly.
3905 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3907 if (haveControl
&& withUndo
)
3909 newPara
= new wxRichTextParagraph(*para
);
3910 action
->GetNewParagraphs().AppendChild(newPara
);
3912 // Also store the old ones for Undo
3913 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3918 wxRichTextListStyleDefinition
* defToUse
= def
;
3921 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
3922 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
3927 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3928 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
3930 // If we've specified a level to apply to all, change the level.
3931 if (specifiedLevel
!= -1)
3932 thisLevel
= specifiedLevel
;
3934 // Do promotion if specified
3935 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
3937 thisLevel
= thisLevel
- promoteBy
;
3944 // Apply the overall list style, and item style for this level
3945 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3946 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3948 // OK, we've (re)applied the style, now let's get the numbering right.
3950 if (currentLevel
== -1)
3951 currentLevel
= thisLevel
;
3953 // Same level as before, do nothing except increment level's number afterwards
3954 if (currentLevel
== thisLevel
)
3957 // A deeper level: start renumbering all levels after current level
3958 else if (thisLevel
> currentLevel
)
3960 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
3964 currentLevel
= thisLevel
;
3966 else if (thisLevel
< currentLevel
)
3968 currentLevel
= thisLevel
;
3971 // Use the current numbering if -1 and we have a bullet number already
3972 if (levels
[currentLevel
] == -1)
3974 if (newPara
->GetAttributes().HasBulletNumber())
3975 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
3977 levels
[currentLevel
] = 1;
3981 levels
[currentLevel
] ++;
3984 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
3986 // Create the bullet text if an outline list
3987 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
3990 for (i
= 0; i
<= currentLevel
; i
++)
3992 if (!text
.IsEmpty())
3994 text
+= wxString::Format(wxT("%d"), levels
[i
]);
3996 newPara
->GetAttributes().SetBulletText(text
);
4002 node
= node
->GetNext();
4005 // Do action, or delay it until end of batch.
4006 if (haveControl
&& withUndo
)
4007 buffer
->SubmitAction(action
);
4012 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4014 wxRichTextBuffer
* buffer
= GetBuffer();
4015 if (buffer
->GetStyleSheet())
4017 wxRichTextListStyleDefinition
* def
= NULL
;
4018 if (!defName
.IsEmpty())
4019 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4020 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4025 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4026 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4029 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4030 // to NumberList with a flag indicating promotion is required within one of the ranges.
4031 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4032 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4033 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4034 // list position will start from 1.
4035 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4036 // We can end the renumbering at this point.
4038 // For now, only renumber within the promotion range.
4040 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4043 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4045 wxRichTextBuffer
* buffer
= GetBuffer();
4046 if (buffer
->GetStyleSheet())
4048 wxRichTextListStyleDefinition
* def
= NULL
;
4049 if (!defName
.IsEmpty())
4050 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4051 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4056 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4057 /// position of the paragraph that it had to start looking from.
4058 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4060 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4063 wxRichTextBuffer
* buffer
= GetBuffer();
4064 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4065 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4067 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4070 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4071 // int thisLevel = def->FindLevelForIndent(thisIndent);
4073 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4075 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4076 if (previousParagraph
->GetAttributes().HasBulletName())
4077 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4078 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4079 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4081 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4082 attr
.SetBulletNumber(nextNumber
);
4086 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4087 if (!text
.IsEmpty())
4089 int pos
= text
.Find(wxT('.'), true);
4090 if (pos
!= wxNOT_FOUND
)
4092 text
= text
.Mid(0, text
.Length() - pos
- 1);
4095 text
= wxEmptyString
;
4096 if (!text
.IsEmpty())
4098 text
+= wxString::Format(wxT("%d"), nextNumber
);
4099 attr
.SetBulletText(text
);
4113 * wxRichTextParagraph
4114 * This object represents a single paragraph (or in a straight text editor, a line).
4117 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4119 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4121 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4122 wxRichTextCompositeObject(parent
)
4125 SetAttributes(*style
);
4128 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4129 wxRichTextCompositeObject(parent
)
4132 SetAttributes(*paraStyle
);
4134 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4137 wxRichTextParagraph::~wxRichTextParagraph()
4143 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4148 // Currently we don't merge these attributes with the parent, but we
4149 // should consider whether we should (e.g. if we set a border colour
4150 // for all paragraphs). But generally box attributes are likely to be
4151 // different for different objects.
4152 wxRect paraRect
= GetRect();
4153 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4155 wxRichTextAttr attr
= GetCombinedAttributes();
4157 // Draw the bullet, if any
4158 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4160 if (attr
.GetLeftSubIndent() != 0)
4162 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4163 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4165 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4167 // Combine with the font of the first piece of content, if one is specified
4168 if (GetChildren().GetCount() > 0)
4170 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4171 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4173 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4177 // Get line height from first line, if any
4178 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4181 int lineHeight
wxDUMMY_INITIALIZE(0);
4184 lineHeight
= line
->GetSize().y
;
4185 linePos
= line
->GetPosition() + GetPosition();
4190 if (bulletAttr
.HasFont() && GetBuffer())
4191 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4193 font
= (*wxNORMAL_FONT
);
4195 wxCheckSetFont(dc
, font
);
4197 lineHeight
= dc
.GetCharHeight();
4198 linePos
= GetPosition();
4199 linePos
.y
+= spaceBeforePara
;
4202 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4204 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4206 if (wxRichTextBuffer::GetRenderer())
4207 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4209 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4211 if (wxRichTextBuffer::GetRenderer())
4212 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4216 wxString bulletText
= GetBulletText();
4218 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4219 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4224 // Draw the range for each line, one object at a time.
4226 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4229 wxRichTextLine
* line
= node
->GetData();
4230 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4232 // Lines are specified relative to the paragraph
4234 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4236 // Don't draw if off the screen
4237 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4239 wxPoint objectPosition
= linePosition
;
4240 int maxDescent
= line
->GetDescent();
4242 // Loop through objects until we get to the one within range
4243 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4248 wxRichTextObject
* child
= node2
->GetData();
4250 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4252 // Draw this part of the line at the correct position
4253 wxRichTextRange
objectRange(child
->GetRange());
4254 objectRange
.LimitTo(lineRange
);
4257 if (child
->IsTopLevel())
4259 objectSize
= child
->GetCachedSize();
4260 objectRange
= child
->GetOwnRange();
4264 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4265 if (i
< (int) line
->GetObjectSizes().GetCount())
4267 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4273 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4277 // Use the child object's width, but the whole line's height
4278 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4279 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4281 objectPosition
.x
+= objectSize
.x
;
4284 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4285 // Can break out of inner loop now since we've passed this line's range
4288 node2
= node2
->GetNext();
4292 node
= node
->GetNext();
4298 // Get the range width using partial extents calculated for the whole paragraph.
4299 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4301 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4303 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4306 int leftMostPos
= 0;
4307 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4308 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4310 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4312 int w
= rightMostPos
- leftMostPos
;
4317 /// Lay the item out
4318 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
4320 // Deal with floating objects firstly before the normal layout
4321 wxRichTextBuffer
* buffer
= GetBuffer();
4323 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4324 wxASSERT(collector
);
4325 LayoutFloat(dc
, rect
, style
, collector
);
4327 wxRichTextAttr attr
= GetCombinedAttributes();
4331 // Increase the size of the paragraph due to spacing
4332 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4333 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4334 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4335 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4336 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4338 int lineSpacing
= 0;
4340 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4341 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4343 wxCheckSetFont(dc
, attr
.GetFont());
4344 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4347 // Start position for each line relative to the paragraph
4348 int startPositionFirstLine
= leftIndent
;
4349 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4351 // If we have a bullet in this paragraph, the start position for the first line's text
4352 // is actually leftIndent + leftSubIndent.
4353 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4354 startPositionFirstLine
= startPositionSubsequentLines
;
4356 long lastEndPos
= GetRange().GetStart()-1;
4357 long lastCompletedEndPos
= lastEndPos
;
4359 int currentWidth
= 0;
4360 SetPosition(rect
.GetPosition());
4362 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4365 int maxHeight
= currentPosition
.y
;
4370 int lineDescent
= 0;
4372 wxRichTextObjectList::compatibility_iterator node
;
4374 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4376 wxArrayInt partialExtents
;
4379 int paraDescent
= 0;
4381 // This calculates the partial text extents
4382 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4384 node
= m_children
.GetFirst();
4387 wxRichTextObject
* child
= node
->GetData();
4389 //child->SetCachedSize(wxDefaultSize);
4390 child
->Layout(dc
, rect
, style
);
4392 node
= node
->GetNext();
4399 // We may need to go back to a previous child, in which case create the new line,
4400 // find the child corresponding to the start position of the string, and
4403 wxRect availableRect
;
4405 node
= m_children
.GetFirst();
4408 wxRichTextObject
* child
= node
->GetData();
4410 // If floating, ignore. We already laid out floats.
4411 // Also ignore if empty object, except if we haven't got any
4413 if (child
->IsFloating() || !child
->IsShown() ||
4414 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4417 node
= node
->GetNext();
4421 // If this is e.g. a composite text box, it will need to be laid out itself.
4422 // But if just a text fragment or image, for example, this will
4423 // do nothing. NB: won't we need to set the position after layout?
4424 // since for example if position is dependent on vertical line size, we
4425 // can't tell the position until the size is determined. So possibly introduce
4426 // another layout phase.
4428 // We may only be looking at part of a child, if we searched back for wrapping
4429 // and found a suitable point some way into the child. So get the size for the fragment
4432 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4433 long lastPosToUse
= child
->GetRange().GetEnd();
4434 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4436 if (lineBreakInThisObject
)
4437 lastPosToUse
= nextBreakPos
;
4440 int childDescent
= 0;
4442 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4443 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4444 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4446 if (child
->IsTopLevel())
4448 wxSize oldSize
= child
->GetCachedSize();
4450 child
->Invalidate(wxRICHTEXT_ALL
);
4451 child
->SetPosition(wxPoint(0, 0));
4453 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4454 // lays out the object again using the minimum size
4455 // The position will be determined by its location in its line,
4456 // and not by the child's actual position.
4457 child
->LayoutToBestSize(dc
, buffer
,
4458 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4460 if (oldSize
!= child
->GetCachedSize())
4462 partialExtents
.Clear();
4464 // Recalculate the partial text extents since the child object changed size
4465 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4469 // Problem: we need to layout composites here for which we need the available width,
4470 // but we can't get the available width without using the float collector which
4471 // needs to know the object height.
4473 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4475 childSize
= child
->GetCachedSize();
4476 childDescent
= child
->GetDescent();
4480 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4481 // Get height only, then the width using the partial extents
4482 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4483 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4485 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4490 int loopIterations
= 0;
4492 // If there are nested objects that need to lay themselves out, we have to do this in a
4493 // loop because the height of the object may well depend on the available width.
4494 // And because of floating object positioning, the available width depends on the
4495 // height of the object and whether it will clash with the floating objects.
4496 // So, we see whether the available width changes due to the presence of floating images.
4497 // If it does, then we'll use the new restricted width to find the object height again.
4498 // If this causes another restriction in the available width, we'll try again, until
4499 // either we lose patience or the available width settles down.
4504 wxRect oldAvailableRect
= availableRect
;
4506 // Available width depends on the floating objects and the line height.
4507 // Note: the floating objects may be placed vertically along the two side of
4508 // buffer, so we may have different available line widths with different
4509 // [startY, endY]. So, we can't determine how wide the available
4510 // space is until we know the exact line height.
4511 lineDescent
= wxMax(childDescent
, maxDescent
);
4512 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4513 lineHeight
= lineDescent
+ lineAscent
;
4514 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4516 // Adjust availableRect to the space that is available when taking floating objects into account.
4518 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4520 int newX
= floatAvailableRect
.x
+ startOffset
;
4521 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4522 availableRect
.x
= newX
;
4523 availableRect
.width
= newW
;
4526 if (floatAvailableRect
.width
< availableRect
.width
)
4527 availableRect
.width
= floatAvailableRect
.width
;
4529 currentPosition
.x
= availableRect
.x
- rect
.x
;
4531 if (child
->IsTopLevel() && loopIterations
<= 20)
4533 if (availableRect
!= oldAvailableRect
)
4535 wxSize oldSize
= child
->GetCachedSize();
4537 //child->SetCachedSize(wxDefaultSize);
4538 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4539 // lays out the object again using the minimum size
4540 child
->Invalidate(wxRICHTEXT_ALL
);
4541 child
->LayoutToBestSize(dc
, buffer
,
4542 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4543 childSize
= child
->GetCachedSize();
4544 childDescent
= child
->GetDescent();
4545 //child->SetPosition(availableRect.GetPosition());
4547 if (oldSize
!= child
->GetCachedSize())
4549 partialExtents
.Clear();
4551 // Recalculate the partial text extents since the child object changed size
4552 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4555 // Go around the loop finding the available rect for the given floating objects
4566 // 1) There was a line break BEFORE the natural break
4567 // 2) There was a line break AFTER the natural break
4568 // 3) It's the last line
4569 // 4) The child still fits (carry on) - 'else' clause
4571 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4573 (childSize
.x
+ currentWidth
> availableRect
.width
)
4575 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4579 if (child
->IsTopLevel())
4581 // We can move it to the correct position at this point
4582 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4585 long wrapPosition
= 0;
4586 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4587 wrapPosition
= child
->GetRange().GetEnd();
4590 // Find a place to wrap. This may walk back to previous children,
4591 // for example if a word spans several objects.
4592 // Note: one object must contains only one wxTextAtrr, so the line height will not
4593 // change inside one object. Thus, we can pass the remain line width to the
4594 // FindWrapPosition function.
4595 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4597 // If the function failed, just cut it off at the end of this child.
4598 wrapPosition
= child
->GetRange().GetEnd();
4601 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4602 if (wrapPosition
<= lastCompletedEndPos
)
4603 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4605 // Line end position shouldn't be the same as the end, or greater.
4606 if (wrapPosition
>= GetRange().GetEnd())
4607 wrapPosition
= GetRange().GetEnd()-1;
4609 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4611 // Let's find the actual size of the current line now
4613 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4615 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4616 /// for the fragment we're about to add.
4617 childDescent
= maxDescent
;
4619 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4620 if (!child
->IsEmpty())
4622 // Get height only, then the width using the partial extents
4623 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4624 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4628 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4630 currentWidth
= actualSize
.x
;
4631 maxDescent
= wxMax(childDescent
, maxDescent
);
4632 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4633 lineHeight
= maxDescent
+ maxAscent
;
4635 if (lineHeight
== 0 && buffer
)
4637 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4638 wxCheckSetFont(dc
, font
);
4639 lineHeight
= dc
.GetCharHeight();
4642 if (maxDescent
== 0)
4645 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4649 wxRichTextLine
* line
= AllocateLine(lineCount
);
4651 // Set relative range so we won't have to change line ranges when paragraphs are moved
4652 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4653 line
->SetPosition(currentPosition
);
4654 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4655 line
->SetDescent(maxDescent
);
4657 maxHeight
= currentPosition
.y
+ lineHeight
;
4659 // Now move down a line. TODO: add margins, spacing
4660 currentPosition
.y
+= lineHeight
;
4661 currentPosition
.y
+= lineSpacing
;
4664 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4669 // TODO: account for zero-length objects, such as fields
4670 // wxASSERT(wrapPosition > lastCompletedEndPos);
4672 lastEndPos
= wrapPosition
;
4673 lastCompletedEndPos
= lastEndPos
;
4677 if (wrapPosition
< GetRange().GetEnd()-1)
4679 // May need to set the node back to a previous one, due to searching back in wrapping
4680 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4681 if (childAfterWrapPosition
)
4682 node
= m_children
.Find(childAfterWrapPosition
);
4684 node
= node
->GetNext();
4687 node
= node
->GetNext();
4689 // Apply paragraph styles such as alignment to the wrapped line
4690 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4694 // We still fit, so don't add a line, and keep going
4695 currentWidth
+= childSize
.x
;
4696 maxDescent
= wxMax(childDescent
, maxDescent
);
4697 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4698 lineHeight
= maxDescent
+ maxAscent
;
4700 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4701 lastEndPos
= child
->GetRange().GetEnd();
4703 node
= node
->GetNext();
4707 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4709 // Remove remaining unused line objects, if any
4710 ClearUnusedLines(lineCount
);
4712 // We need to add back the margins etc.
4714 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4715 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4716 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4717 SetCachedSize(marginRect
.GetSize());
4720 // The maximum size is the length of the paragraph stretched out into a line.
4721 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4722 // this size. TODO: take into account line breaks.
4724 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4725 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
, currentPosition
.y
+ spaceAfterPara
));
4726 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4727 SetMaxSize(marginRect
.GetSize());
4730 // Find the greatest minimum size. Currently we only look at non-text objects,
4731 // which isn't ideal but it would be slow to find the maximum word width to
4732 // use as the minimum.
4735 node
= m_children
.GetFirst();
4738 wxRichTextObject
* child
= node
->GetData();
4740 // If floating, ignore. We already laid out floats.
4741 // Also ignore if empty object, except if we haven't got any
4743 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4745 if (child
->GetCachedSize().x
> minWidth
)
4746 minWidth
= child
->GetMinSize().x
;
4748 node
= node
->GetNext();
4751 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4752 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4753 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4754 SetMinSize(marginRect
.GetSize());
4758 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4759 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4760 // Use the text extents to calculate the size of each fragment in each line
4761 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4764 wxRichTextLine
* line
= lineNode
->GetData();
4765 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4767 // Loop through objects until we get to the one within range
4768 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4772 wxRichTextObject
* child
= node2
->GetData();
4774 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4776 wxRichTextRange rangeToUse
= lineRange
;
4777 rangeToUse
.LimitTo(child
->GetRange());
4779 // Find the size of the child from the text extents, and store in an array
4780 // for drawing later
4782 if (rangeToUse
.GetStart() > GetRange().GetStart())
4783 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4784 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4785 int sz
= right
- left
;
4786 line
->GetObjectSizes().Add(sz
);
4788 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4789 // Can break out of inner loop now since we've passed this line's range
4792 node2
= node2
->GetNext();
4795 lineNode
= lineNode
->GetNext();
4803 /// Apply paragraph styles, such as centering, to wrapped lines
4804 /// TODO: take into account box attributes, possibly
4805 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4807 if (!attr
.HasAlignment())
4810 wxPoint pos
= line
->GetPosition();
4811 wxSize size
= line
->GetSize();
4813 // centering, right-justification
4814 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4816 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4817 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
4818 line
->SetPosition(pos
);
4820 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4822 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4823 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
4824 line
->SetPosition(pos
);
4828 /// Insert text at the given position
4829 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
4831 wxRichTextObject
* childToUse
= NULL
;
4832 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
4834 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4837 wxRichTextObject
* child
= node
->GetData();
4838 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
4845 node
= node
->GetNext();
4850 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
4853 int posInString
= pos
- textObject
->GetRange().GetStart();
4855 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
4856 text
+ textObject
->GetText().Mid(posInString
);
4857 textObject
->SetText(newText
);
4859 int textLength
= text
.length();
4861 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
4862 textObject
->GetRange().GetEnd() + textLength
));
4864 // Increment the end range of subsequent fragments in this paragraph.
4865 // We'll set the paragraph range itself at a higher level.
4867 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
4870 wxRichTextObject
* child
= node
->GetData();
4871 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
4872 textObject
->GetRange().GetEnd() + textLength
));
4874 node
= node
->GetNext();
4881 // TODO: if not a text object, insert at closest position, e.g. in front of it
4887 // Don't pass parent initially to suppress auto-setting of parent range.
4888 // We'll do that at a higher level.
4889 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
4891 AppendChild(textObject
);
4898 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
4900 wxRichTextCompositeObject::Copy(obj
);
4903 /// Clear the cached lines
4904 void wxRichTextParagraph::ClearLines()
4906 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
4909 /// Get/set the object size for the given range. Returns false if the range
4910 /// is invalid for this object.
4911 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
4913 if (!range
.IsWithin(GetRange()))
4916 if (flags
& wxRICHTEXT_UNFORMATTED
)
4918 // Just use unformatted data, assume no line breaks
4919 // TODO: take into account line breaks
4923 wxArrayInt childExtents
;
4930 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4934 wxRichTextObject
* child
= node
->GetData();
4935 if (!child
->GetRange().IsOutside(range
))
4937 // Floating objects have a zero size within the paragraph.
4938 if (child
->IsFloating())
4943 if (partialExtents
->GetCount() > 0)
4944 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4948 partialExtents
->Add(0 /* zero size */ + lastSize
);
4955 wxRichTextRange rangeToUse
= range
;
4956 rangeToUse
.LimitTo(child
->GetRange());
4958 if (child
->IsTopLevel())
4959 rangeToUse
= child
->GetOwnRange();
4961 int childDescent
= 0;
4963 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4964 // but it's only going to be used after caching has taken place.
4965 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
4967 childDescent
= child
->GetDescent();
4968 childSize
= child
->GetCachedSize();
4970 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4971 sz
.x
+= childSize
.x
;
4972 descent
= wxMax(descent
, childDescent
);
4974 else if (child
->IsTopLevel())
4976 childDescent
= child
->GetDescent();
4977 childSize
= child
->GetCachedSize();
4979 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4980 sz
.x
+= childSize
.x
;
4981 descent
= wxMax(descent
, childDescent
);
4982 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
4984 child
->SetCachedSize(childSize
);
4985 child
->SetDescent(childDescent
);
4991 if (partialExtents
->GetCount() > 0)
4992 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4996 partialExtents
->Add(childSize
.x
+ lastSize
);
4999 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5001 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5002 sz
.x
+= childSize
.x
;
5003 descent
= wxMax(descent
, childDescent
);
5005 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5007 child
->SetCachedSize(childSize
);
5008 child
->SetDescent(childDescent
);
5014 if (partialExtents
->GetCount() > 0)
5015 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5020 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5022 partialExtents
->Add(childExtents
[i
] + lastSize
);
5032 node
= node
->GetNext();
5038 // Use formatted data, with line breaks
5041 // We're going to loop through each line, and then for each line,
5042 // call GetRangeSize for the fragment that comprises that line.
5043 // Only we have to do that multiple times within the line, because
5044 // the line may be broken into pieces. For now ignore line break commands
5045 // (so we can assume that getting the unformatted size for a fragment
5046 // within a line is the actual size)
5048 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5051 wxRichTextLine
* line
= node
->GetData();
5052 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5053 if (!lineRange
.IsOutside(range
))
5057 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5060 wxRichTextObject
* child
= node2
->GetData();
5062 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5064 wxRichTextRange rangeToUse
= lineRange
;
5065 rangeToUse
.LimitTo(child
->GetRange());
5066 if (child
->IsTopLevel())
5067 rangeToUse
= child
->GetOwnRange();
5070 int childDescent
= 0;
5071 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5073 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5074 lineSize
.x
+= childSize
.x
;
5076 descent
= wxMax(descent
, childDescent
);
5079 node2
= node2
->GetNext();
5082 // Increase size by a line (TODO: paragraph spacing)
5084 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5086 node
= node
->GetNext();
5093 /// Finds the absolute position and row height for the given character position
5094 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5098 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5100 *height
= line
->GetSize().y
;
5102 *height
= dc
.GetCharHeight();
5104 // -1 means 'the start of the buffer'.
5107 pt
= pt
+ line
->GetPosition();
5112 // The final position in a paragraph is taken to mean the position
5113 // at the start of the next paragraph.
5114 if (index
== GetRange().GetEnd())
5116 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5117 wxASSERT( parent
!= NULL
);
5119 // Find the height at the next paragraph, if any
5120 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5123 *height
= line
->GetSize().y
;
5124 pt
= line
->GetAbsolutePosition();
5128 *height
= dc
.GetCharHeight();
5129 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5130 pt
= wxPoint(indent
, GetCachedSize().y
);
5136 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5139 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5142 wxRichTextLine
* line
= node
->GetData();
5143 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5144 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5146 // If this is the last point in the line, and we're forcing the
5147 // returned value to be the start of the next line, do the required
5149 if (index
== lineRange
.GetEnd() && forceLineStart
)
5151 if (node
->GetNext())
5153 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5154 *height
= nextLine
->GetSize().y
;
5155 pt
= nextLine
->GetAbsolutePosition();
5160 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5162 wxRichTextRange
r(lineRange
.GetStart(), index
);
5166 // We find the size of the line up to this point,
5167 // then we can add this size to the line start position and
5168 // paragraph start position to find the actual position.
5170 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5172 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5173 *height
= line
->GetSize().y
;
5180 node
= node
->GetNext();
5186 /// Hit-testing: returns a flag indicating hit test details, plus
5187 /// information about position
5188 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5191 return wxRICHTEXT_HITTEST_NONE
;
5193 // If we're in the top-level container, then we can return
5194 // a suitable hit test code even if the point is outside the container area,
5195 // so that we can position the caret sensibly even if we don't
5196 // click on valid content. If we're not at the top-level, and the point
5197 // is not within this paragraph object, then we don't want to stop more
5198 // precise hit-testing from working prematurely, so return immediately.
5199 // NEW STRATEGY: use the parent boundary to test whether we're in the
5200 // right region, not the paragraph, since the paragraph may be positioned
5201 // some way in from where the user clicks.
5204 wxRichTextObject
* tempObj
, *tempContextObj
;
5205 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5206 return wxRICHTEXT_HITTEST_NONE
;
5209 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5212 wxRichTextObject
* child
= objNode
->GetData();
5213 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5216 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5217 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5222 objNode
= objNode
->GetNext();
5225 wxPoint paraPos
= GetPosition();
5227 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5230 wxRichTextLine
* line
= node
->GetData();
5231 wxPoint linePos
= paraPos
+ line
->GetPosition();
5232 wxSize lineSize
= line
->GetSize();
5233 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5235 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5237 if (pt
.x
< linePos
.x
)
5239 textPosition
= lineRange
.GetStart();
5240 *obj
= FindObjectAtPosition(textPosition
);
5241 *contextObj
= GetContainer();
5242 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5244 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5246 textPosition
= lineRange
.GetEnd();
5247 *obj
= FindObjectAtPosition(textPosition
);
5248 *contextObj
= GetContainer();
5249 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5253 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5254 wxArrayInt partialExtents
;
5259 // This calculates the partial text extents
5260 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5262 int lastX
= linePos
.x
;
5264 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5266 int nextX
= partialExtents
[i
] + linePos
.x
;
5268 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5270 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5272 *obj
= FindObjectAtPosition(textPosition
);
5273 *contextObj
= GetContainer();
5275 // So now we know it's between i-1 and i.
5276 // Let's see if we can be more precise about
5277 // which side of the position it's on.
5279 int midPoint
= (nextX
+ lastX
)/2;
5280 if (pt
.x
>= midPoint
)
5281 return wxRICHTEXT_HITTEST_AFTER
;
5283 return wxRICHTEXT_HITTEST_BEFORE
;
5290 int lastX
= linePos
.x
;
5291 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5296 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5298 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5300 int nextX
= childSize
.x
+ linePos
.x
;
5302 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5306 *obj
= FindObjectAtPosition(textPosition
);
5307 *contextObj
= GetContainer();
5309 // So now we know it's between i-1 and i.
5310 // Let's see if we can be more precise about
5311 // which side of the position it's on.
5313 int midPoint
= (nextX
+ lastX
)/2;
5314 if (pt
.x
>= midPoint
)
5315 return wxRICHTEXT_HITTEST_AFTER
;
5317 return wxRICHTEXT_HITTEST_BEFORE
;
5328 node
= node
->GetNext();
5331 return wxRICHTEXT_HITTEST_NONE
;
5334 /// Split an object at this position if necessary, and return
5335 /// the previous object, or NULL if inserting at beginning.
5336 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5338 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5341 wxRichTextObject
* child
= node
->GetData();
5343 if (pos
== child
->GetRange().GetStart())
5347 if (node
->GetPrevious())
5348 *previousObject
= node
->GetPrevious()->GetData();
5350 *previousObject
= NULL
;
5356 if (child
->GetRange().Contains(pos
))
5358 // This should create a new object, transferring part of
5359 // the content to the old object and the rest to the new object.
5360 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5362 // If we couldn't split this object, just insert in front of it.
5365 // Maybe this is an empty string, try the next one
5370 // Insert the new object after 'child'
5371 if (node
->GetNext())
5372 m_children
.Insert(node
->GetNext(), newObject
);
5374 m_children
.Append(newObject
);
5375 newObject
->SetParent(this);
5378 *previousObject
= child
;
5384 node
= node
->GetNext();
5387 *previousObject
= NULL
;
5391 /// Move content to a list from obj on
5392 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5394 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5397 wxRichTextObject
* child
= node
->GetData();
5400 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5402 node
= node
->GetNext();
5404 m_children
.DeleteNode(oldNode
);
5408 /// Add content back from list
5409 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5411 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5413 AppendChild((wxRichTextObject
*) node
->GetData());
5418 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5420 wxRichTextCompositeObject::CalculateRange(start
, end
);
5422 // Add one for end of paragraph
5425 m_range
.SetRange(start
, end
);
5428 /// Find the object at the given position
5429 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5431 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5434 wxRichTextObject
* obj
= node
->GetData();
5435 if (obj
->GetRange().Contains(position
) ||
5436 obj
->GetRange().GetStart() == position
||
5437 obj
->GetRange().GetEnd() == position
)
5440 node
= node
->GetNext();
5445 /// Get the plain text searching from the start or end of the range.
5446 /// The resulting string may be shorter than the range given.
5447 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5449 text
= wxEmptyString
;
5453 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5456 wxRichTextObject
* obj
= node
->GetData();
5457 if (!obj
->GetRange().IsOutside(range
))
5459 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5462 text
+= textObj
->GetTextForRange(range
);
5470 node
= node
->GetNext();
5475 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5478 wxRichTextObject
* obj
= node
->GetData();
5479 if (!obj
->GetRange().IsOutside(range
))
5481 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5484 text
= textObj
->GetTextForRange(range
) + text
;
5488 text
= wxT(" ") + text
;
5492 node
= node
->GetPrevious();
5499 /// Find a suitable wrap position.
5500 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5502 if (range
.GetLength() <= 0)
5505 // Find the first position where the line exceeds the available space.
5507 long breakPosition
= range
.GetEnd();
5509 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5510 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5514 if (range
.GetStart() > GetRange().GetStart())
5515 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5520 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5522 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5524 if (widthFromStartOfThisRange
> availableSpace
)
5526 breakPosition
= i
-1;
5534 // Binary chop for speed
5535 long minPos
= range
.GetStart();
5536 long maxPos
= range
.GetEnd();
5539 if (minPos
== maxPos
)
5542 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5544 if (sz
.x
> availableSpace
)
5545 breakPosition
= minPos
- 1;
5548 else if ((maxPos
- minPos
) == 1)
5551 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5553 if (sz
.x
> availableSpace
)
5554 breakPosition
= minPos
- 1;
5557 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5558 if (sz
.x
> availableSpace
)
5559 breakPosition
= maxPos
-1;
5565 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5568 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5570 if (sz
.x
> availableSpace
)
5582 // Now we know the last position on the line.
5583 // Let's try to find a word break.
5586 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5588 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5589 if (newLinePos
!= wxNOT_FOUND
)
5591 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5595 int spacePos
= plainText
.Find(wxT(' '), true);
5596 int tabPos
= plainText
.Find(wxT('\t'), true);
5597 int pos
= wxMax(spacePos
, tabPos
);
5598 if (pos
!= wxNOT_FOUND
)
5600 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5601 breakPosition
= breakPosition
- positionsFromEndOfString
;
5606 wrapPosition
= breakPosition
;
5611 /// Get the bullet text for this paragraph.
5612 wxString
wxRichTextParagraph::GetBulletText()
5614 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5615 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5616 return wxEmptyString
;
5618 int number
= GetAttributes().GetBulletNumber();
5621 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5623 text
.Printf(wxT("%d"), number
);
5625 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5627 // TODO: Unicode, and also check if number > 26
5628 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5630 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5632 // TODO: Unicode, and also check if number > 26
5633 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5635 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5637 text
= wxRichTextDecimalToRoman(number
);
5639 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5641 text
= wxRichTextDecimalToRoman(number
);
5644 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5646 text
= GetAttributes().GetBulletText();
5649 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5651 // The outline style relies on the text being computed statically,
5652 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5653 // should be stored in the attributes; if not, just use the number for this
5654 // level, as previously computed.
5655 if (!GetAttributes().GetBulletText().IsEmpty())
5656 text
= GetAttributes().GetBulletText();
5659 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5661 text
= wxT("(") + text
+ wxT(")");
5663 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5665 text
= text
+ wxT(")");
5668 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5676 /// Allocate or reuse a line object
5677 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5679 if (pos
< (int) m_cachedLines
.GetCount())
5681 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5687 wxRichTextLine
* line
= new wxRichTextLine(this);
5688 m_cachedLines
.Append(line
);
5693 /// Clear remaining unused line objects, if any
5694 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5696 int cachedLineCount
= m_cachedLines
.GetCount();
5697 if ((int) cachedLineCount
> lineCount
)
5699 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5701 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5702 wxRichTextLine
* line
= node
->GetData();
5703 m_cachedLines
.Erase(node
);
5710 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5711 /// retrieve the actual style.
5712 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5714 wxRichTextAttr attr
;
5715 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5718 attr
= buf
->GetBasicStyle();
5719 if (!includingBoxAttr
)
5721 attr
.GetTextBoxAttr().Reset();
5722 // The background colour will be painted by the container, and we don't
5723 // want to unnecessarily overwrite the background when we're drawing text
5724 // because this may erase the guideline (which appears just under the text
5725 // if there's no padding).
5726 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5728 wxRichTextApplyStyle(attr
, GetAttributes());
5731 attr
= GetAttributes();
5733 wxRichTextApplyStyle(attr
, contentStyle
);
5737 /// Get combined attributes of the base style and paragraph style.
5738 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5740 wxRichTextAttr attr
;
5741 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5744 attr
= buf
->GetBasicStyle();
5745 if (!includingBoxAttr
)
5746 attr
.GetTextBoxAttr().Reset();
5747 wxRichTextApplyStyle(attr
, GetAttributes());
5750 attr
= GetAttributes();
5755 // Create default tabstop array
5756 void wxRichTextParagraph::InitDefaultTabs()
5758 // create a default tab list at 10 mm each.
5759 for (int i
= 0; i
< 20; ++i
)
5761 sm_defaultTabs
.Add(i
*100);
5765 // Clear default tabstop array
5766 void wxRichTextParagraph::ClearDefaultTabs()
5768 sm_defaultTabs
.Clear();
5771 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5773 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5776 wxRichTextObject
* anchored
= node
->GetData();
5777 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5781 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5784 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5786 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5787 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5789 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5793 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5795 /* Update the offset */
5796 int newOffsetY
= pos
- rect
.y
;
5797 if (newOffsetY
!= offsetY
)
5799 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5800 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5801 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5804 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5806 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5807 x
= rect
.x
+ rect
.width
- size
.x
;
5809 anchored
->SetPosition(wxPoint(x
, pos
));
5810 anchored
->SetCachedSize(size
);
5811 floatCollector
->CollectFloat(this, anchored
);
5814 node
= node
->GetNext();
5818 // Get the first position from pos that has a line break character.
5819 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
5821 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5824 wxRichTextObject
* obj
= node
->GetData();
5825 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
5827 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5830 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
5835 node
= node
->GetNext();
5842 * This object represents a line in a paragraph, and stores
5843 * offsets from the start of the paragraph representing the
5844 * start and end positions of the line.
5847 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
5853 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
5856 m_range
.SetRange(-1, -1);
5857 m_pos
= wxPoint(0, 0);
5858 m_size
= wxSize(0, 0);
5860 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5861 m_objectSizes
.Clear();
5866 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
5868 m_range
= obj
.m_range
;
5869 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5870 m_objectSizes
= obj
.m_objectSizes
;
5874 /// Get the absolute object position
5875 wxPoint
wxRichTextLine::GetAbsolutePosition() const
5877 return m_parent
->GetPosition() + m_pos
;
5880 /// Get the absolute range
5881 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
5883 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
5884 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
5889 * wxRichTextPlainText
5890 * This object represents a single piece of text.
5893 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
5895 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
5896 wxRichTextObject(parent
)
5899 SetAttributes(*style
);
5904 #define USE_KERNING_FIX 1
5906 // If insufficient tabs are defined, this is the tab width used
5907 #define WIDTH_FOR_DEFAULT_TABS 50
5910 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
5912 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
5913 wxASSERT (para
!= NULL
);
5915 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5917 // Let's make the assumption for now that for content in a paragraph, including
5918 // text, we never have a discontinuous selection. So we only deal with a
5920 wxRichTextRange selectionRange
;
5921 if (selection
.IsValid())
5923 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
5924 if (selectionRanges
.GetCount() > 0)
5925 selectionRange
= selectionRanges
[0];
5927 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5930 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5932 int offset
= GetRange().GetStart();
5934 // Replace line break characters with spaces
5935 wxString str
= m_text
;
5936 wxString toRemove
= wxRichTextLineBreakChar
;
5937 str
.Replace(toRemove
, wxT(" "));
5938 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
5941 long len
= range
.GetLength();
5942 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
5944 // Test for the optimized situations where all is selected, or none
5947 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
5948 wxCheckSetFont(dc
, textFont
);
5949 int charHeight
= dc
.GetCharHeight();
5952 if ( textFont
.IsOk() )
5954 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
5956 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5957 textFont
.SetPointSize( static_cast<int>(size
) );
5960 wxCheckSetFont(dc
, textFont
);
5962 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
5964 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5965 textFont
.SetPointSize( static_cast<int>(size
) );
5967 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
5968 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
5969 wxCheckSetFont(dc
, textFont
);
5974 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5980 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5983 // TODO: new selection code
5985 // (a) All selected.
5986 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
5988 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
5990 // (b) None selected.
5991 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
5993 // Draw all unselected
5994 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
5998 // (c) Part selected, part not
5999 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6001 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6003 // 1. Initial unselected chunk, if any, up until start of selection.
6004 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6006 int r1
= range
.GetStart();
6007 int s1
= selectionRange
.GetStart()-1;
6008 int fragmentLen
= s1
- r1
+ 1;
6009 if (fragmentLen
< 0)
6011 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6013 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6015 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6018 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6020 // Compensate for kerning difference
6021 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6022 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6024 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6025 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6026 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6027 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6029 int kerningDiff
= (w1
+ w3
) - w2
;
6030 x
= x
- kerningDiff
;
6035 // 2. Selected chunk, if any.
6036 if (selectionRange
.GetEnd() >= range
.GetStart())
6038 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6039 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6041 int fragmentLen
= s2
- s1
+ 1;
6042 if (fragmentLen
< 0)
6044 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6046 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6048 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6051 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6053 // Compensate for kerning difference
6054 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6055 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6057 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6058 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6059 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6060 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6062 int kerningDiff
= (w1
+ w3
) - w2
;
6063 x
= x
- kerningDiff
;
6068 // 3. Remaining unselected chunk, if any
6069 if (selectionRange
.GetEnd() < range
.GetEnd())
6071 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6072 int r2
= range
.GetEnd();
6074 int fragmentLen
= r2
- s2
+ 1;
6075 if (fragmentLen
< 0)
6077 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6079 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6081 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6088 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6090 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6092 wxArrayInt tabArray
;
6096 if (attr
.GetTabs().IsEmpty())
6097 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6099 tabArray
= attr
.GetTabs();
6100 tabCount
= tabArray
.GetCount();
6102 for (int i
= 0; i
< tabCount
; ++i
)
6104 int pos
= tabArray
[i
];
6105 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6112 int nextTabPos
= -1;
6118 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6119 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6121 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6122 wxCheckSetPen(dc
, wxPen(highlightColour
));
6123 dc
.SetTextForeground(highlightTextColour
);
6124 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6128 dc
.SetTextForeground(attr
.GetTextColour());
6130 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6132 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6133 dc
.SetTextBackground(attr
.GetBackgroundColour());
6136 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6139 wxCoord x_orig
= GetParent()->GetPosition().x
;
6142 // the string has a tab
6143 // break up the string at the Tab
6144 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6145 str
= str
.AfterFirst(wxT('\t'));
6146 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6148 bool not_found
= true;
6149 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6151 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6153 // Find the next tab position.
6154 // Even if we're at the end of the tab array, we must still draw the chunk.
6156 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6158 if (nextTabPos
<= tabPos
)
6160 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6161 nextTabPos
= tabPos
+ defaultTabWidth
;
6168 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6169 dc
.DrawRectangle(selRect
);
6171 dc
.DrawText(stringChunk
, x
, y
);
6173 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6175 wxPen oldPen
= dc
.GetPen();
6176 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6177 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6178 wxCheckSetPen(dc
, oldPen
);
6184 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6189 dc
.GetTextExtent(str
, & w
, & h
);
6192 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6193 dc
.DrawRectangle(selRect
);
6195 dc
.DrawText(str
, x
, y
);
6197 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6199 wxPen oldPen
= dc
.GetPen();
6200 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6201 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6202 wxCheckSetPen(dc
, oldPen
);
6211 /// Lay the item out
6212 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
))
6214 // Only lay out if we haven't already cached the size
6216 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6218 // Eventually we want to have a reasonable estimate of minimum size.
6219 m_minSize
= wxSize(0, 0);
6224 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6226 wxRichTextObject::Copy(obj
);
6228 m_text
= obj
.m_text
;
6231 /// Get/set the object size for the given range. Returns false if the range
6232 /// is invalid for this object.
6233 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6235 if (!range
.IsWithin(GetRange()))
6238 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6239 wxASSERT (para
!= NULL
);
6241 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6243 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6245 // Always assume unformatted text, since at this level we have no knowledge
6246 // of line breaks - and we don't need it, since we'll calculate size within
6247 // formatted text by doing it in chunks according to the line ranges
6249 bool bScript(false);
6250 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6253 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6254 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6256 wxFont textFont
= font
;
6257 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6258 textFont
.SetPointSize( static_cast<int>(size
) );
6259 wxCheckSetFont(dc
, textFont
);
6264 wxCheckSetFont(dc
, font
);
6268 bool haveDescent
= false;
6269 int startPos
= range
.GetStart() - GetRange().GetStart();
6270 long len
= range
.GetLength();
6272 wxString
str(m_text
);
6273 wxString toReplace
= wxRichTextLineBreakChar
;
6274 str
.Replace(toReplace
, wxT(" "));
6276 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6278 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6279 stringChunk
.MakeUpper();
6283 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6285 // the string has a tab
6286 wxArrayInt tabArray
;
6287 if (textAttr
.GetTabs().IsEmpty())
6288 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6290 tabArray
= textAttr
.GetTabs();
6292 int tabCount
= tabArray
.GetCount();
6294 for (int i
= 0; i
< tabCount
; ++i
)
6296 int pos
= tabArray
[i
];
6297 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6301 int nextTabPos
= -1;
6303 while (stringChunk
.Find(wxT('\t')) >= 0)
6305 int absoluteWidth
= 0;
6307 // the string has a tab
6308 // break up the string at the Tab
6309 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6310 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6315 if (partialExtents
->GetCount() > 0)
6316 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6320 // Add these partial extents
6322 dc
.GetPartialTextExtents(stringFragment
, p
);
6324 for (j
= 0; j
< p
.GetCount(); j
++)
6325 partialExtents
->Add(oldWidth
+ p
[j
]);
6327 if (partialExtents
->GetCount() > 0)
6328 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6330 absoluteWidth
= relativeX
;
6334 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6336 absoluteWidth
= width
+ relativeX
;
6340 bool notFound
= true;
6341 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6343 nextTabPos
= tabArray
.Item(i
);
6345 // Find the next tab position.
6346 // Even if we're at the end of the tab array, we must still process the chunk.
6348 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6350 if (nextTabPos
<= absoluteWidth
)
6352 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6353 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6357 width
= nextTabPos
- relativeX
;
6360 partialExtents
->Add(width
);
6366 if (!stringChunk
.IsEmpty())
6371 if (partialExtents
->GetCount() > 0)
6372 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6376 // Add these partial extents
6378 dc
.GetPartialTextExtents(stringChunk
, p
);
6380 for (j
= 0; j
< p
.GetCount(); j
++)
6381 partialExtents
->Add(oldWidth
+ p
[j
]);
6385 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6393 int charHeight
= dc
.GetCharHeight();
6394 if ((*partialExtents
).GetCount() > 0)
6395 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6398 size
= wxSize(w
, charHeight
);
6402 size
= wxSize(width
, dc
.GetCharHeight());
6406 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6414 /// Do a split, returning an object containing the second part, and setting
6415 /// the first part in 'this'.
6416 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6418 long index
= pos
- GetRange().GetStart();
6420 if (index
< 0 || index
>= (int) m_text
.length())
6423 wxString firstPart
= m_text
.Mid(0, index
);
6424 wxString secondPart
= m_text
.Mid(index
);
6428 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6429 newObject
->SetAttributes(GetAttributes());
6431 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6432 GetRange().SetEnd(pos
-1);
6438 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6440 end
= start
+ m_text
.length() - 1;
6441 m_range
.SetRange(start
, end
);
6445 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6447 wxRichTextRange r
= range
;
6449 r
.LimitTo(GetRange());
6451 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6457 long startIndex
= r
.GetStart() - GetRange().GetStart();
6458 long len
= r
.GetLength();
6460 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6464 /// Get text for the given range.
6465 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6467 wxRichTextRange r
= range
;
6469 r
.LimitTo(GetRange());
6471 long startIndex
= r
.GetStart() - GetRange().GetStart();
6472 long len
= r
.GetLength();
6474 return m_text
.Mid(startIndex
, len
);
6477 /// Returns true if this object can merge itself with the given one.
6478 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6480 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6481 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6484 /// Returns true if this object merged itself with the given one.
6485 /// The calling code will then delete the given object.
6486 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6488 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6489 wxASSERT( textObject
!= NULL
);
6493 m_text
+= textObject
->GetText();
6494 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6501 /// Dump to output stream for debugging
6502 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6504 wxRichTextObject::Dump(stream
);
6505 stream
<< m_text
<< wxT("\n");
6508 /// Get the first position from pos that has a line break character.
6509 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6512 int len
= m_text
.length();
6513 int startPos
= pos
- m_range
.GetStart();
6514 for (i
= startPos
; i
< len
; i
++)
6516 wxChar ch
= m_text
[i
];
6517 if (ch
== wxRichTextLineBreakChar
)
6519 return i
+ m_range
.GetStart();
6527 * This is a kind of box, used to represent the whole buffer
6530 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6532 wxList
wxRichTextBuffer::sm_handlers
;
6533 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6534 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6535 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6538 void wxRichTextBuffer::Init()
6540 m_commandProcessor
= new wxCommandProcessor
;
6541 m_styleSheet
= NULL
;
6543 m_batchedCommandDepth
= 0;
6544 m_batchedCommand
= NULL
;
6551 wxRichTextBuffer::~wxRichTextBuffer()
6553 delete m_commandProcessor
;
6554 delete m_batchedCommand
;
6557 ClearEventHandlers();
6560 void wxRichTextBuffer::ResetAndClearCommands()
6564 GetCommandProcessor()->ClearCommands();
6567 Invalidate(wxRICHTEXT_ALL
);
6570 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6572 wxRichTextParagraphLayoutBox::Copy(obj
);
6574 m_styleSheet
= obj
.m_styleSheet
;
6575 m_modified
= obj
.m_modified
;
6576 m_batchedCommandDepth
= 0;
6577 if (m_batchedCommand
)
6578 delete m_batchedCommand
;
6579 m_batchedCommand
= NULL
;
6580 m_suppressUndo
= obj
.m_suppressUndo
;
6581 m_invalidRange
= obj
.m_invalidRange
;
6584 /// Push style sheet to top of stack
6585 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6588 styleSheet
->InsertSheet(m_styleSheet
);
6590 SetStyleSheet(styleSheet
);
6595 /// Pop style sheet from top of stack
6596 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6600 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6601 m_styleSheet
= oldSheet
->GetNextSheet();
6610 /// Submit command to insert paragraphs
6611 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6613 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(pos
, paragraphs
, ctrl
, this, flags
);
6616 /// Submit command to insert paragraphs
6617 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int WXUNUSED(flags
))
6619 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6621 action
->GetNewParagraphs() = paragraphs
;
6623 action
->SetPosition(pos
);
6625 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6626 if (!paragraphs
.GetPartialParagraph())
6627 range
.SetEnd(range
.GetEnd()+1);
6629 // Set the range we'll need to delete in Undo
6630 action
->SetRange(range
);
6632 buffer
->SubmitAction(action
);
6637 /// Submit command to insert the given text
6638 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6640 return ctrl
->GetFocusObject()->InsertTextWithUndo(pos
, text
, ctrl
, this, flags
);
6643 /// Submit command to insert the given text
6644 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6646 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6648 wxRichTextAttr
* p
= NULL
;
6649 wxRichTextAttr paraAttr
;
6650 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6652 // Get appropriate paragraph style
6653 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6654 if (!paraAttr
.IsDefault())
6658 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6660 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6662 if (!text
.empty() && text
.Last() != wxT('\n'))
6664 // Don't count the newline when undoing
6666 action
->GetNewParagraphs().SetPartialParagraph(true);
6668 else if (!text
.empty() && text
.Last() == wxT('\n'))
6671 action
->SetPosition(pos
);
6673 // Set the range we'll need to delete in Undo
6674 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6676 buffer
->SubmitAction(action
);
6681 /// Submit command to insert the given text
6682 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6684 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(pos
, ctrl
, this, flags
);
6687 /// Submit command to insert the given text
6688 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6690 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6692 wxRichTextAttr
* p
= NULL
;
6693 wxRichTextAttr paraAttr
;
6694 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6696 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6697 if (!paraAttr
.IsDefault())
6701 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6703 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6704 action
->GetNewParagraphs().AppendChild(newPara
);
6705 action
->GetNewParagraphs().UpdateRanges();
6706 action
->GetNewParagraphs().SetPartialParagraph(false);
6707 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6711 newPara
->SetAttributes(*p
);
6713 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6715 if (para
&& para
->GetRange().GetEnd() == pos
)
6718 // Now see if we need to number the paragraph.
6719 if (newPara
->GetAttributes().HasBulletNumber())
6721 wxRichTextAttr numberingAttr
;
6722 if (FindNextParagraphNumber(para
, numberingAttr
))
6723 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6727 action
->SetPosition(pos
);
6729 // Use the default character style
6730 // Use the default character style
6731 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6733 // Check whether the default style merely reflects the paragraph/basic style,
6734 // in which case don't apply it.
6735 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6736 wxRichTextAttr toApply
;
6739 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6740 wxRichTextAttr newAttr
;
6741 // This filters out attributes that are accounted for by the current
6742 // paragraph/basic style
6743 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6746 toApply
= defaultStyle
;
6748 if (!toApply
.IsDefault())
6749 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6752 // Set the range we'll need to delete in Undo
6753 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6755 buffer
->SubmitAction(action
);
6760 /// Submit command to insert the given image
6761 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6762 const wxRichTextAttr
& textAttr
)
6764 return ctrl
->GetFocusObject()->InsertImageWithUndo(pos
, imageBlock
, ctrl
, this, flags
, textAttr
);
6767 /// Submit command to insert the given image
6768 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
,
6769 wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
,
6770 const wxRichTextAttr
& textAttr
)
6772 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6774 wxRichTextAttr
* p
= NULL
;
6775 wxRichTextAttr paraAttr
;
6776 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6778 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6779 if (!paraAttr
.IsDefault())
6783 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6785 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6787 newPara
->SetAttributes(*p
);
6789 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6790 newPara
->AppendChild(imageObject
);
6791 imageObject
->SetAttributes(textAttr
);
6792 action
->GetNewParagraphs().AppendChild(newPara
);
6793 action
->GetNewParagraphs().UpdateRanges();
6795 action
->GetNewParagraphs().SetPartialParagraph(true);
6797 action
->SetPosition(pos
);
6799 // Set the range we'll need to delete in Undo
6800 action
->SetRange(wxRichTextRange(pos
, pos
));
6802 buffer
->SubmitAction(action
);
6807 // Insert an object with no change of it
6808 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6810 return ctrl
->GetFocusObject()->InsertObjectWithUndo(pos
, object
, ctrl
, this, flags
);
6813 // Insert an object with no change of it
6814 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6816 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6818 wxRichTextAttr
* p
= NULL
;
6819 wxRichTextAttr paraAttr
;
6820 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6822 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6823 if (!paraAttr
.IsDefault())
6827 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6829 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6831 newPara
->SetAttributes(*p
);
6833 newPara
->AppendChild(object
);
6834 action
->GetNewParagraphs().AppendChild(newPara
);
6835 action
->GetNewParagraphs().UpdateRanges();
6837 action
->GetNewParagraphs().SetPartialParagraph(true);
6839 action
->SetPosition(pos
);
6841 // Set the range we'll need to delete in Undo
6842 action
->SetRange(wxRichTextRange(pos
, pos
));
6844 buffer
->SubmitAction(action
);
6846 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
6850 /// Get the style that is appropriate for a new paragraph at this position.
6851 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6853 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
6855 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
6858 wxRichTextAttr attr
;
6859 bool foundAttributes
= false;
6861 // Look for a matching paragraph style
6862 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
6864 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
6867 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6868 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
6870 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
6873 foundAttributes
= true;
6874 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6878 // If we didn't find the 'next style', use this style instead.
6879 if (!foundAttributes
)
6881 foundAttributes
= true;
6882 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6887 // Also apply list style if present
6888 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
6890 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
6893 int thisIndent
= para
->GetAttributes().GetLeftIndent();
6894 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
6896 // Apply the overall list style, and item style for this level
6897 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
6898 wxRichTextApplyStyle(attr
, listStyle
);
6899 attr
.SetOutlineLevel(thisLevel
);
6900 if (para
->GetAttributes().HasBulletNumber())
6901 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
6905 if (!foundAttributes
)
6907 attr
= para
->GetAttributes();
6908 int flags
= attr
.GetFlags();
6910 // Eliminate character styles
6911 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
6912 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
6913 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
6914 attr
.SetFlags(flags
);
6920 return wxRichTextAttr();
6923 /// Submit command to delete this range
6924 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
6926 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
6929 /// Submit command to delete this range
6930 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
6932 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
6934 action
->SetPosition(ctrl
->GetCaretPosition());
6936 // Set the range to delete
6937 action
->SetRange(range
);
6939 // Copy the fragment that we'll need to restore in Undo
6940 CopyFragment(range
, action
->GetOldParagraphs());
6942 // See if we're deleting a paragraph marker, in which case we need to
6943 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6944 if (range
.GetStart() == range
.GetEnd())
6946 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
6947 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
6949 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
6950 if (nextPara
&& nextPara
!= para
)
6952 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
6953 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
6958 buffer
->SubmitAction(action
);
6963 /// Collapse undo/redo commands
6964 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
6966 if (m_batchedCommandDepth
== 0)
6968 wxASSERT(m_batchedCommand
== NULL
);
6969 if (m_batchedCommand
)
6971 GetCommandProcessor()->Store(m_batchedCommand
);
6973 m_batchedCommand
= new wxRichTextCommand(cmdName
);
6976 m_batchedCommandDepth
++;
6981 /// Collapse undo/redo commands
6982 bool wxRichTextBuffer::EndBatchUndo()
6984 m_batchedCommandDepth
--;
6986 wxASSERT(m_batchedCommandDepth
>= 0);
6987 wxASSERT(m_batchedCommand
!= NULL
);
6989 if (m_batchedCommandDepth
== 0)
6991 GetCommandProcessor()->Store(m_batchedCommand
);
6992 m_batchedCommand
= NULL
;
6998 /// Submit immediately, or delay according to whether collapsing is on
6999 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7001 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7003 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7004 cmd
->AddAction(action
);
7006 cmd
->GetActions().Clear();
7009 m_batchedCommand
->AddAction(action
);
7013 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7014 cmd
->AddAction(action
);
7016 // Only store it if we're not suppressing undo.
7017 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7023 /// Begin suppressing undo/redo commands.
7024 bool wxRichTextBuffer::BeginSuppressUndo()
7031 /// End suppressing undo/redo commands.
7032 bool wxRichTextBuffer::EndSuppressUndo()
7039 /// Begin using a style
7040 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7042 wxRichTextAttr
newStyle(GetDefaultStyle());
7044 // Save the old default style
7045 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7047 wxRichTextApplyStyle(newStyle
, style
);
7048 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7050 SetDefaultStyle(newStyle
);
7056 bool wxRichTextBuffer::EndStyle()
7058 if (!m_attributeStack
.GetFirst())
7060 wxLogDebug(_("Too many EndStyle calls!"));
7064 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7065 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7066 m_attributeStack
.Erase(node
);
7068 SetDefaultStyle(*attr
);
7075 bool wxRichTextBuffer::EndAllStyles()
7077 while (m_attributeStack
.GetCount() != 0)
7082 /// Clear the style stack
7083 void wxRichTextBuffer::ClearStyleStack()
7085 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7086 delete (wxRichTextAttr
*) node
->GetData();
7087 m_attributeStack
.Clear();
7090 /// Begin using bold
7091 bool wxRichTextBuffer::BeginBold()
7093 wxRichTextAttr attr
;
7094 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7096 return BeginStyle(attr
);
7099 /// Begin using italic
7100 bool wxRichTextBuffer::BeginItalic()
7102 wxRichTextAttr attr
;
7103 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7105 return BeginStyle(attr
);
7108 /// Begin using underline
7109 bool wxRichTextBuffer::BeginUnderline()
7111 wxRichTextAttr attr
;
7112 attr
.SetFontUnderlined(true);
7114 return BeginStyle(attr
);
7117 /// Begin using point size
7118 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7120 wxRichTextAttr attr
;
7121 attr
.SetFontSize(pointSize
);
7123 return BeginStyle(attr
);
7126 /// Begin using this font
7127 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7129 wxRichTextAttr attr
;
7132 return BeginStyle(attr
);
7135 /// Begin using this colour
7136 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7138 wxRichTextAttr attr
;
7139 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7140 attr
.SetTextColour(colour
);
7142 return BeginStyle(attr
);
7145 /// Begin using alignment
7146 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7148 wxRichTextAttr attr
;
7149 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7150 attr
.SetAlignment(alignment
);
7152 return BeginStyle(attr
);
7155 /// Begin left indent
7156 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7158 wxRichTextAttr attr
;
7159 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7160 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7162 return BeginStyle(attr
);
7165 /// Begin right indent
7166 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7168 wxRichTextAttr attr
;
7169 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7170 attr
.SetRightIndent(rightIndent
);
7172 return BeginStyle(attr
);
7175 /// Begin paragraph spacing
7176 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7180 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7182 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7184 wxRichTextAttr attr
;
7185 attr
.SetFlags(flags
);
7186 attr
.SetParagraphSpacingBefore(before
);
7187 attr
.SetParagraphSpacingAfter(after
);
7189 return BeginStyle(attr
);
7192 /// Begin line spacing
7193 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7195 wxRichTextAttr attr
;
7196 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7197 attr
.SetLineSpacing(lineSpacing
);
7199 return BeginStyle(attr
);
7202 /// Begin numbered bullet
7203 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7205 wxRichTextAttr attr
;
7206 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7207 attr
.SetBulletStyle(bulletStyle
);
7208 attr
.SetBulletNumber(bulletNumber
);
7209 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7211 return BeginStyle(attr
);
7214 /// Begin symbol bullet
7215 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7217 wxRichTextAttr attr
;
7218 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7219 attr
.SetBulletStyle(bulletStyle
);
7220 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7221 attr
.SetBulletText(symbol
);
7223 return BeginStyle(attr
);
7226 /// Begin standard bullet
7227 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7229 wxRichTextAttr attr
;
7230 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7231 attr
.SetBulletStyle(bulletStyle
);
7232 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7233 attr
.SetBulletName(bulletName
);
7235 return BeginStyle(attr
);
7238 /// Begin named character style
7239 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7241 if (GetStyleSheet())
7243 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7246 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7247 return BeginStyle(attr
);
7253 /// Begin named paragraph style
7254 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7256 if (GetStyleSheet())
7258 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7261 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7262 return BeginStyle(attr
);
7268 /// Begin named list style
7269 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7271 if (GetStyleSheet())
7273 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7276 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7278 attr
.SetBulletNumber(number
);
7280 return BeginStyle(attr
);
7287 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7289 wxRichTextAttr attr
;
7291 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7293 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7296 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7301 return BeginStyle(attr
);
7304 /// Adds a handler to the end
7305 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7307 sm_handlers
.Append(handler
);
7310 /// Inserts a handler at the front
7311 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7313 sm_handlers
.Insert( handler
);
7316 /// Removes a handler
7317 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7319 wxRichTextFileHandler
*handler
= FindHandler(name
);
7322 sm_handlers
.DeleteObject(handler
);
7330 /// Finds a handler by filename or, if supplied, type
7331 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7332 wxRichTextFileType imageType
)
7334 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7335 return FindHandler(imageType
);
7336 else if (!filename
.IsEmpty())
7338 wxString path
, file
, ext
;
7339 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7340 return FindHandler(ext
, imageType
);
7347 /// Finds a handler by name
7348 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7350 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7353 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7354 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7356 node
= node
->GetNext();
7361 /// Finds a handler by extension and type
7362 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7364 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7367 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7368 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7369 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7371 node
= node
->GetNext();
7376 /// Finds a handler by type
7377 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7379 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7382 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7383 if (handler
->GetType() == type
) return handler
;
7384 node
= node
->GetNext();
7389 void wxRichTextBuffer::InitStandardHandlers()
7391 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7392 AddHandler(new wxRichTextPlainTextHandler
);
7395 void wxRichTextBuffer::CleanUpHandlers()
7397 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7400 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7401 wxList::compatibility_iterator next
= node
->GetNext();
7406 sm_handlers
.Clear();
7409 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7416 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7420 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7421 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7426 wildcard
+= wxT(";");
7427 wildcard
+= wxT("*.") + handler
->GetExtension();
7432 wildcard
+= wxT("|");
7433 wildcard
+= handler
->GetName();
7434 wildcard
+= wxT(" ");
7435 wildcard
+= _("files");
7436 wildcard
+= wxT(" (*.");
7437 wildcard
+= handler
->GetExtension();
7438 wildcard
+= wxT(")|*.");
7439 wildcard
+= handler
->GetExtension();
7441 types
->Add(handler
->GetType());
7446 node
= node
->GetNext();
7450 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7455 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7457 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7460 SetDefaultStyle(wxRichTextAttr());
7461 handler
->SetFlags(GetHandlerFlags());
7462 bool success
= handler
->LoadFile(this, filename
);
7463 Invalidate(wxRICHTEXT_ALL
);
7471 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7473 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7476 handler
->SetFlags(GetHandlerFlags());
7477 return handler
->SaveFile(this, filename
);
7483 /// Load from a stream
7484 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7486 wxRichTextFileHandler
* handler
= FindHandler(type
);
7489 SetDefaultStyle(wxRichTextAttr());
7490 handler
->SetFlags(GetHandlerFlags());
7491 bool success
= handler
->LoadFile(this, stream
);
7492 Invalidate(wxRICHTEXT_ALL
);
7499 /// Save to a stream
7500 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7502 wxRichTextFileHandler
* handler
= FindHandler(type
);
7505 handler
->SetFlags(GetHandlerFlags());
7506 return handler
->SaveFile(this, stream
);
7512 /// Copy the range to the clipboard
7513 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7515 bool success
= false;
7516 wxRichTextParagraphLayoutBox
* container
= this;
7517 if (GetRichTextCtrl())
7518 container
= GetRichTextCtrl()->GetFocusObject();
7520 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7522 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7524 wxTheClipboard
->Clear();
7526 // Add composite object
7528 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7531 wxString text
= container
->GetTextForRange(range
);
7534 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7537 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7540 // Add rich text buffer data object. This needs the XML handler to be present.
7542 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7544 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7545 container
->CopyFragment(range
, *richTextBuf
);
7547 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7550 if (wxTheClipboard
->SetData(compositeObject
))
7553 wxTheClipboard
->Close();
7562 /// Paste the clipboard content to the buffer
7563 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7565 bool success
= false;
7566 wxRichTextParagraphLayoutBox
* container
= this;
7567 if (GetRichTextCtrl())
7568 container
= GetRichTextCtrl()->GetFocusObject();
7570 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7571 if (CanPasteFromClipboard())
7573 if (wxTheClipboard
->Open())
7575 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7577 wxRichTextBufferDataObject data
;
7578 wxTheClipboard
->GetData(data
);
7579 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7582 container
->InsertParagraphsWithUndo(position
+1, *richTextBuffer
, GetRichTextCtrl(), this, 0);
7583 if (GetRichTextCtrl())
7584 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7585 delete richTextBuffer
;
7588 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7590 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7594 wxTextDataObject data
;
7595 wxTheClipboard
->GetData(data
);
7596 wxString
text(data
.GetText());
7599 text2
.Alloc(text
.Length()+1);
7601 for (i
= 0; i
< text
.Length(); i
++)
7603 wxChar ch
= text
[i
];
7604 if (ch
!= wxT('\r'))
7608 wxString text2
= text
;
7610 container
->InsertTextWithUndo(position
+1, text2
, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7612 if (GetRichTextCtrl())
7613 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7617 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7619 wxBitmapDataObject data
;
7620 wxTheClipboard
->GetData(data
);
7621 wxBitmap
bitmap(data
.GetBitmap());
7622 wxImage
image(bitmap
.ConvertToImage());
7624 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7626 action
->GetNewParagraphs().AddImage(image
);
7628 if (action
->GetNewParagraphs().GetChildCount() == 1)
7629 action
->GetNewParagraphs().SetPartialParagraph(true);
7631 action
->SetPosition(position
+1);
7633 // Set the range we'll need to delete in Undo
7634 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7636 SubmitAction(action
);
7640 wxTheClipboard
->Close();
7644 wxUnusedVar(position
);
7649 /// Can we paste from the clipboard?
7650 bool wxRichTextBuffer::CanPasteFromClipboard() const
7652 bool canPaste
= false;
7653 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7654 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7656 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7658 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7660 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7661 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7665 wxTheClipboard
->Close();
7671 /// Dumps contents of buffer for debugging purposes
7672 void wxRichTextBuffer::Dump()
7676 wxStringOutputStream
stream(& text
);
7677 wxTextOutputStream
textStream(stream
);
7684 /// Add an event handler
7685 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7687 m_eventHandlers
.Append(handler
);
7691 /// Remove an event handler
7692 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7694 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7697 m_eventHandlers
.Erase(node
);
7707 /// Clear event handlers
7708 void wxRichTextBuffer::ClearEventHandlers()
7710 m_eventHandlers
.Clear();
7713 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7714 /// otherwise will stop at the first successful one.
7715 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7717 bool success
= false;
7718 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7720 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7721 if (handler
->ProcessEvent(event
))
7731 /// Set style sheet and notify of the change
7732 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7734 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7736 wxWindowID winid
= wxID_ANY
;
7737 if (GetRichTextCtrl())
7738 winid
= GetRichTextCtrl()->GetId();
7740 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7741 event
.SetEventObject(GetRichTextCtrl());
7742 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7743 event
.SetOldStyleSheet(oldSheet
);
7744 event
.SetNewStyleSheet(sheet
);
7747 if (SendEvent(event
) && !event
.IsAllowed())
7749 if (sheet
!= oldSheet
)
7755 if (oldSheet
&& oldSheet
!= sheet
)
7758 SetStyleSheet(sheet
);
7760 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7761 event
.SetOldStyleSheet(NULL
);
7764 return SendEvent(event
);
7767 /// Set renderer, deleting old one
7768 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7772 sm_renderer
= renderer
;
7775 /// Hit-testing: returns a flag indicating hit test details, plus
7776 /// information about position
7777 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7779 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7780 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7786 textPosition
= m_ownRange
.GetEnd()-1;
7789 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7793 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7795 if (bulletAttr
.GetTextColour().IsOk())
7797 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7798 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7802 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7803 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7807 if (bulletAttr
.HasFont())
7809 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
7812 font
= (*wxNORMAL_FONT
);
7814 wxCheckSetFont(dc
, font
);
7816 int charHeight
= dc
.GetCharHeight();
7818 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
7819 int bulletHeight
= bulletWidth
;
7823 // Calculate the top position of the character (as opposed to the whole line height)
7824 int y
= rect
.y
+ (rect
.height
- charHeight
);
7826 // Calculate where the bullet should be positioned
7827 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
7829 // The margin between a bullet and text.
7830 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7832 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7833 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
7834 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7835 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
7837 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
7839 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
7841 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
7844 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
7845 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
7846 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
7847 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
7849 dc
.DrawPolygon(4, pts
);
7851 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
7854 pts
[0].x
= x
; pts
[0].y
= y
;
7855 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
7856 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
7858 dc
.DrawPolygon(3, pts
);
7860 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
7862 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
7863 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7865 else // "standard/circle", and catch-all
7867 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7873 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
7878 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
7880 wxRichTextAttr fontAttr
;
7881 fontAttr
.SetFontSize(attr
.GetFontSize());
7882 fontAttr
.SetFontStyle(attr
.GetFontStyle());
7883 fontAttr
.SetFontWeight(attr
.GetFontWeight());
7884 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
7885 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
7886 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
7888 else if (attr
.HasFont())
7889 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
7891 font
= (*wxNORMAL_FONT
);
7893 wxCheckSetFont(dc
, font
);
7895 if (attr
.GetTextColour().IsOk())
7896 dc
.SetTextForeground(attr
.GetTextColour());
7898 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
7900 int charHeight
= dc
.GetCharHeight();
7902 dc
.GetTextExtent(text
, & tw
, & th
);
7906 // Calculate the top position of the character (as opposed to the whole line height)
7907 int y
= rect
.y
+ (rect
.height
- charHeight
);
7909 // The margin between a bullet and text.
7910 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7912 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7913 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
7914 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7915 x
= x
+ (rect
.width
)/2 - tw
/2;
7917 dc
.DrawText(text
, x
, y
);
7925 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
7927 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7928 // with the buffer. The store will allow retrieval from memory, disk or other means.
7932 /// Enumerate the standard bullet names currently supported
7933 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
7935 bulletNames
.Add(wxTRANSLATE("standard/circle"));
7936 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
7937 bulletNames
.Add(wxTRANSLATE("standard/square"));
7938 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
7939 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
7948 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
7950 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
7951 wxRichTextParagraphLayoutBox(parent
)
7956 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
7961 // TODO: if the active object in the control, draw an indication.
7962 // We need to add the concept of active object, and not just focus object,
7963 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
7964 // Ultimately we would like to be able to interactively resize an active object
7965 // using drag handles.
7966 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
7970 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
7972 wxRichTextParagraphLayoutBox::Copy(obj
);
7975 // Edit properties via a GUI
7976 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
7978 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
7979 boxDlg
.SetAttributes(GetAttributes());
7981 if (boxDlg
.ShowModal() == wxID_OK
)
7983 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
7984 // indeterminate in the object.
7985 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
7992 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
7994 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
7995 wxRichTextBox(parent
)
8000 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8002 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8006 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8008 wxRichTextBox::Copy(obj
);
8011 // Edit properties via a GUI
8012 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8014 // We need to gather common attributes for all selected cells.
8016 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8017 bool multipleCells
= false;
8018 wxRichTextAttr attr
;
8020 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8021 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8023 wxRichTextAttr clashingAttr
, absentAttr
;
8024 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8026 int selectedCellCount
= 0;
8027 for (i
= 0; i
< sel
.GetCount(); i
++)
8029 const wxRichTextRange
& range
= sel
[i
];
8030 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8033 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8035 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8037 selectedCellCount
++;
8040 multipleCells
= selectedCellCount
> 1;
8044 attr
= GetAttributes();
8049 caption
= _("Multiple Cell Properties");
8051 caption
= _("Cell Properties");
8053 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8054 cellDlg
.SetAttributes(attr
);
8056 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8059 // We don't want position and floating controls for a cell.
8060 sizePage
->ShowPositionControls(false);
8061 sizePage
->ShowFloatingControls(false);
8064 if (cellDlg
.ShowModal() == wxID_OK
)
8068 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8069 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8070 // since it may represent clashing attributes across multiple objects.
8071 table
->SetCellStyle(sel
, attr
);
8074 // For a single object, indeterminate attributes set by the user should be reflected in the
8075 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8076 // the style directly instead of applying (which ignores indeterminate attributes,
8077 // leaving them as they were).
8078 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8085 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8087 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8089 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8095 // Draws the object.
8096 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8098 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8101 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8102 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8104 // Lays the object out. rect is the space available for layout. Often it will
8105 // be the specified overall space for this object, if trying to constrain
8106 // layout to a particular size, or it could be the total space available in the
8107 // parent. rect is the overall size, so we must subtract margins and padding.
8108 // to get the actual available space.
8109 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
8111 SetPosition(rect
.GetPosition());
8113 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8114 // minimum size if within alloted size, then divide up remaining size
8115 // between rows/cols.
8118 wxRichTextBuffer
* buffer
= GetBuffer();
8119 if (buffer
) scale
= buffer
->GetScale();
8121 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8122 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8124 // If we have no fixed table size, and assuming we're not pushed for
8125 // space, then we don't have to try to stretch the table to fit the contents.
8126 bool stretchToFitTableWidth
= false;
8128 int tableWidth
= rect
.width
;
8129 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8131 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8133 // Fixed table width, so we do want to stretch columns out if necessary.
8134 stretchToFitTableWidth
= true;
8136 // Shouldn't be able to exceed the size passed to this function
8137 tableWidth
= wxMin(rect
.width
, tableWidth
);
8140 // Get internal padding
8141 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
8142 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8143 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8144 if (GetAttributes().GetTextBoxAttr().GetPadding().GetRight().IsValid())
8145 paddingRight
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetRight());
8146 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8147 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8148 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8149 paddingBottom
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetBottom());
8151 // Assume that left and top padding are also used for inter-cell padding.
8152 int paddingX
= paddingLeft
;
8153 int paddingY
= paddingTop
;
8155 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8156 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8158 // Internal table width - the area for content
8159 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8161 int rowCount
= m_cells
.GetCount();
8162 if (m_colCount
== 0 || rowCount
== 0)
8164 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8165 SetCachedSize(overallRect
.GetSize());
8167 // Zero content size
8168 SetMinSize(overallRect
.GetSize());
8169 SetMaxSize(GetMinSize());
8173 // The final calculated widths
8174 wxArrayInt
colWidths(m_colCount
);
8176 wxArrayInt
absoluteColWidths(m_colCount
);
8177 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8178 wxArrayInt
percentageColWidths(m_colCount
);
8179 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8180 // These are only relevant when the first column contains spanning information.
8181 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8182 wxArrayInt
maxColWidths(m_colCount
);
8183 wxArrayInt
minColWidths(m_colCount
);
8185 wxSize
tableSize(tableWidth
, 0);
8189 for (i
= 0; i
< m_colCount
; i
++)
8191 absoluteColWidths
[i
] = 0;
8192 // absoluteColWidthsSpanning[i] = 0;
8193 percentageColWidths
[i
] = -1;
8194 // percentageColWidthsSpanning[i] = -1;
8196 maxColWidths
[i
] = 0;
8197 minColWidths
[i
] = 0;
8198 // columnSpans[i] = 1;
8201 // (0) Determine which cells are visible according to spans
8203 // __________________
8208 // |------------------|
8209 // |__________________| 4
8211 // To calculate cell visibility:
8212 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8213 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8214 // that cell, hide the cell.
8216 // We can also use this array to match the size of spanning cells to the grid. Or just do
8217 // this when we iterate through all cells.
8219 // 0.1: add spanning cells to an array
8220 wxRichTextRectArray rectArray
;
8221 for (j
= 0; j
< m_rowCount
; j
++)
8223 for (i
= 0; i
< m_colCount
; i
++)
8225 wxRichTextBox
* cell
= GetCell(j
, i
);
8226 int colSpan
= 1, rowSpan
= 1;
8227 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8228 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8229 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8230 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8231 if (colSpan
> 1 || rowSpan
> 1)
8233 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8237 // 0.2: find which cells are subsumed by a spanning cell
8238 for (j
= 0; j
< m_rowCount
; j
++)
8240 for (i
= 0; i
< m_colCount
; i
++)
8242 wxRichTextBox
* cell
= GetCell(j
, i
);
8243 if (rectArray
.GetCount() == 0)
8249 int colSpan
= 1, rowSpan
= 1;
8250 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8251 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8252 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8253 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8254 if (colSpan
> 1 || rowSpan
> 1)
8256 // Assume all spanning cells are shown
8262 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8264 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8276 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8277 // overlap with a spanned cell starting at a previous column position.
8278 // This means we need to keep an array of rects so we can check. However
8279 // it does also mean that some spans simply may not be taken into account
8280 // where there are different spans happening on different rows. In these cases,
8281 // they will simply be as wide as their constituent columns.
8283 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8284 // the absolute or percentage width of each column.
8286 for (j
= 0; j
< m_rowCount
; j
++)
8288 // First get the overall margins so we can calculate percentage widths based on
8289 // the available content space for all cells on the row
8291 int overallRowContentMargin
= 0;
8292 int visibleCellCount
= 0;
8294 for (i
= 0; i
< m_colCount
; i
++)
8296 wxRichTextBox
* cell
= GetCell(j
, i
);
8297 if (cell
->IsShown())
8299 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8300 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8302 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8303 visibleCellCount
++;
8307 // Add in inter-cell padding
8308 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8310 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8311 wxSize
rowTableSize(rowContentWidth
, 0);
8312 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8314 for (i
= 0; i
< m_colCount
; i
++)
8316 wxRichTextBox
* cell
= GetCell(j
, i
);
8317 if (cell
->IsShown())
8320 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8321 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8323 // Lay out cell to find min/max widths
8324 cell
->Invalidate(wxRICHTEXT_ALL
);
8325 cell
->Layout(dc
, availableSpace
, style
);
8329 int absoluteCellWidth
= -1;
8330 int percentageCellWidth
= -1;
8332 // I think we need to calculate percentages from the internal table size,
8333 // minus the padding between cells which we'll need to calculate from the
8334 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8335 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8336 // so if we want to conform to that we'll need to add in the overall cell margins.
8337 // However, this will make it difficult to specify percentages that add up to
8338 // 100% and still fit within the table width.
8339 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8340 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8341 // If we're using internal content size for the width, we would calculate the
8342 // the overall cell width for n cells as:
8343 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8344 // + thisOverallCellMargin
8345 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8346 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8348 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8350 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8351 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8353 percentageCellWidth
= w
;
8357 absoluteCellWidth
= w
;
8359 // Override absolute width with minimum width if necessary
8360 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8361 absoluteCellWidth
= cell
->GetMinSize().x
;
8364 if (absoluteCellWidth
!= -1)
8366 if (absoluteCellWidth
> absoluteColWidths
[i
])
8367 absoluteColWidths
[i
] = absoluteCellWidth
;
8370 if (percentageCellWidth
!= -1)
8372 if (percentageCellWidth
> percentageColWidths
[i
])
8373 percentageColWidths
[i
] = percentageCellWidth
;
8376 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8377 minColWidths
[i
] = cell
->GetMinSize().x
;
8378 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8379 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8385 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8386 // TODO: simply merge this into (1).
8387 for (i
= 0; i
< m_colCount
; i
++)
8389 if (absoluteColWidths
[i
] > 0)
8391 colWidths
[i
] = absoluteColWidths
[i
];
8393 else if (percentageColWidths
[i
] > 0)
8395 colWidths
[i
] = percentageColWidths
[i
];
8397 // This is rubbish - we calculated the absolute widths from percentages, so
8398 // we can't do it again here.
8399 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8403 // (3) Process absolute or proportional widths of spanning columns,
8404 // now that we know what our fixed column widths are going to be.
8405 // Spanned cells will try to adjust columns so the span will fit.
8406 // Even existing fixed column widths can be expanded if necessary.
8407 // Actually, currently fixed columns widths aren't adjusted; instead,
8408 // the algorithm favours earlier rows and adjusts unspecified column widths
8409 // the first time only. After that, we can't know whether the column has been
8410 // specified explicitly or not. (We could make a note if necessary.)
8411 for (j
= 0; j
< m_rowCount
; j
++)
8413 // First get the overall margins so we can calculate percentage widths based on
8414 // the available content space for all cells on the row
8416 int overallRowContentMargin
= 0;
8417 int visibleCellCount
= 0;
8419 for (i
= 0; i
< m_colCount
; i
++)
8421 wxRichTextBox
* cell
= GetCell(j
, i
);
8422 if (cell
->IsShown())
8424 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8425 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8427 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8428 visibleCellCount
++;
8432 // Add in inter-cell padding
8433 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8435 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8436 wxSize
rowTableSize(rowContentWidth
, 0);
8437 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8439 for (i
= 0; i
< m_colCount
; i
++)
8441 wxRichTextBox
* cell
= GetCell(j
, i
);
8442 if (cell
->IsShown())
8445 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8446 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8450 int spans
= wxMin(colSpan
, m_colCount
- i
);
8454 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8456 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8457 // Override absolute width with minimum width if necessary
8458 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8459 cellWidth
= cell
->GetMinSize().x
;
8463 // Do we want to do this? It's the only chance we get to
8464 // use the cell's min/max sizes, so we need to work out
8465 // how we're going to balance the unspecified spanning cell
8466 // width with the possibility more-constrained constituent cell widths.
8467 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8468 // don't want to constraint all the spanned columns to fit into this cell.
8469 // OK, let's say that if any of the constituent columns don't fit,
8470 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8471 // cells to the columns later.
8472 cellWidth
= cell
->GetMinSize().x
;
8473 if (cell
->GetMaxSize().x
> cellWidth
)
8474 cellWidth
= cell
->GetMaxSize().x
;
8477 // Subtract the padding between cells
8478 int spanningWidth
= cellWidth
;
8479 spanningWidth
-= paddingX
* (spans
-1);
8481 if (spanningWidth
> 0)
8483 // Now share the spanning width between columns within that span
8484 // TODO: take into account min widths of columns within the span
8485 int spanningWidthLeft
= spanningWidth
;
8486 int stretchColCount
= 0;
8487 for (k
= i
; k
< (i
+spans
); k
++)
8489 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8490 spanningWidthLeft
-= colWidths
[k
];
8494 // Now divide what's left between the remaining columns
8496 if (stretchColCount
> 0)
8497 colShare
= spanningWidthLeft
/ stretchColCount
;
8498 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8500 // If fixed-width columns are currently too big, then we'll later
8501 // stretch the spanned cell to fit.
8503 if (spanningWidthLeft
> 0)
8505 for (k
= i
; k
< (i
+spans
); k
++)
8507 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8509 int newWidth
= colShare
;
8510 if (k
== (i
+spans
-1))
8511 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8512 colWidths
[k
] = newWidth
;
8523 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8524 // TODO: take into account min widths of columns within the span
8525 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8526 int widthLeft
= tableWidthMinusPadding
;
8527 int stretchColCount
= 0;
8528 for (i
= 0; i
< m_colCount
; i
++)
8530 // TODO: we need to take into account min widths.
8531 // Subtract min width from width left, then
8532 // add the colShare to the min width
8533 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8534 widthLeft
-= colWidths
[i
];
8537 if (minColWidths
[i
] > 0)
8538 widthLeft
-= minColWidths
[i
];
8544 // Now divide what's left between the remaining columns
8546 if (stretchColCount
> 0)
8547 colShare
= widthLeft
/ stretchColCount
;
8548 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8550 // Check we don't have enough space, in which case shrink all columns, overriding
8551 // any absolute/proportional widths
8552 // TODO: actually we would like to divide up the shrinkage according to size.
8553 // How do we calculate the proportions that will achieve this?
8554 // Could first choose an arbitrary value for stretching cells, and then calculate
8555 // factors to multiply each width by.
8556 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8557 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8559 colShare
= tableWidthMinusPadding
/ m_colCount
;
8560 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8561 for (i
= 0; i
< m_colCount
; i
++)
8564 minColWidths
[i
] = 0;
8568 // We have to adjust the columns if either we need to shrink the
8569 // table to fit the parent/table width, or we explicitly set the
8570 // table width and need to stretch out the table.
8571 if (widthLeft
< 0 || stretchToFitTableWidth
)
8573 for (i
= 0; i
< m_colCount
; i
++)
8575 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8577 if (minColWidths
[i
] > 0)
8578 colWidths
[i
] = minColWidths
[i
] + colShare
;
8580 colWidths
[i
] = colShare
;
8581 if (i
== (m_colCount
-1))
8582 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8587 // TODO: if spanned cells have no specified or max width, make them the
8588 // as big as the columns they span. Do this for all spanned cells in all
8589 // rows, of course. Size any spanned cells left over at the end - even if they
8590 // have width > 0, make sure they're limited to the appropriate column edge.
8594 Sort out confusion between content width
8595 and overall width later. For now, assume we specify overall width.
8597 So, now we've laid out the table to fit into the given space
8598 and have used specified widths and minimum widths.
8600 Now we need to consider how we will try to take maximum width into account.
8604 // (??) TODO: take max width into account
8606 // (6) Lay out all cells again with the current values
8609 int y
= availableSpace
.y
;
8610 for (j
= 0; j
< m_rowCount
; j
++)
8612 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8613 int maxCellHeight
= 0;
8614 int maxSpecifiedCellHeight
= 0;
8616 wxArrayInt
actualWidths(m_colCount
);
8618 wxTextAttrDimensionConverter
converter(dc
, scale
);
8619 for (i
= 0; i
< m_colCount
; i
++)
8621 wxRichTextCell
* cell
= GetCell(j
, i
);
8622 if (cell
->IsShown())
8624 // Get max specified cell height
8625 // Don't handle percentages for height
8626 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8628 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8629 if (h
> maxSpecifiedCellHeight
)
8630 maxSpecifiedCellHeight
= h
;
8633 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8636 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8637 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8639 wxRect availableCellSpace
;
8641 // TODO: take into acount spans
8644 // Calculate the size of this spanning cell from its constituent columns
8646 int spans
= wxMin(colSpan
, m_colCount
- i
);
8647 for (k
= i
; k
< spans
; k
++)
8653 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8656 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8658 // Store actual width so we can force cell to be the appropriate width on the final loop
8659 actualWidths
[i
] = availableCellSpace
.GetWidth();
8662 cell
->Invalidate(wxRICHTEXT_ALL
);
8663 cell
->Layout(dc
, availableCellSpace
, style
);
8665 // TODO: use GetCachedSize().x to compute 'natural' size
8667 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8668 if (cell
->GetCachedSize().y
> maxCellHeight
)
8669 maxCellHeight
= cell
->GetCachedSize().y
;
8674 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8676 for (i
= 0; i
< m_colCount
; i
++)
8678 wxRichTextCell
* cell
= GetCell(j
, i
);
8679 if (cell
->IsShown())
8681 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8682 // Lay out cell with new height
8683 cell
->Invalidate(wxRICHTEXT_ALL
);
8684 cell
->Layout(dc
, availableCellSpace
, style
);
8686 // Make sure the cell size really is the appropriate size,
8687 // not the calculated box size
8688 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8690 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8695 if (j
< (m_rowCount
-1))
8699 // We need to add back the margins etc.
8701 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8702 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8703 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8704 SetCachedSize(marginRect
.GetSize());
8707 // TODO: calculate max size
8709 SetMaxSize(GetCachedSize());
8712 // TODO: calculate min size
8714 SetMinSize(GetCachedSize());
8717 // TODO: currently we use either a fixed table width or the parent's size.
8718 // We also want to be able to calculate the table width from its content,
8719 // whether using fixed column widths or cell content min/max width.
8720 // Probably need a boolean flag to say whether we need to stretch cells
8721 // to fit the table width, or to simply use min/max cell widths. The
8722 // trouble with this is that if cell widths are not specified, they
8723 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8724 // Anyway, ignoring that problem, we probably need to factor layout into a function
8725 // that can can calculate the maximum unconstrained layout in case table size is
8726 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8727 // constrain Layout(), or the previously-calculated max size to constraint layout.
8732 // Finds the absolute position and row height for the given character position
8733 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8735 wxRichTextCell
* child
= GetCell(index
+1);
8738 // Find the position at the start of the child cell, since the table doesn't
8739 // have any caret position of its own.
8740 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8746 // Get the cell at the given character position (in the range of the table).
8747 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8749 int row
= 0, col
= 0;
8750 if (GetCellRowColumnPosition(pos
, row
, col
))
8752 return GetCell(row
, col
);
8758 // Get the row/column for a given character position
8759 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8761 if (m_colCount
== 0 || m_rowCount
== 0)
8764 row
= (int) (pos
/ m_colCount
);
8765 col
= pos
- (row
* m_colCount
);
8767 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8769 if (row
< m_rowCount
&& col
< m_colCount
)
8775 // Calculate range, taking row/cell ordering into account instead of relying
8776 // on list ordering.
8777 void wxRichTextTable::CalculateRange(long start
, long& end
)
8779 long current
= start
;
8780 long lastEnd
= current
;
8789 for (i
= 0; i
< m_rowCount
; i
++)
8791 for (j
= 0; j
< m_colCount
; j
++)
8793 wxRichTextCell
* child
= GetCell(i
, j
);
8798 child
->CalculateRange(current
, childEnd
);
8801 current
= childEnd
+ 1;
8806 // A top-level object always has a range of size 1,
8807 // because its children don't count at this level.
8809 m_range
.SetRange(start
, start
);
8811 // An object with no children has zero length
8812 if (m_children
.GetCount() == 0)
8814 m_ownRange
.SetRange(0, lastEnd
);
8817 // Gets the range size.
8818 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8820 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
8823 // Deletes content in the given range.
8824 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
8826 // TODO: implement deletion of cells
8830 // Gets any text in this object for the given range.
8831 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
8833 return wxRichTextBox::GetTextForRange(range
);
8836 // Copies this object.
8837 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
8839 wxRichTextBox::Copy(obj
);
8843 m_rowCount
= obj
.m_rowCount
;
8844 m_colCount
= obj
.m_colCount
;
8846 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
8849 for (i
= 0; i
< m_rowCount
; i
++)
8851 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8852 for (j
= 0; j
< m_colCount
; j
++)
8854 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
8862 void wxRichTextTable::ClearTable()
8868 bool wxRichTextTable::CreateTable(int rows
, int cols
)
8875 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
8878 for (i
= 0; i
< rows
; i
++)
8880 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8881 for (j
= 0; j
< cols
; j
++)
8883 wxRichTextCell
* cell
= new wxRichTextCell
;
8885 cell
->AddParagraph(wxEmptyString
);
8894 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
8896 wxASSERT(row
< m_rowCount
);
8897 wxASSERT(col
< m_colCount
);
8899 if (row
< m_rowCount
&& col
< m_colCount
)
8901 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
8902 wxRichTextObject
* obj
= colArray
[col
];
8903 return wxDynamicCast(obj
, wxRichTextCell
);
8909 // Returns a selection object specifying the selections between start and end character positions.
8910 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8911 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
8913 wxRichTextSelection selection
;
8914 selection
.SetContainer((wxRichTextTable
*) this);
8923 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
8925 if (end
>= (m_colCount
* m_rowCount
))
8928 // We need to find the rectangle of cells that is described by the rectangle
8929 // with start, end as the diagonal. Make sure we don't add cells that are
8930 // not currenty visible because they are overlapped by spanning cells.
8932 --------------------------
8933 | 0 | 1 | 2 | 3 | 4 |
8934 --------------------------
8935 | 5 | 6 | 7 | 8 | 9 |
8936 --------------------------
8937 | 10 | 11 | 12 | 13 | 14 |
8938 --------------------------
8939 | 15 | 16 | 17 | 18 | 19 |
8940 --------------------------
8942 Let's say we select 6 -> 18.
8944 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8945 which is left and which is right.
8947 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8949 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8955 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
8956 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
8958 int topRow
= int(start
/m_colCount
);
8959 int bottomRow
= int(end
/m_colCount
);
8961 if (leftCol
> rightCol
)
8968 if (topRow
> bottomRow
)
8970 int tmp
= bottomRow
;
8976 for (i
= topRow
; i
<= bottomRow
; i
++)
8978 for (j
= leftCol
; j
<= rightCol
; j
++)
8980 wxRichTextCell
* cell
= GetCell(i
, j
);
8981 if (cell
&& cell
->IsShown())
8982 selection
.Add(cell
->GetRange());
8989 // Sets the attributes for the cells specified by the selection.
8990 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
8992 if (selection
.GetContainer() != this)
8995 wxRichTextBuffer
* buffer
= GetBuffer();
8996 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
8997 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9000 buffer
->BeginBatchUndo(_("Set Cell Style"));
9002 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9005 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9006 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9007 SetStyle(cell
, style
, flags
);
9008 node
= node
->GetNext();
9011 // Do action, or delay it until end of batch.
9013 buffer
->EndBatchUndo();
9018 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9020 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9021 if ((startRow
+ noRows
) >= m_rowCount
)
9025 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9027 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9028 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9030 wxRichTextObject
* cell
= colArray
[j
];
9031 RemoveChild(cell
, true);
9034 // Keep deleting at the same position, since we move all
9036 m_cells
.RemoveAt(startRow
);
9039 m_rowCount
= m_rowCount
- noRows
;
9044 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9046 wxASSERT((startCol
+ noCols
) < m_colCount
);
9047 if ((startCol
+ noCols
) >= m_colCount
)
9050 bool deleteRows
= (noCols
== m_colCount
);
9053 for (i
= 0; i
< m_rowCount
; i
++)
9055 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9056 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9058 wxRichTextObject
* cell
= colArray
[j
];
9059 RemoveChild(cell
, true);
9063 m_cells
.RemoveAt(0);
9068 m_colCount
= m_colCount
- noCols
;
9073 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9075 wxASSERT(startRow
<= m_rowCount
);
9076 if (startRow
> m_rowCount
)
9080 for (i
= 0; i
< noRows
; i
++)
9083 if (startRow
== m_rowCount
)
9085 m_cells
.Add(wxRichTextObjectPtrArray());
9086 idx
= m_cells
.GetCount() - 1;
9090 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9094 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9095 for (j
= 0; j
< m_colCount
; j
++)
9097 wxRichTextCell
* cell
= new wxRichTextCell
;
9098 cell
->GetAttributes() = attr
;
9105 m_rowCount
= m_rowCount
+ noRows
;
9109 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9111 wxASSERT(startCol
<= m_colCount
);
9112 if (startCol
> m_colCount
)
9116 for (i
= 0; i
< m_rowCount
; i
++)
9118 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9119 for (j
= 0; j
< noCols
; j
++)
9121 wxRichTextCell
* cell
= new wxRichTextCell
;
9122 cell
->GetAttributes() = attr
;
9126 if (startCol
== m_colCount
)
9129 colArray
.Insert(cell
, startCol
+j
);
9133 m_colCount
= m_colCount
+ noCols
;
9138 // Edit properties via a GUI
9139 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9141 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9142 boxDlg
.SetAttributes(GetAttributes());
9144 if (boxDlg
.ShowModal() == wxID_OK
)
9146 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9154 * Module to initialise and clean up handlers
9157 class wxRichTextModule
: public wxModule
9159 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9161 wxRichTextModule() {}
9164 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9165 wxRichTextBuffer::InitStandardHandlers();
9166 wxRichTextParagraph::InitDefaultTabs();
9171 wxRichTextBuffer::CleanUpHandlers();
9172 wxRichTextDecimalToRoman(-1);
9173 wxRichTextParagraph::ClearDefaultTabs();
9174 wxRichTextCtrl::ClearAvailableFontNames();
9175 wxRichTextBuffer::SetRenderer(NULL
);
9179 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9182 // If the richtext lib is dynamically loaded after the app has already started
9183 // (such as from wxPython) then the built-in module system will not init this
9184 // module. Provide this function to do it manually.
9185 void wxRichTextModuleInit()
9187 wxModule
* module = new wxRichTextModule
;
9189 wxModule::RegisterModule(module);
9194 * Commands for undo/redo
9198 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9199 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9201 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9204 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9208 wxRichTextCommand::~wxRichTextCommand()
9213 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9215 if (!m_actions
.Member(action
))
9216 m_actions
.Append(action
);
9219 bool wxRichTextCommand::Do()
9221 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9223 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9230 bool wxRichTextCommand::Undo()
9232 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9234 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9241 void wxRichTextCommand::ClearActions()
9243 WX_CLEAR_LIST(wxList
, m_actions
);
9251 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9252 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9253 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9257 m_containerAddress
.Create(buffer
, container
);
9258 m_ignoreThis
= ignoreFirstTime
;
9263 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9264 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9266 cmd
->AddAction(this);
9269 wxRichTextAction::~wxRichTextAction()
9275 // Returns the container that this action refers to, using the container address and top-level buffer.
9276 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9278 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9283 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9285 // Store a list of line start character and y positions so we can figure out which area
9286 // we need to refresh
9288 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9289 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9290 wxASSERT(container
!= NULL
);
9294 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9295 // If we had several actions, which only invalidate and leave layout until the
9296 // paint handler is called, then this might not be true. So we may need to switch
9297 // optimisation on only when we're simply adding text and not simultaneously
9298 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9299 // first, but of course this means we'll be doing it twice.
9300 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9302 wxSize clientSize
= m_ctrl
->GetClientSize();
9303 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9304 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9306 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9307 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9310 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9311 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9314 wxRichTextLine
* line
= node2
->GetData();
9315 wxPoint pt
= line
->GetAbsolutePosition();
9316 wxRichTextRange range
= line
->GetAbsoluteRange();
9320 node2
= wxRichTextLineList::compatibility_iterator();
9321 node
= wxRichTextObjectList::compatibility_iterator();
9323 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9325 optimizationLineCharPositions
.Add(range
.GetStart());
9326 optimizationLineYPositions
.Add(pt
.y
);
9330 node2
= node2
->GetNext();
9334 node
= node
->GetNext();
9340 bool wxRichTextAction::Do()
9342 m_buffer
->Modify(true);
9344 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9345 wxASSERT(container
!= NULL
);
9351 case wxRICHTEXT_INSERT
:
9353 // Store a list of line start character and y positions so we can figure out which area
9354 // we need to refresh
9355 wxArrayInt optimizationLineCharPositions
;
9356 wxArrayInt optimizationLineYPositions
;
9358 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9359 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9362 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9363 container
->UpdateRanges();
9365 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9366 // Layout() would stop prematurely at the top level.
9367 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9369 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9371 // Character position to caret position
9372 newCaretPosition
--;
9374 // Don't take into account the last newline
9375 if (m_newParagraphs
.GetPartialParagraph())
9376 newCaretPosition
--;
9378 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9380 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9381 if (p
->GetRange().GetLength() == 1)
9382 newCaretPosition
--;
9385 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9387 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9389 wxRichTextEvent
cmdEvent(
9390 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9391 m_ctrl
? m_ctrl
->GetId() : -1);
9392 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9393 cmdEvent
.SetRange(GetRange());
9394 cmdEvent
.SetPosition(GetRange().GetStart());
9395 cmdEvent
.SetContainer(container
);
9397 m_buffer
->SendEvent(cmdEvent
);
9401 case wxRICHTEXT_DELETE
:
9403 wxArrayInt optimizationLineCharPositions
;
9404 wxArrayInt optimizationLineYPositions
;
9406 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9407 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9410 container
->DeleteRange(GetRange());
9411 container
->UpdateRanges();
9412 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9413 // Layout() would stop prematurely at the top level.
9414 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9416 long caretPos
= GetRange().GetStart()-1;
9417 if (caretPos
>= container
->GetOwnRange().GetEnd())
9420 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9422 wxRichTextEvent
cmdEvent(
9423 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9424 m_ctrl
? m_ctrl
->GetId() : -1);
9425 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9426 cmdEvent
.SetRange(GetRange());
9427 cmdEvent
.SetPosition(GetRange().GetStart());
9428 cmdEvent
.SetContainer(container
);
9430 m_buffer
->SendEvent(cmdEvent
);
9434 case wxRICHTEXT_CHANGE_STYLE
:
9436 ApplyParagraphs(GetNewParagraphs());
9438 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9439 // Layout() would stop prematurely at the top level.
9440 container
->InvalidateHierarchy(GetRange());
9442 UpdateAppearance(GetPosition());
9444 wxRichTextEvent
cmdEvent(
9445 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9446 m_ctrl
? m_ctrl
->GetId() : -1);
9447 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9448 cmdEvent
.SetRange(GetRange());
9449 cmdEvent
.SetPosition(GetRange().GetStart());
9450 cmdEvent
.SetContainer(container
);
9452 m_buffer
->SendEvent(cmdEvent
);
9456 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9458 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9461 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9462 obj
->GetAttributes() = m_attributes
;
9463 m_attributes
= oldAttr
;
9466 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9467 // Layout() would stop prematurely at the top level.
9468 container
->InvalidateHierarchy(GetRange());
9470 UpdateAppearance(GetPosition());
9472 wxRichTextEvent
cmdEvent(
9473 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9474 m_ctrl
? m_ctrl
->GetId() : -1);
9475 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9476 cmdEvent
.SetRange(GetRange());
9477 cmdEvent
.SetPosition(GetRange().GetStart());
9478 cmdEvent
.SetContainer(container
);
9480 m_buffer
->SendEvent(cmdEvent
);
9484 case wxRICHTEXT_CHANGE_OBJECT
:
9486 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9487 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9488 if (obj
&& m_object
)
9490 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9493 wxRichTextObject
* obj
= node
->GetData();
9494 node
->SetData(m_object
);
9499 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9500 // Layout() would stop prematurely at the top level.
9501 container
->InvalidateHierarchy(GetRange());
9503 UpdateAppearance(GetPosition());
9505 // TODO: send new kind of modification event
9516 bool wxRichTextAction::Undo()
9518 m_buffer
->Modify(true);
9520 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9521 wxASSERT(container
!= NULL
);
9527 case wxRICHTEXT_INSERT
:
9529 wxArrayInt optimizationLineCharPositions
;
9530 wxArrayInt optimizationLineYPositions
;
9532 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9533 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9536 container
->DeleteRange(GetRange());
9537 container
->UpdateRanges();
9538 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9539 // Layout() would stop prematurely at the top level.
9540 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9542 long newCaretPosition
= GetPosition() - 1;
9544 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9546 wxRichTextEvent
cmdEvent(
9547 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9548 m_ctrl
? m_ctrl
->GetId() : -1);
9549 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9550 cmdEvent
.SetRange(GetRange());
9551 cmdEvent
.SetPosition(GetRange().GetStart());
9552 cmdEvent
.SetContainer(container
);
9554 m_buffer
->SendEvent(cmdEvent
);
9558 case wxRICHTEXT_DELETE
:
9560 wxArrayInt optimizationLineCharPositions
;
9561 wxArrayInt optimizationLineYPositions
;
9563 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9564 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9567 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9568 container
->UpdateRanges();
9569 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9570 // Layout() would stop prematurely at the top level.
9571 container
->InvalidateHierarchy(GetRange());
9573 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9575 wxRichTextEvent
cmdEvent(
9576 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9577 m_ctrl
? m_ctrl
->GetId() : -1);
9578 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9579 cmdEvent
.SetRange(GetRange());
9580 cmdEvent
.SetPosition(GetRange().GetStart());
9581 cmdEvent
.SetContainer(container
);
9583 m_buffer
->SendEvent(cmdEvent
);
9587 case wxRICHTEXT_CHANGE_STYLE
:
9589 ApplyParagraphs(GetOldParagraphs());
9590 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9591 // Layout() would stop prematurely at the top level.
9592 container
->InvalidateHierarchy(GetRange());
9594 UpdateAppearance(GetPosition());
9596 wxRichTextEvent
cmdEvent(
9597 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9598 m_ctrl
? m_ctrl
->GetId() : -1);
9599 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9600 cmdEvent
.SetRange(GetRange());
9601 cmdEvent
.SetPosition(GetRange().GetStart());
9602 cmdEvent
.SetContainer(container
);
9604 m_buffer
->SendEvent(cmdEvent
);
9608 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9609 case wxRICHTEXT_CHANGE_OBJECT
:
9620 /// Update the control appearance
9621 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9623 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9624 wxASSERT(container
!= NULL
);
9630 m_ctrl
->SetFocusObject(container
);
9631 m_ctrl
->SetCaretPosition(caretPosition
);
9633 if (!m_ctrl
->IsFrozen())
9635 wxRect containerRect
= container
->GetRect();
9637 m_ctrl
->LayoutContent();
9639 // Refresh everything if there were floating objects or the container changed size
9640 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9641 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9643 m_ctrl
->Refresh(false);
9647 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9648 // Find refresh rectangle if we are in a position to optimise refresh
9649 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9653 wxSize clientSize
= m_ctrl
->GetClientSize();
9654 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9656 // Start/end positions
9658 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9660 bool foundEnd
= false;
9662 // position offset - how many characters were inserted
9663 int positionOffset
= GetRange().GetLength();
9665 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9666 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9667 positionOffset
= - positionOffset
;
9669 // find the first line which is being drawn at the same position as it was
9670 // before. Since we're talking about a simple insertion, we can assume
9671 // that the rest of the window does not need to be redrawn.
9673 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9674 // Since we support floating layout, we should redraw the whole para instead of just
9675 // the first line touching the invalid range.
9678 firstY
= para
->GetPosition().y
;
9681 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9684 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9685 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9688 wxRichTextLine
* line
= node2
->GetData();
9689 wxPoint pt
= line
->GetAbsolutePosition();
9690 wxRichTextRange range
= line
->GetAbsoluteRange();
9692 // we want to find the first line that is in the same position
9693 // as before. This will mean we're at the end of the changed text.
9695 if (pt
.y
> lastY
) // going past the end of the window, no more info
9697 node2
= wxRichTextLineList::compatibility_iterator();
9698 node
= wxRichTextObjectList::compatibility_iterator();
9700 // Detect last line in the buffer
9701 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9703 // If deleting text, make sure we refresh below as well as above
9704 if (positionOffset
>= 0)
9707 lastY
= pt
.y
+ line
->GetSize().y
;
9710 node2
= wxRichTextLineList::compatibility_iterator();
9711 node
= wxRichTextObjectList::compatibility_iterator();
9717 // search for this line being at the same position as before
9718 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9720 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9721 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9723 // Stop, we're now the same as we were
9728 node2
= wxRichTextLineList::compatibility_iterator();
9729 node
= wxRichTextObjectList::compatibility_iterator();
9737 node2
= node2
->GetNext();
9741 node
= node
->GetNext();
9744 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9746 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9748 // Convert to device coordinates
9749 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9750 m_ctrl
->RefreshRect(rect
);
9754 m_ctrl
->Refresh(false);
9756 m_ctrl
->PositionCaret();
9758 // This causes styles to persist when doing programmatic
9759 // content creation except when Freeze/Thaw is used, so
9760 // disable this and check for the consequences.
9761 // m_ctrl->SetDefaultStyleToCursorStyle();
9763 if (sendUpdateEvent
)
9764 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9769 /// Replace the buffer paragraphs with the new ones.
9770 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9772 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9773 wxASSERT(container
!= NULL
);
9777 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9780 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9781 wxASSERT (para
!= NULL
);
9783 // We'll replace the existing paragraph by finding the paragraph at this position,
9784 // delete its node data, and setting a copy as the new node data.
9785 // TODO: make more efficient by simply swapping old and new paragraph objects.
9787 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9790 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9793 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9794 newPara
->SetParent(container
);
9796 bufferParaNode
->SetData(newPara
);
9798 delete existingPara
;
9802 node
= node
->GetNext();
9809 * This stores beginning and end positions for a range of data.
9812 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
9814 /// Limit this range to be within 'range'
9815 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
9817 if (m_start
< range
.m_start
)
9818 m_start
= range
.m_start
;
9820 if (m_end
> range
.m_end
)
9821 m_end
= range
.m_end
;
9827 * wxRichTextImage implementation
9828 * This object represents an image.
9831 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
9833 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9834 wxRichTextObject(parent
)
9836 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
9838 SetAttributes(*charStyle
);
9841 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9842 wxRichTextObject(parent
)
9844 m_imageBlock
= imageBlock
;
9846 SetAttributes(*charStyle
);
9849 /// Create a cached image at the required size
9850 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
9852 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9854 if (!m_imageBlock
.IsOk())
9858 m_imageBlock
.Load(image
);
9862 int width
= image
.GetWidth();
9863 int height
= image
.GetHeight();
9865 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9867 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9868 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9870 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9872 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9874 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9875 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9877 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9880 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
9881 m_imageCache
= wxBitmap(image
);
9884 // If the original width and height is small, e.g. 400 or below,
9885 // scale up and then down to improve image quality. This can make
9886 // a big difference, with not much performance hit.
9887 int upscaleThreshold
= 400;
9889 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
9891 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
9892 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9895 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9896 m_imageCache
= wxBitmap(img
);
9900 return m_imageCache
.IsOk();
9904 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
9909 // Don't need cached size AFAIK
9910 // wxSize size = GetCachedSize();
9911 if (!LoadImageCache(dc
))
9914 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9917 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
9919 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
9922 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9923 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9924 marginRect
= rect
; // outer rectangle, will calculate contentRect
9925 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9927 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
9929 if (selection
.WithinSelection(range
.GetStart(), this))
9931 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9932 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9933 dc
.SetLogicalFunction(wxINVERT
);
9934 dc
.DrawRectangle(contentRect
);
9935 dc
.SetLogicalFunction(wxCOPY
);
9941 /// Lay the item out
9942 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(style
))
9944 if (!LoadImageCache(dc
))
9947 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9948 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9949 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9950 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9952 wxSize overallSize
= marginRect
.GetSize();
9954 SetCachedSize(overallSize
);
9955 SetMaxSize(overallSize
);
9956 SetMinSize(overallSize
);
9957 SetPosition(rect
.GetPosition());
9962 /// Get/set the object size for the given range. Returns false if the range
9963 /// is invalid for this object.
9964 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
9966 if (!range
.IsWithin(GetRange()))
9969 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
9971 size
.x
= 0; size
.y
= 0;
9973 partialExtents
->Add(0);
9977 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9978 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9979 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9980 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9982 wxSize overallSize
= marginRect
.GetSize();
9985 partialExtents
->Add(overallSize
.x
);
9992 // Get the 'natural' size for an object. For an image, it would be the
9994 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
9996 wxTextAttrSize size
;
9997 if (GetImageCache().IsOk())
9999 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10000 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10007 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10009 wxRichTextObject::Copy(obj
);
10011 m_imageBlock
= obj
.m_imageBlock
;
10014 /// Edit properties via a GUI
10015 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10017 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10018 imageDlg
.SetAttributes(GetAttributes());
10020 if (imageDlg
.ShowModal() == wxID_OK
)
10022 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10023 // indeterminate in the object.
10024 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10036 /// Compare two attribute objects
10037 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10039 return (attr1
== attr2
);
10042 // Partial equality test taking flags into account
10043 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10045 return attr1
.EqPartial(attr2
);
10049 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10051 if (tabs1
.GetCount() != tabs2
.GetCount())
10055 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10057 if (tabs1
[i
] != tabs2
[i
])
10063 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10065 return destStyle
.Apply(style
, compareWith
);
10068 // Remove attributes
10069 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10071 return destStyle
.RemoveStyle(style
);
10074 /// Combine two bitlists, specifying the bits of interest with separate flags.
10075 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10077 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10080 /// Compare two bitlists
10081 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10083 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10086 /// Split into paragraph and character styles
10087 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10089 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10092 /// Convert a decimal to Roman numerals
10093 wxString
wxRichTextDecimalToRoman(long n
)
10095 static wxArrayInt decimalNumbers
;
10096 static wxArrayString romanNumbers
;
10101 decimalNumbers
.Clear();
10102 romanNumbers
.Clear();
10103 return wxEmptyString
;
10106 if (decimalNumbers
.GetCount() == 0)
10108 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10110 wxRichTextAddDecRom(1000, wxT("M"));
10111 wxRichTextAddDecRom(900, wxT("CM"));
10112 wxRichTextAddDecRom(500, wxT("D"));
10113 wxRichTextAddDecRom(400, wxT("CD"));
10114 wxRichTextAddDecRom(100, wxT("C"));
10115 wxRichTextAddDecRom(90, wxT("XC"));
10116 wxRichTextAddDecRom(50, wxT("L"));
10117 wxRichTextAddDecRom(40, wxT("XL"));
10118 wxRichTextAddDecRom(10, wxT("X"));
10119 wxRichTextAddDecRom(9, wxT("IX"));
10120 wxRichTextAddDecRom(5, wxT("V"));
10121 wxRichTextAddDecRom(4, wxT("IV"));
10122 wxRichTextAddDecRom(1, wxT("I"));
10128 while (n
> 0 && i
< 13)
10130 if (n
>= decimalNumbers
[i
])
10132 n
-= decimalNumbers
[i
];
10133 roman
+= romanNumbers
[i
];
10140 if (roman
.IsEmpty())
10146 * wxRichTextFileHandler
10147 * Base class for file handlers
10150 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10152 #if wxUSE_FFILE && wxUSE_STREAMS
10153 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10155 wxFFileInputStream
stream(filename
);
10157 return LoadFile(buffer
, stream
);
10162 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10164 wxFFileOutputStream
stream(filename
);
10166 return SaveFile(buffer
, stream
);
10170 #endif // wxUSE_FFILE && wxUSE_STREAMS
10172 /// Can we handle this filename (if using files)? By default, checks the extension.
10173 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10175 wxString path
, file
, ext
;
10176 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10178 return (ext
.Lower() == GetExtension());
10182 * wxRichTextTextHandler
10183 * Plain text handler
10186 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10189 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10191 if (!stream
.IsOk())
10197 while (!stream
.Eof())
10199 int ch
= stream
.GetC();
10203 if (ch
== 10 && lastCh
!= 13)
10206 if (ch
> 0 && ch
!= 10)
10213 buffer
->ResetAndClearCommands();
10215 buffer
->AddParagraphs(str
);
10216 buffer
->UpdateRanges();
10221 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10223 if (!stream
.IsOk())
10226 wxString text
= buffer
->GetText();
10228 wxString newLine
= wxRichTextLineBreakChar
;
10229 text
.Replace(newLine
, wxT("\n"));
10231 wxCharBuffer buf
= text
.ToAscii();
10233 stream
.Write((const char*) buf
, text
.length());
10236 #endif // wxUSE_STREAMS
10239 * Stores information about an image, in binary in-memory form
10242 wxRichTextImageBlock::wxRichTextImageBlock()
10247 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10253 wxRichTextImageBlock::~wxRichTextImageBlock()
10258 void wxRichTextImageBlock::Init()
10262 m_imageType
= wxBITMAP_TYPE_INVALID
;
10265 void wxRichTextImageBlock::Clear()
10269 m_imageType
= wxBITMAP_TYPE_INVALID
;
10273 // Load the original image into a memory block.
10274 // If the image is not a JPEG, we must convert it into a JPEG
10275 // to conserve space.
10276 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10277 // load the image a 2nd time.
10279 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10280 wxImage
& image
, bool convertToJPEG
)
10282 m_imageType
= imageType
;
10284 wxString
filenameToRead(filename
);
10285 bool removeFile
= false;
10287 if (imageType
== wxBITMAP_TYPE_INVALID
)
10288 return false; // Could not determine image type
10290 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10292 wxString tempFile
=
10293 wxFileName::CreateTempFileName(_("image"));
10295 wxASSERT(!tempFile
.IsEmpty());
10297 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10298 filenameToRead
= tempFile
;
10301 m_imageType
= wxBITMAP_TYPE_JPEG
;
10304 if (!file
.Open(filenameToRead
))
10307 m_dataSize
= (size_t) file
.Length();
10312 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10315 wxRemoveFile(filenameToRead
);
10317 return (m_data
!= NULL
);
10320 // Make an image block from the wxImage in the given
10322 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10324 image
.SetOption(wxT("quality"), quality
);
10326 if (imageType
== wxBITMAP_TYPE_INVALID
)
10327 return false; // Could not determine image type
10329 return DoMakeImageBlock(image
, imageType
);
10332 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10333 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10335 if (imageType
== wxBITMAP_TYPE_INVALID
)
10336 return false; // Could not determine image type
10338 return DoMakeImageBlock(image
, imageType
);
10341 // Makes the image block
10342 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10344 wxMemoryOutputStream memStream
;
10345 if (!image
.SaveFile(memStream
, imageType
))
10350 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10358 m_imageType
= imageType
;
10359 m_dataSize
= memStream
.GetSize();
10361 memStream
.CopyTo(m_data
, m_dataSize
);
10363 return (m_data
!= NULL
);
10367 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10369 return WriteBlock(filename
, m_data
, m_dataSize
);
10372 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10374 m_imageType
= block
.m_imageType
;
10376 m_dataSize
= block
.m_dataSize
;
10377 if (m_dataSize
== 0)
10380 m_data
= new unsigned char[m_dataSize
];
10382 for (i
= 0; i
< m_dataSize
; i
++)
10383 m_data
[i
] = block
.m_data
[i
];
10387 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10392 // Load a wxImage from the block
10393 bool wxRichTextImageBlock::Load(wxImage
& image
)
10398 // Read in the image.
10400 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10401 bool success
= image
.LoadFile(mstream
, GetImageType());
10403 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10404 wxASSERT(!tempFile
.IsEmpty());
10406 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10410 success
= image
.LoadFile(tempFile
, GetImageType());
10411 wxRemoveFile(tempFile
);
10417 // Write data in hex to a stream
10418 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10420 if (m_dataSize
== 0)
10423 int bufSize
= 100000;
10424 if (int(2*m_dataSize
) < bufSize
)
10425 bufSize
= 2*m_dataSize
;
10426 char* buf
= new char[bufSize
+1];
10428 int left
= m_dataSize
;
10433 if (left
*2 > bufSize
)
10435 n
= bufSize
; left
-= (bufSize
/2);
10439 n
= left
*2; left
= 0;
10443 for (i
= 0; i
< (n
/2); i
++)
10445 wxDecToHex(m_data
[j
], b
, b
+1);
10450 stream
.Write((const char*) buf
, n
);
10456 // Read data in hex from a stream
10457 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10459 int dataSize
= length
/2;
10464 // create a null terminated temporary string:
10468 m_data
= new unsigned char[dataSize
];
10470 for (i
= 0; i
< dataSize
; i
++)
10472 str
[0] = (char)stream
.GetC();
10473 str
[1] = (char)stream
.GetC();
10475 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10478 m_dataSize
= dataSize
;
10479 m_imageType
= imageType
;
10484 // Allocate and read from stream as a block of memory
10485 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10487 unsigned char* block
= new unsigned char[size
];
10491 stream
.Read(block
, size
);
10496 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10498 wxFileInputStream
stream(filename
);
10499 if (!stream
.IsOk())
10502 return ReadBlock(stream
, size
);
10505 // Write memory block to stream
10506 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10508 stream
.Write((void*) block
, size
);
10509 return stream
.IsOk();
10513 // Write memory block to file
10514 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10516 wxFileOutputStream
outStream(filename
);
10517 if (!outStream
.IsOk())
10520 return WriteBlock(outStream
, block
, size
);
10523 // Gets the extension for the block's type
10524 wxString
wxRichTextImageBlock::GetExtension() const
10526 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10528 return handler
->GetExtension();
10530 return wxEmptyString
;
10536 * The data object for a wxRichTextBuffer
10539 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10541 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10543 m_richTextBuffer
= richTextBuffer
;
10545 // this string should uniquely identify our format, but is otherwise
10547 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10549 SetFormat(m_formatRichTextBuffer
);
10552 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10554 delete m_richTextBuffer
;
10557 // after a call to this function, the richTextBuffer is owned by the caller and it
10558 // is responsible for deleting it!
10559 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10561 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10562 m_richTextBuffer
= NULL
;
10564 return richTextBuffer
;
10567 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10569 return m_formatRichTextBuffer
;
10572 size_t wxRichTextBufferDataObject::GetDataSize() const
10574 if (!m_richTextBuffer
)
10580 wxStringOutputStream
stream(& bufXML
);
10581 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10583 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10589 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10590 return strlen(buffer
) + 1;
10592 return bufXML
.Length()+1;
10596 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10598 if (!pBuf
|| !m_richTextBuffer
)
10604 wxStringOutputStream
stream(& bufXML
);
10605 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10607 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10613 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10614 size_t len
= strlen(buffer
);
10615 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10616 ((char*) pBuf
)[len
] = 0;
10618 size_t len
= bufXML
.Length();
10619 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10620 ((char*) pBuf
)[len
] = 0;
10626 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10628 wxDELETE(m_richTextBuffer
);
10630 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10632 m_richTextBuffer
= new wxRichTextBuffer
;
10634 wxStringInputStream
stream(bufXML
);
10635 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10637 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10639 wxDELETE(m_richTextBuffer
);
10651 * wxRichTextFontTable
10652 * Manages quick access to a pool of fonts for rendering rich text
10655 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10657 class wxRichTextFontTableData
: public wxObjectRefData
10660 wxRichTextFontTableData() {}
10662 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10664 wxRichTextFontTableHashMap m_hashMap
;
10667 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10669 wxString
facename(fontSpec
.GetFontFaceName());
10670 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()));
10671 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10673 if ( entry
== m_hashMap
.end() )
10675 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10676 m_hashMap
[spec
] = font
;
10681 return entry
->second
;
10685 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10687 wxRichTextFontTable::wxRichTextFontTable()
10689 m_refData
= new wxRichTextFontTableData
;
10692 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10698 wxRichTextFontTable::~wxRichTextFontTable()
10703 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10705 return (m_refData
== table
.m_refData
);
10708 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10713 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10715 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10717 return data
->FindFont(fontSpec
);
10722 void wxRichTextFontTable::Clear()
10724 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10726 data
->m_hashMap
.clear();
10732 void wxTextBoxAttr::Reset()
10735 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10736 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10737 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10738 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10742 m_position
.Reset();
10751 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10754 m_flags
== attr
.m_flags
&&
10755 m_floatMode
== attr
.m_floatMode
&&
10756 m_clearMode
== attr
.m_clearMode
&&
10757 m_collapseMode
== attr
.m_collapseMode
&&
10758 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10760 m_margins
== attr
.m_margins
&&
10761 m_padding
== attr
.m_padding
&&
10762 m_position
== attr
.m_position
&&
10764 m_size
== attr
.m_size
&&
10766 m_border
== attr
.m_border
&&
10767 m_outline
== attr
.m_outline
10771 // Partial equality test
10772 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10774 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10777 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10780 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10783 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10788 if (!m_position
.EqPartial(attr
.m_position
))
10793 if (!m_margins
.EqPartial(attr
.m_margins
))
10798 if (!m_padding
.EqPartial(attr
.m_padding
))
10803 if (!GetBorder().EqPartial(attr
.GetBorder()))
10808 if (!GetOutline().EqPartial(attr
.GetOutline()))
10814 // Merges the given attributes. If compareWith
10815 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10816 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10817 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
10819 if (attr
.HasFloatMode())
10821 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
10822 SetFloatMode(attr
.GetFloatMode());
10825 if (attr
.HasClearMode())
10827 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
10828 SetClearMode(attr
.GetClearMode());
10831 if (attr
.HasCollapseBorders())
10833 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
10834 SetCollapseBorders(attr
.GetCollapseBorders());
10837 if (attr
.HasVerticalAlignment())
10839 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
10840 SetVerticalAlignment(attr
.GetVerticalAlignment());
10843 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
10844 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
10845 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
10847 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
10849 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
10850 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
10855 // Remove specified attributes from this object
10856 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
10858 if (attr
.HasFloatMode())
10859 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10861 if (attr
.HasClearMode())
10862 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10864 if (attr
.HasCollapseBorders())
10865 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10867 if (attr
.HasVerticalAlignment())
10868 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10870 m_margins
.RemoveStyle(attr
.m_margins
);
10871 m_padding
.RemoveStyle(attr
.m_padding
);
10872 m_position
.RemoveStyle(attr
.m_position
);
10874 m_size
.RemoveStyle(attr
.m_size
);
10876 m_border
.RemoveStyle(attr
.m_border
);
10877 m_outline
.RemoveStyle(attr
.m_outline
);
10882 // Collects the attributes that are common to a range of content, building up a note of
10883 // which attributes are absent in some objects and which clash in some objects.
10884 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
10886 if (attr
.HasFloatMode())
10888 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
10890 if (HasFloatMode())
10892 if (GetFloatMode() != attr
.GetFloatMode())
10894 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10895 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10899 SetFloatMode(attr
.GetFloatMode());
10903 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10905 if (attr
.HasClearMode())
10907 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
10909 if (HasClearMode())
10911 if (GetClearMode() != attr
.GetClearMode())
10913 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10914 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10918 SetClearMode(attr
.GetClearMode());
10922 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10924 if (attr
.HasCollapseBorders())
10926 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
10928 if (HasCollapseBorders())
10930 if (GetCollapseBorders() != attr
.GetCollapseBorders())
10932 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10933 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10937 SetCollapseBorders(attr
.GetCollapseBorders());
10941 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10943 if (attr
.HasVerticalAlignment())
10945 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
10947 if (HasVerticalAlignment())
10949 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
10951 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10952 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10956 SetVerticalAlignment(attr
.GetVerticalAlignment());
10960 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10962 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
10963 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
10964 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
10966 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
10968 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
10969 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
10974 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
10976 wxTextAttr::Copy(attr
);
10978 m_textBoxAttr
= attr
.m_textBoxAttr
;
10981 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
10983 if (!(wxTextAttr::operator==(attr
)))
10986 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
10989 // Partial equality test taking comparison object into account
10990 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
10992 if (!(wxTextAttr::EqPartial(attr
)))
10995 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
10998 // Merges the given attributes. If compareWith
10999 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11000 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11001 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11003 wxTextAttr::Apply(style
, compareWith
);
11005 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11008 // Remove specified attributes from this object
11009 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11011 wxTextAttr::RemoveStyle(*this, attr
);
11013 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11016 // Collects the attributes that are common to a range of content, building up a note of
11017 // which attributes are absent in some objects and which clash in some objects.
11018 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11020 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11022 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11025 // Partial equality test
11026 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11028 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11031 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11034 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11040 // Apply border to 'this', but not if the same as compareWith
11041 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11043 if (border
.HasStyle())
11045 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11046 SetStyle(border
.GetStyle());
11048 if (border
.HasColour())
11050 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11051 SetColour(border
.GetColourLong());
11053 if (border
.HasWidth())
11055 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11056 SetWidth(border
.GetWidth());
11062 // Remove specified attributes from this object
11063 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11065 if (attr
.HasStyle() && HasStyle())
11066 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11067 if (attr
.HasColour() && HasColour())
11068 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11069 if (attr
.HasWidth() && HasWidth())
11070 m_borderWidth
.Reset();
11075 // Collects the attributes that are common to a range of content, building up a note of
11076 // which attributes are absent in some objects and which clash in some objects.
11077 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11079 if (attr
.HasStyle())
11081 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11085 if (GetStyle() != attr
.GetStyle())
11087 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11088 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11092 SetStyle(attr
.GetStyle());
11096 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11098 if (attr
.HasColour())
11100 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11104 if (GetColour() != attr
.GetColour())
11106 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11107 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11111 SetColour(attr
.GetColourLong());
11115 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11117 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11120 // Partial equality test
11121 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11123 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11124 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11127 // Apply border to 'this', but not if the same as compareWith
11128 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11130 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11131 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11132 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11133 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11137 // Remove specified attributes from this object
11138 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11140 m_left
.RemoveStyle(attr
.m_left
);
11141 m_right
.RemoveStyle(attr
.m_right
);
11142 m_top
.RemoveStyle(attr
.m_top
);
11143 m_bottom
.RemoveStyle(attr
.m_bottom
);
11147 // Collects the attributes that are common to a range of content, building up a note of
11148 // which attributes are absent in some objects and which clash in some objects.
11149 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11151 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11152 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11153 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11154 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11157 // Set style of all borders
11158 void wxTextAttrBorders::SetStyle(int style
)
11160 m_left
.SetStyle(style
);
11161 m_right
.SetStyle(style
);
11162 m_top
.SetStyle(style
);
11163 m_bottom
.SetStyle(style
);
11166 // Set colour of all borders
11167 void wxTextAttrBorders::SetColour(unsigned long colour
)
11169 m_left
.SetColour(colour
);
11170 m_right
.SetColour(colour
);
11171 m_top
.SetColour(colour
);
11172 m_bottom
.SetColour(colour
);
11175 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11177 m_left
.SetColour(colour
);
11178 m_right
.SetColour(colour
);
11179 m_top
.SetColour(colour
);
11180 m_bottom
.SetColour(colour
);
11183 // Set width of all borders
11184 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11186 m_left
.SetWidth(width
);
11187 m_right
.SetWidth(width
);
11188 m_top
.SetWidth(width
);
11189 m_bottom
.SetWidth(width
);
11192 // Partial equality test
11193 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11195 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11201 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11205 if (!(compareWith
&& dim
== (*compareWith
)))
11212 // Collects the attributes that are common to a range of content, building up a note of
11213 // which attributes are absent in some objects and which clash in some objects.
11214 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11216 if (attr
.IsValid())
11218 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11222 if (!((*this) == attr
))
11224 clashingAttr
.SetValid(true);
11233 absentAttr
.SetValid(true);
11236 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11238 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11241 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11243 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11246 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11248 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11251 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11253 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11256 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11258 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11259 return ConvertTenthsMMToPixels(dim
.GetValue());
11260 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11261 return dim
.GetValue();
11262 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11264 wxASSERT(m_parentSize
!= wxDefaultSize
);
11265 if (direction
== wxHORIZONTAL
)
11266 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11268 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11277 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11279 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11280 return dim
.GetValue();
11281 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11282 return ConvertPixelsToTenthsMM(dim
.GetValue());
11290 // Partial equality test
11291 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11293 if (!m_left
.EqPartial(dims
.m_left
))
11296 if (!m_right
.EqPartial(dims
.m_right
))
11299 if (!m_top
.EqPartial(dims
.m_top
))
11302 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11308 // Apply border to 'this', but not if the same as compareWith
11309 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11311 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11312 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11313 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11314 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11319 // Remove specified attributes from this object
11320 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11322 if (attr
.m_left
.IsValid())
11324 if (attr
.m_right
.IsValid())
11326 if (attr
.m_top
.IsValid())
11328 if (attr
.m_bottom
.IsValid())
11334 // Collects the attributes that are common to a range of content, building up a note of
11335 // which attributes are absent in some objects and which clash in some objects.
11336 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11338 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11339 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11340 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11341 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11344 // Partial equality test
11345 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11347 if (!m_width
.EqPartial(size
.m_width
))
11350 if (!m_height
.EqPartial(size
.m_height
))
11356 // Apply border to 'this', but not if the same as compareWith
11357 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11359 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11360 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11365 // Remove specified attributes from this object
11366 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11368 if (attr
.m_width
.IsValid())
11370 if (attr
.m_height
.IsValid())
11376 // Collects the attributes that are common to a range of content, building up a note of
11377 // which attributes are absent in some objects and which clash in some objects.
11378 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11380 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11381 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11384 // Collects the attributes that are common to a range of content, building up a note of
11385 // which attributes are absent in some objects and which clash in some objects.
11386 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11388 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11389 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11391 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11393 if (attr
.HasFont())
11395 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11397 if (currentStyle
.HasFontSize())
11399 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11401 // Clash of attr - mark as such
11402 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11403 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11407 currentStyle
.SetFontSize(attr
.GetFontSize());
11410 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11412 if (currentStyle
.HasFontItalic())
11414 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11416 // Clash of attr - mark as such
11417 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11418 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11422 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11425 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11427 if (currentStyle
.HasFontFamily())
11429 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11431 // Clash of attr - mark as such
11432 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11433 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11437 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11440 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11442 if (currentStyle
.HasFontWeight())
11444 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11446 // Clash of attr - mark as such
11447 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11448 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11452 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11455 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11457 if (currentStyle
.HasFontFaceName())
11459 wxString
faceName1(currentStyle
.GetFontFaceName());
11460 wxString
faceName2(attr
.GetFontFaceName());
11462 if (faceName1
!= faceName2
)
11464 // Clash of attr - mark as such
11465 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11466 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11470 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11473 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11475 if (currentStyle
.HasFontUnderlined())
11477 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11479 // Clash of attr - mark as such
11480 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11481 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11485 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11489 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11491 if (currentStyle
.HasTextColour())
11493 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11495 // Clash of attr - mark as such
11496 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11497 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11501 currentStyle
.SetTextColour(attr
.GetTextColour());
11504 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11506 if (currentStyle
.HasBackgroundColour())
11508 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11510 // Clash of attr - mark as such
11511 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11512 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11516 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11519 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11521 if (currentStyle
.HasAlignment())
11523 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11525 // Clash of attr - mark as such
11526 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11527 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11531 currentStyle
.SetAlignment(attr
.GetAlignment());
11534 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11536 if (currentStyle
.HasTabs())
11538 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11540 // Clash of attr - mark as such
11541 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11542 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11546 currentStyle
.SetTabs(attr
.GetTabs());
11549 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11551 if (currentStyle
.HasLeftIndent())
11553 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11555 // Clash of attr - mark as such
11556 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11557 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11561 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11564 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11566 if (currentStyle
.HasRightIndent())
11568 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11570 // Clash of attr - mark as such
11571 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11572 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11576 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11579 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11581 if (currentStyle
.HasParagraphSpacingAfter())
11583 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11585 // Clash of attr - mark as such
11586 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11587 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11591 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11594 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11596 if (currentStyle
.HasParagraphSpacingBefore())
11598 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11600 // Clash of attr - mark as such
11601 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11602 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11606 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11609 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11611 if (currentStyle
.HasLineSpacing())
11613 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11615 // Clash of attr - mark as such
11616 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11617 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11621 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11624 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11626 if (currentStyle
.HasCharacterStyleName())
11628 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11630 // Clash of attr - mark as such
11631 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11632 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11636 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11639 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11641 if (currentStyle
.HasParagraphStyleName())
11643 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11645 // Clash of attr - mark as such
11646 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11647 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11651 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11654 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11656 if (currentStyle
.HasListStyleName())
11658 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11660 // Clash of attr - mark as such
11661 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11662 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11666 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11669 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11671 if (currentStyle
.HasBulletStyle())
11673 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11675 // Clash of attr - mark as such
11676 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11677 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11681 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11684 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11686 if (currentStyle
.HasBulletNumber())
11688 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11690 // Clash of attr - mark as such
11691 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11692 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11696 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11699 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11701 if (currentStyle
.HasBulletText())
11703 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11705 // Clash of attr - mark as such
11706 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11707 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11712 currentStyle
.SetBulletText(attr
.GetBulletText());
11713 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11717 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11719 if (currentStyle
.HasBulletName())
11721 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11723 // Clash of attr - mark as such
11724 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11725 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11730 currentStyle
.SetBulletName(attr
.GetBulletName());
11734 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11736 if (currentStyle
.HasURL())
11738 if (currentStyle
.GetURL() != attr
.GetURL())
11740 // Clash of attr - mark as such
11741 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11742 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
11747 currentStyle
.SetURL(attr
.GetURL());
11751 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
11753 if (currentStyle
.HasTextEffects())
11755 // We need to find the bits in the new attr that are different:
11756 // just look at those bits that are specified by the new attr.
11758 // We need to remove the bits and flags that are not common between current attr
11759 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11760 // previous styles.
11762 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
11763 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
11765 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
11767 // Find the text effects that were different, using XOR
11768 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
11770 // Clash of attr - mark as such
11771 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
11772 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
11777 currentStyle
.SetTextEffects(attr
.GetTextEffects());
11778 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
11781 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11782 // that we've looked at so far
11783 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
11784 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
11786 if (currentStyle
.GetTextEffectFlags() == 0)
11787 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
11790 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
11792 if (currentStyle
.HasOutlineLevel())
11794 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
11796 // Clash of attr - mark as such
11797 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11798 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11802 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
11806 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
11808 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
11810 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
11812 if (m_properties
.GetCount() != props
.GetCount())
11816 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11818 const wxVariant
& var1
= m_properties
[i
];
11819 int idx
= props
.Find(var1
.GetName());
11822 const wxVariant
& var2
= props
.m_properties
[idx
];
11823 if (!(var1
== var2
))
11830 wxArrayString
wxRichTextProperties::GetPropertyNames() const
11834 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11836 arr
.Add(m_properties
[i
].GetName());
11841 int wxRichTextProperties::Find(const wxString
& name
) const
11844 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11846 if (m_properties
[i
].GetName() == name
)
11852 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
11854 int idx
= Find(name
);
11855 if (idx
== wxNOT_FOUND
)
11856 SetProperty(name
, wxString());
11858 if (idx
!= wxNOT_FOUND
)
11860 return & (*this)[idx
];
11866 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
11868 static const wxVariant nullVariant
;
11869 int idx
= Find(name
);
11871 return m_properties
[idx
];
11873 return nullVariant
;
11876 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
11878 return GetProperty(name
).GetString();
11881 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
11883 return GetProperty(name
).GetLong();
11886 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
11888 return GetProperty(name
).GetBool();
11891 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
11893 return GetProperty(name
).GetDouble();
11896 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
11898 wxASSERT(!variant
.GetName().IsEmpty());
11900 int idx
= Find(variant
.GetName());
11903 m_properties
.Add(variant
);
11905 m_properties
[idx
] = variant
;
11908 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
11910 int idx
= Find(name
);
11911 wxVariant
var(variant
);
11915 m_properties
.Add(var
);
11917 m_properties
[idx
] = var
;
11920 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
11922 SetProperty(name
, wxVariant(value
, name
));
11925 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
11927 SetProperty(name
, wxVariant(value
, name
));
11930 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
11932 SetProperty(name
, wxVariant(value
, name
));
11935 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
11937 SetProperty(name
, wxVariant(value
, name
));
11940 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
11942 if (m_address
.GetCount() == 0)
11943 return topLevelContainer
;
11945 wxRichTextCompositeObject
* p
= topLevelContainer
;
11947 while (p
&& i
< m_address
.GetCount())
11949 int pos
= m_address
[i
];
11950 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
11951 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
11954 wxRichTextObject
* p1
= p
->GetChild(pos
);
11955 if (i
== (m_address
.GetCount()-1))
11958 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
11964 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
11968 if (topLevelContainer
== obj
)
11971 wxRichTextObject
* o
= obj
;
11974 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
11978 int pos
= p
->GetChildren().IndexOf(o
);
11982 m_address
.Insert(pos
, 0);
11984 if (p
== topLevelContainer
)
11993 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
11995 if (m_container
!= sel
.m_container
)
11997 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12000 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12001 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12006 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12007 // or none at the level of the object's container.
12008 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12012 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12014 if (container
== m_container
)
12017 container
= obj
->GetContainer();
12020 if (container
->GetParent())
12022 // If we found that our object's container is within the range of
12023 // a selection higher up, then assume the whole original object
12024 // is also selected.
12025 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12026 if (parentContainer
== m_container
)
12028 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12030 wxRichTextRangeArray ranges
;
12031 ranges
.Add(obj
->GetRange());
12036 container
= parentContainer
;
12045 return wxRichTextRangeArray();
12048 // Is the given position within the selection?
12049 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12055 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12056 return WithinSelection(pos
, selectionRanges
);
12060 // Is the given position within the selection range?
12061 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12064 for (i
= 0; i
< ranges
.GetCount(); i
++)
12066 const wxRichTextRange
& range
= ranges
[i
];
12067 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12073 // Is the given range completely within the selection range?
12074 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12077 for (i
= 0; i
< ranges
.GetCount(); i
++)
12079 const wxRichTextRange
& eachRange
= ranges
[i
];
12080 if (range
.IsWithin(eachRange
))