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().GetBottom().IsValid())
868 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
870 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
871 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
872 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
873 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
874 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
875 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
876 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
877 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
879 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
880 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
881 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
882 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
883 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
884 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
885 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
886 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
888 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
889 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
890 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
891 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
892 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
893 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
894 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
895 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
897 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
898 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
899 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
900 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
902 if (marginRect
!= wxRect())
904 contentRect
.x
= marginRect
.x
+ leftTotal
;
905 contentRect
.y
= marginRect
.y
+ topTotal
;
906 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
907 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
911 marginRect
.x
= contentRect
.x
- leftTotal
;
912 marginRect
.y
= contentRect
.y
- topTotal
;
913 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
914 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
917 borderRect
.x
= marginRect
.x
+ marginLeft
;
918 borderRect
.y
= marginRect
.y
+ marginTop
;
919 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
920 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
922 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
923 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
924 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
925 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
927 // The outline is outside the margin and doesn't influence the overall box position or content size.
928 outlineRect
.x
= marginRect
.x
- outlineLeft
;
929 outlineRect
.y
= marginRect
.y
- outlineTop
;
930 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
931 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
936 // Get the total margin for the object in pixels, taking into account margin, padding and border size
937 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
938 int& topMargin
, int& bottomMargin
)
940 // Assume boxRect is the area around the content
941 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
942 marginRect
= wxRect(0, 0, 1000, 1000);
944 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
946 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
947 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
948 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
949 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
954 // Returns the rectangle which the child has available to it given restrictions specified in the
955 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
956 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;
1917 // First get the size of the paragraphs we won't be laying out
1918 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1919 while (n
&& n
!= node
)
1921 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1924 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1925 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1926 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1933 // Assume this box only contains paragraphs
1935 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1936 // Unsure if this is needed
1937 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1939 if (child
&& child
->IsShown())
1941 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1942 if ( !forceQuickLayout
&&
1944 child
->GetLines().IsEmpty() ||
1945 !child
->GetRange().IsOutside(invalidRange
)) )
1947 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1948 // lays out the object again using the minimum size
1949 child
->LayoutToBestSize(dc
, GetBuffer(),
1950 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1952 // Layout must set the cached size
1953 availableSpace
.y
+= child
->GetCachedSize().y
;
1954 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1955 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1956 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1958 // If we're just formatting the visible part of the buffer,
1959 // and we're now past the bottom of the window, and we don't have any
1960 // floating objects (since they may cause wrapping to change for the rest of the
1961 // the buffer), start quick layout.
1962 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1963 forceQuickLayout
= true;
1967 // We're outside the immediately affected range, so now let's just
1968 // move everything up or down. This assumes that all the children have previously
1969 // been laid out and have wrapped line lists associated with them.
1970 // TODO: check all paragraphs before the affected range.
1972 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1976 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1979 if (child
->GetLines().GetCount() == 0)
1981 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1982 // lays out the object again using the minimum size
1983 child
->LayoutToBestSize(dc
, GetBuffer(),
1984 GetAttributes(), child
->GetAttributes(), availableSpace
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1986 //child->Layout(dc, availableChildRect, style);
1989 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1991 availableSpace
.y
+= child
->GetCachedSize().y
;
1992 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1993 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1994 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1997 node
= node
->GetNext();
2003 node
= node
->GetNext();
2006 node
= m_children
.GetLast();
2007 if (node
&& node
->GetData()->IsShown())
2009 wxRichTextObject
* child
= node
->GetData();
2010 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
2011 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2014 maxHeight
= 0; // topMargin + bottomMargin;
2016 // TODO: (also in para layout) should set the
2017 // object's size to an absolute one if specified,
2018 // but if not specified, calculate it from content.
2020 // We need to add back the margins etc.
2022 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2023 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2024 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2025 SetCachedSize(marginRect
.GetSize());
2028 // The maximum size is the greatest of all maximum widths for all paragraphs.
2030 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2031 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2032 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2033 SetMaxSize(marginRect
.GetSize());
2036 // The minimum size is the greatest of all minimum widths for all paragraphs.
2038 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2039 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2040 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2041 SetMinSize(marginRect
.GetSize());
2044 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2045 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2048 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2049 if (leftOverSpace
> 0)
2051 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2053 yOffset
= (leftOverSpace
/2);
2055 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2057 yOffset
= leftOverSpace
;
2061 // Move all the children to vertically align the content
2062 // This doesn't take into account floating objects, unfortunately.
2065 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2068 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2070 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2072 node
= node
->GetNext();
2077 m_invalidRange
= wxRICHTEXT_NONE
;
2082 /// Get/set the size for the given range.
2083 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2087 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2088 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2090 // First find the first paragraph whose starting position is within the range.
2091 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2094 // child is a paragraph
2095 wxRichTextObject
* child
= node
->GetData();
2096 const wxRichTextRange
& r
= child
->GetRange();
2098 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2104 node
= node
->GetNext();
2107 // Next find the last paragraph containing part of the range
2108 node
= m_children
.GetFirst();
2111 // child is a paragraph
2112 wxRichTextObject
* child
= node
->GetData();
2113 const wxRichTextRange
& r
= child
->GetRange();
2115 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2121 node
= node
->GetNext();
2124 if (!startPara
|| !endPara
)
2127 // Now we can add up the sizes
2128 for (node
= startPara
; node
; node
= node
->GetNext())
2130 // child is a paragraph
2131 wxRichTextObject
* child
= node
->GetData();
2132 const wxRichTextRange
& childRange
= child
->GetRange();
2133 wxRichTextRange rangeToFind
= range
;
2134 rangeToFind
.LimitTo(childRange
);
2136 if (child
->IsTopLevel())
2137 rangeToFind
= child
->GetOwnRange();
2141 int childDescent
= 0;
2142 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2144 descent
= wxMax(childDescent
, descent
);
2146 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2147 sz
.y
+= childSize
.y
;
2149 if (node
== endPara
)
2158 /// Get the paragraph at the given position
2159 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2164 // First find the first paragraph whose starting position is within the range.
2165 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2168 // child is a paragraph
2169 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2170 // wxASSERT (child != NULL);
2174 // Return first child in buffer if position is -1
2178 if (child
->GetRange().Contains(pos
))
2182 node
= node
->GetNext();
2187 /// Get the line at the given position
2188 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2197 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2198 if (obj
->GetRange().Contains(pos
))
2200 // child is a paragraph
2201 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2202 // wxASSERT (child != NULL);
2206 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2209 wxRichTextLine
* line
= node2
->GetData();
2211 wxRichTextRange range
= line
->GetAbsoluteRange();
2213 if (range
.Contains(pos
) ||
2215 // If the position is end-of-paragraph, then return the last line of
2216 // of the paragraph.
2217 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2220 node2
= node2
->GetNext();
2225 node
= node
->GetNext();
2228 int lineCount
= GetLineCount();
2230 return GetLineForVisibleLineNumber(lineCount
-1);
2235 /// Get the line at the given y pixel position, or the last line.
2236 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2238 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2241 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2242 // wxASSERT (child != NULL);
2246 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2249 wxRichTextLine
* line
= node2
->GetData();
2251 wxRect
rect(line
->GetRect());
2253 if (y
<= rect
.GetBottom())
2256 node2
= node2
->GetNext();
2260 node
= node
->GetNext();
2264 int lineCount
= GetLineCount();
2266 return GetLineForVisibleLineNumber(lineCount
-1);
2271 /// Get the number of visible lines
2272 int wxRichTextParagraphLayoutBox::GetLineCount() const
2276 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2279 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2280 // wxASSERT (child != NULL);
2283 count
+= child
->GetLines().GetCount();
2285 node
= node
->GetNext();
2291 /// Get the paragraph for a given line
2292 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2294 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2297 /// Get the line size at the given position
2298 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2300 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2303 return line
->GetSize();
2306 return wxSize(0, 0);
2310 /// Convenience function to add a paragraph of text
2311 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2313 // Don't use the base style, just the default style, and the base style will
2314 // be combined at display time.
2315 // Divide into paragraph and character styles.
2317 wxRichTextAttr defaultCharStyle
;
2318 wxRichTextAttr defaultParaStyle
;
2320 // If the default style is a named paragraph style, don't apply any character formatting
2321 // to the initial text string.
2322 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2324 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2326 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2329 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2331 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2332 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2334 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2340 return para
->GetRange();
2343 /// Adds multiple paragraphs, based on newlines.
2344 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2346 // Don't use the base style, just the default style, and the base style will
2347 // be combined at display time.
2348 // Divide into paragraph and character styles.
2350 wxRichTextAttr defaultCharStyle
;
2351 wxRichTextAttr defaultParaStyle
;
2353 // If the default style is a named paragraph style, don't apply any character formatting
2354 // to the initial text string.
2355 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2357 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2359 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2362 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2364 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2365 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2367 wxRichTextParagraph
* firstPara
= NULL
;
2368 wxRichTextParagraph
* lastPara
= NULL
;
2370 wxRichTextRange
range(-1, -1);
2373 size_t len
= text
.length();
2375 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2384 wxChar ch
= text
[i
];
2385 if (ch
== wxT('\n') || ch
== wxT('\r'))
2389 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2390 plainText
->SetText(line
);
2392 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2397 line
= wxEmptyString
;
2408 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2409 plainText
->SetText(line
);
2414 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2417 /// Convenience function to add an image
2418 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2420 // Don't use the base style, just the default style, and the base style will
2421 // be combined at display time.
2422 // Divide into paragraph and character styles.
2424 wxRichTextAttr defaultCharStyle
;
2425 wxRichTextAttr defaultParaStyle
;
2427 // If the default style is a named paragraph style, don't apply any character formatting
2428 // to the initial text string.
2429 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2431 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2433 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2436 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2438 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2439 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2441 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2443 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2447 return para
->GetRange();
2451 /// Insert fragment into this box at the given position. If partialParagraph is true,
2452 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2455 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2457 // First, find the first paragraph whose starting position is within the range.
2458 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2461 wxRichTextAttr originalAttr
= para
->GetAttributes();
2463 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2465 // Now split at this position, returning the object to insert the new
2466 // ones in front of.
2467 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2469 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2470 // text, for example, so let's optimize.
2472 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2474 // Add the first para to this para...
2475 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2479 // Iterate through the fragment paragraph inserting the content into this paragraph.
2480 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2481 wxASSERT (firstPara
!= NULL
);
2483 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2486 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2491 para
->AppendChild(newObj
);
2495 // Insert before nextObject
2496 para
->InsertChild(newObj
, nextObject
);
2499 objectNode
= objectNode
->GetNext();
2506 // Procedure for inserting a fragment consisting of a number of
2509 // 1. Remove and save the content that's after the insertion point, for adding
2510 // back once we've added the fragment.
2511 // 2. Add the content from the first fragment paragraph to the current
2513 // 3. Add remaining fragment paragraphs after the current paragraph.
2514 // 4. Add back the saved content from the first paragraph. If partialParagraph
2515 // is true, add it to the last paragraph added and not a new one.
2517 // 1. Remove and save objects after split point.
2518 wxList savedObjects
;
2520 para
->MoveToList(nextObject
, savedObjects
);
2522 // 2. Add the content from the 1st fragment paragraph.
2523 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2527 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2528 wxASSERT(firstPara
!= NULL
);
2530 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2531 para
->SetAttributes(firstPara
->GetAttributes());
2533 // Save empty paragraph attributes for appending later
2534 // These are character attributes deliberately set for a new paragraph. Without this,
2535 // we couldn't pass default attributes when appending a new paragraph.
2536 wxRichTextAttr emptyParagraphAttributes
;
2538 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2540 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2541 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2545 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2548 para
->AppendChild(newObj
);
2550 objectNode
= objectNode
->GetNext();
2553 // 3. Add remaining fragment paragraphs after the current paragraph.
2554 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2555 wxRichTextObject
* nextParagraph
= NULL
;
2556 if (nextParagraphNode
)
2557 nextParagraph
= nextParagraphNode
->GetData();
2559 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2560 wxRichTextParagraph
* finalPara
= para
;
2562 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2564 // If there was only one paragraph, we need to insert a new one.
2567 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2568 wxASSERT( para
!= NULL
);
2570 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2573 InsertChild(finalPara
, nextParagraph
);
2575 AppendChild(finalPara
);
2580 // If there was only one paragraph, or we have full paragraphs in our fragment,
2581 // we need to insert a new one.
2584 finalPara
= new wxRichTextParagraph
;
2587 InsertChild(finalPara
, nextParagraph
);
2589 AppendChild(finalPara
);
2592 // 4. Add back the remaining content.
2596 finalPara
->MoveFromList(savedObjects
);
2598 // Ensure there's at least one object
2599 if (finalPara
->GetChildCount() == 0)
2601 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2602 text
->SetAttributes(emptyParagraphAttributes
);
2604 finalPara
->AppendChild(text
);
2608 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2609 finalPara
->SetAttributes(firstPara
->GetAttributes());
2610 else if (finalPara
&& finalPara
!= para
)
2611 finalPara
->SetAttributes(originalAttr
);
2619 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2622 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2623 wxASSERT( para
!= NULL
);
2625 AppendChild(para
->Clone());
2634 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2635 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2636 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2638 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2641 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2642 wxASSERT( para
!= NULL
);
2644 if (!para
->GetRange().IsOutside(range
))
2646 fragment
.AppendChild(para
->Clone());
2651 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2652 if (!fragment
.IsEmpty())
2654 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2655 wxASSERT( firstPara
!= NULL
);
2657 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2658 wxASSERT( lastPara
!= NULL
);
2660 if (!firstPara
|| !lastPara
)
2663 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2665 long firstPos
= firstPara
->GetRange().GetStart();
2667 // Adjust for renumbering from zero
2668 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2671 fragment
.CalculateRange(0, end
);
2673 // Chop off the start of the paragraph
2674 if (topTailRange
.GetStart() > 0)
2676 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2677 firstPara
->DeleteRange(r
);
2679 // Make sure the numbering is correct
2680 fragment
.CalculateRange(0, end
);
2682 // Now, we've deleted some positions, so adjust the range
2684 topTailRange
.SetStart(range
.GetLength());
2685 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2689 topTailRange
.SetStart(range
.GetLength());
2690 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2693 if (topTailRange
.GetStart() < (lastPara
->GetRange().GetEnd()-1))
2695 lastPara
->DeleteRange(topTailRange
);
2697 // Make sure the numbering is correct
2699 fragment
.CalculateRange(0, end
);
2701 // We only have part of a paragraph at the end
2702 fragment
.SetPartialParagraph(true);
2706 // We have a partial paragraph (don't save last new paragraph marker)
2707 // or complete paragraph
2708 fragment
.SetPartialParagraph(isFragment
);
2715 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2716 /// starting from zero at the start of the buffer.
2717 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2724 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2727 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2728 // wxASSERT( child != NULL );
2732 if (child
->GetRange().Contains(pos
))
2734 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2737 wxRichTextLine
* line
= node2
->GetData();
2738 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2740 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2742 // If the caret is displayed at the end of the previous wrapped line,
2743 // we want to return the line it's _displayed_ at (not the actual line
2744 // containing the position).
2745 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2746 return lineCount
- 1;
2753 node2
= node2
->GetNext();
2755 // If we didn't find it in the lines, it must be
2756 // the last position of the paragraph. So return the last line.
2760 lineCount
+= child
->GetLines().GetCount();
2763 node
= node
->GetNext();
2770 /// Given a line number, get the corresponding wxRichTextLine object.
2771 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2775 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2778 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2779 // wxASSERT(child != NULL);
2783 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2785 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2788 wxRichTextLine
* line
= node2
->GetData();
2790 if (lineCount
== lineNumber
)
2795 node2
= node2
->GetNext();
2799 lineCount
+= child
->GetLines().GetCount();
2802 node
= node
->GetNext();
2809 /// Delete range from layout.
2810 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2812 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2814 wxRichTextParagraph
* firstPara
= NULL
;
2817 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2818 // wxASSERT (obj != NULL);
2820 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2824 // Delete the range in each paragraph
2826 if (!obj
->GetRange().IsOutside(range
))
2828 // Deletes the content of this object within the given range
2829 obj
->DeleteRange(range
);
2831 wxRichTextRange thisRange
= obj
->GetRange();
2832 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2834 // If the whole paragraph is within the range to delete,
2835 // delete the whole thing.
2836 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2838 // Delete the whole object
2839 RemoveChild(obj
, true);
2842 else if (!firstPara
)
2845 // If the range includes the paragraph end, we need to join this
2846 // and the next paragraph.
2847 if (range
.GetEnd() <= thisRange
.GetEnd())
2849 // We need to move the objects from the next paragraph
2850 // to this paragraph
2852 wxRichTextParagraph
* nextParagraph
= NULL
;
2853 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2854 nextParagraph
= obj
;
2857 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2859 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2862 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2864 wxRichTextAttr nextParaAttr
;
2865 if (applyFinalParagraphStyle
)
2867 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2868 // not the next one.
2869 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2870 nextParaAttr
= thisAttr
;
2872 nextParaAttr
= nextParagraph
->GetAttributes();
2875 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2877 // Move the objects to the previous para
2878 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2882 wxRichTextObject
* obj1
= node1
->GetData();
2884 firstPara
->AppendChild(obj1
);
2886 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2887 nextParagraph
->GetChildren().Erase(node1
);
2892 // Delete the paragraph
2893 RemoveChild(nextParagraph
, true);
2896 // Avoid empty paragraphs
2897 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2899 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2900 firstPara
->AppendChild(text
);
2903 if (applyFinalParagraphStyle
)
2904 firstPara
->SetAttributes(nextParaAttr
);
2917 /// Get any text in this object for the given range
2918 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2922 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2925 wxRichTextObject
* child
= node
->GetData();
2926 if (!child
->GetRange().IsOutside(range
))
2928 wxRichTextRange childRange
= range
;
2929 childRange
.LimitTo(child
->GetRange());
2931 wxString childText
= child
->GetTextForRange(childRange
);
2935 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2940 node
= node
->GetNext();
2946 /// Get all the text
2947 wxString
wxRichTextParagraphLayoutBox::GetText() const
2949 return GetTextForRange(GetOwnRange());
2952 /// Get the paragraph by number
2953 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2955 if ((size_t) paragraphNumber
>= GetChildCount())
2958 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2961 /// Get the length of the paragraph
2962 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2964 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2966 return para
->GetRange().GetLength() - 1; // don't include newline
2971 /// Get the text of the paragraph
2972 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2974 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2976 return para
->GetTextForRange(para
->GetRange());
2978 return wxEmptyString
;
2981 /// Convert zero-based line column and paragraph number to a position.
2982 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
2984 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
2987 return para
->GetRange().GetStart() + x
;
2993 /// Convert zero-based position to line column and paragraph number
2994 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
2996 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3000 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3003 wxRichTextObject
* child
= node
->GetData();
3007 node
= node
->GetNext();
3011 *x
= pos
- para
->GetRange().GetStart();
3019 /// Get the leaf object in a paragraph at this position.
3020 /// Given a line number, get the corresponding wxRichTextLine object.
3021 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3023 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3026 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3030 wxRichTextObject
* child
= node
->GetData();
3031 if (child
->GetRange().Contains(position
))
3034 node
= node
->GetNext();
3036 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3037 return para
->GetChildren().GetLast()->GetData();
3042 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3043 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3045 bool characterStyle
= false;
3046 bool paragraphStyle
= false;
3048 if (style
.IsCharacterStyle())
3049 characterStyle
= true;
3050 if (style
.IsParagraphStyle())
3051 paragraphStyle
= true;
3053 wxRichTextBuffer
* buffer
= GetBuffer();
3055 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3056 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3057 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3058 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3059 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3060 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3062 // Apply paragraph style first, if any
3063 wxRichTextAttr
wholeStyle(style
);
3065 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3067 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3069 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3072 // Limit the attributes to be set to the content to only character attributes.
3073 wxRichTextAttr
characterAttributes(wholeStyle
);
3074 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3076 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3078 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3080 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3083 // If we are associated with a control, make undoable; otherwise, apply immediately
3086 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3088 wxRichTextAction
* action
= NULL
;
3090 if (haveControl
&& withUndo
)
3092 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3093 action
->SetRange(range
);
3094 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3097 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3100 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3101 // wxASSERT (para != NULL);
3103 if (para
&& para
->GetChildCount() > 0)
3105 // Stop searching if we're beyond the range of interest
3106 if (para
->GetRange().GetStart() > range
.GetEnd())
3109 if (!para
->GetRange().IsOutside(range
))
3111 // We'll be using a copy of the paragraph to make style changes,
3112 // not updating the buffer directly.
3113 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3115 if (haveControl
&& withUndo
)
3117 newPara
= new wxRichTextParagraph(*para
);
3118 action
->GetNewParagraphs().AppendChild(newPara
);
3120 // Also store the old ones for Undo
3121 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3126 // If we're specifying paragraphs only, then we really mean character formatting
3127 // to be included in the paragraph style
3128 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3132 // Removes the given style from the paragraph
3133 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3135 else if (resetExistingStyle
)
3136 newPara
->GetAttributes() = wholeStyle
;
3141 // Only apply attributes that will make a difference to the combined
3142 // style as seen on the display
3143 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3144 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3147 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3151 // When applying paragraph styles dynamically, don't change the text objects' attributes
3152 // since they will computed as needed. Only apply the character styling if it's _only_
3153 // character styling. This policy is subject to change and might be put under user control.
3155 // Hm. we might well be applying a mix of paragraph and character styles, in which
3156 // case we _do_ want to apply character styles regardless of what para styles are set.
3157 // But if we're applying a paragraph style, which has some character attributes, but
3158 // we only want the paragraphs to hold this character style, then we _don't_ want to
3159 // apply the character style. So we need to be able to choose.
3161 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3163 wxRichTextRange
childRange(range
);
3164 childRange
.LimitTo(newPara
->GetRange());
3166 // Find the starting position and if necessary split it so
3167 // we can start applying a different style.
3168 // TODO: check that the style actually changes or is different
3169 // from style outside of range
3170 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3171 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3173 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3174 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3176 firstObject
= newPara
->SplitAt(range
.GetStart());
3178 // Increment by 1 because we're apply the style one _after_ the split point
3179 long splitPoint
= childRange
.GetEnd();
3180 if (splitPoint
!= newPara
->GetRange().GetEnd())
3184 if (splitPoint
== newPara
->GetRange().GetEnd())
3185 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3187 // lastObject is set as a side-effect of splitting. It's
3188 // returned as the object before the new object.
3189 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3191 wxASSERT(firstObject
!= NULL
);
3192 wxASSERT(lastObject
!= NULL
);
3194 if (!firstObject
|| !lastObject
)
3197 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3198 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3200 wxASSERT(firstNode
);
3203 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3207 wxRichTextObject
* child
= node2
->GetData();
3211 // Removes the given style from the paragraph
3212 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3214 else if (resetExistingStyle
)
3215 child
->GetAttributes() = characterAttributes
;
3220 // Only apply attributes that will make a difference to the combined
3221 // style as seen on the display
3222 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3223 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3226 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3229 if (node2
== lastNode
)
3232 node2
= node2
->GetNext();
3238 node
= node
->GetNext();
3241 // Do action, or delay it until end of batch.
3242 if (haveControl
&& withUndo
)
3243 buffer
->SubmitAction(action
);
3248 // Just change the attributes for this single object.
3249 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3251 wxRichTextBuffer
* buffer
= GetBuffer();
3252 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3253 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3254 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3256 wxRichTextAction
*action
= NULL
;
3257 wxRichTextAttr newAttr
= obj
->GetAttributes();
3258 if (resetExistingStyle
)
3261 newAttr
.Apply(textAttr
);
3263 if (haveControl
&& withUndo
)
3265 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3266 action
->SetRange(obj
->GetRange().FromInternal());
3267 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3268 action
->MakeObject(obj
);
3270 action
->GetAttributes() = newAttr
;
3273 obj
->GetAttributes() = newAttr
;
3275 if (haveControl
&& withUndo
)
3276 buffer
->SubmitAction(action
);
3279 /// Get the text attributes for this position.
3280 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3282 return DoGetStyle(position
, style
, true);
3285 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3287 return DoGetStyle(position
, style
, false);
3290 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3291 /// context attributes.
3292 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3294 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3296 if (style
.IsParagraphStyle())
3298 obj
= GetParagraphAtPosition(position
);
3303 // Start with the base style
3304 style
= GetAttributes();
3306 // Apply the paragraph style
3307 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3310 style
= obj
->GetAttributes();
3317 obj
= GetLeafObjectAtPosition(position
);
3322 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3323 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3326 style
= obj
->GetAttributes();
3334 static bool wxHasStyle(long flags
, long style
)
3336 return (flags
& style
) != 0;
3339 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3341 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3343 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3348 /// Get the combined style for a range - if any attribute is different within the range,
3349 /// that attribute is not present within the flags.
3350 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3352 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3354 style
= wxRichTextAttr();
3356 wxRichTextAttr clashingAttr
;
3357 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3359 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3362 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3363 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3365 if (para
->GetChildren().GetCount() == 0)
3367 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3369 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3373 wxRichTextRange
paraRange(para
->GetRange());
3374 paraRange
.LimitTo(range
);
3376 // First collect paragraph attributes only
3377 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3378 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3379 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3381 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3385 wxRichTextObject
* child
= childNode
->GetData();
3386 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3388 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3390 // Now collect character attributes only
3391 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3393 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3396 childNode
= childNode
->GetNext();
3400 node
= node
->GetNext();
3405 /// Set default style
3406 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3408 m_defaultAttributes
= style
;
3412 /// Test if this whole range has character attributes of the specified kind. If any
3413 /// of the attributes are different within the range, the test fails. You
3414 /// can use this to implement, for example, bold button updating. style must have
3415 /// flags indicating which attributes are of interest.
3416 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3419 int matchingCount
= 0;
3421 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3424 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3425 // wxASSERT (para != NULL);
3429 // Stop searching if we're beyond the range of interest
3430 if (para
->GetRange().GetStart() > range
.GetEnd())
3431 return foundCount
== matchingCount
&& foundCount
!= 0;
3433 if (!para
->GetRange().IsOutside(range
))
3435 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3439 wxRichTextObject
* child
= node2
->GetData();
3440 // Allow for empty string if no buffer
3441 wxRichTextRange childRange
= child
->GetRange();
3442 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3443 childRange
.SetEnd(childRange
.GetEnd()+1);
3445 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3448 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3450 if (wxTextAttrEqPartial(textAttr
, style
))
3454 node2
= node2
->GetNext();
3459 node
= node
->GetNext();
3462 return foundCount
== matchingCount
&& foundCount
!= 0;
3465 /// Test if this whole range has paragraph attributes of the specified kind. If any
3466 /// of the attributes are different within the range, the test fails. You
3467 /// can use this to implement, for example, centering button updating. style must have
3468 /// flags indicating which attributes are of interest.
3469 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3472 int matchingCount
= 0;
3474 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3477 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3478 // wxASSERT (para != NULL);
3482 // Stop searching if we're beyond the range of interest
3483 if (para
->GetRange().GetStart() > range
.GetEnd())
3484 return foundCount
== matchingCount
&& foundCount
!= 0;
3486 if (!para
->GetRange().IsOutside(range
))
3488 wxRichTextAttr textAttr
= GetAttributes();
3489 // Apply the paragraph style
3490 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3493 if (wxTextAttrEqPartial(textAttr
, style
))
3498 node
= node
->GetNext();
3500 return foundCount
== matchingCount
&& foundCount
!= 0;
3503 void wxRichTextParagraphLayoutBox::Reset()
3507 wxRichTextBuffer
* buffer
= GetBuffer();
3508 if (buffer
&& buffer
->GetRichTextCtrl())
3510 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3511 event
.SetEventObject(buffer
->GetRichTextCtrl());
3512 event
.SetContainer(this);
3514 buffer
->SendEvent(event
, true);
3517 AddParagraph(wxEmptyString
);
3519 InvalidateHierarchy(wxRICHTEXT_ALL
);
3522 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3523 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3525 wxRichTextCompositeObject::Invalidate(invalidRange
);
3527 DoInvalidate(invalidRange
);
3530 // Do the (in)validation for this object only
3531 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3533 if (invalidRange
== wxRICHTEXT_ALL
)
3535 m_invalidRange
= wxRICHTEXT_ALL
;
3537 // Already invalidating everything
3538 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3543 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3544 m_invalidRange
.SetStart(invalidRange
.GetStart());
3545 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3546 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3550 // Do the (in)validation both up and down the hierarchy
3551 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3553 Invalidate(invalidRange
);
3555 if (invalidRange
!= wxRICHTEXT_NONE
)
3557 // Now go up the hierarchy
3558 wxRichTextObject
* thisObj
= this;
3559 wxRichTextObject
* p
= GetParent();
3562 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3564 l
->DoInvalidate(thisObj
->GetRange());
3572 /// Get invalid range, rounding to entire paragraphs if argument is true.
3573 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3575 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3576 return m_invalidRange
;
3578 wxRichTextRange range
= m_invalidRange
;
3580 if (wholeParagraphs
)
3582 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3584 range
.SetStart(para1
->GetRange().GetStart());
3585 // floating layout make all child should be relayout
3586 range
.SetEnd(GetOwnRange().GetEnd());
3591 /// Apply the style sheet to the buffer, for example if the styles have changed.
3592 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3594 wxASSERT(styleSheet
!= NULL
);
3600 wxRichTextAttr
attr(GetBasicStyle());
3601 if (GetBasicStyle().HasParagraphStyleName())
3603 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3606 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3607 SetBasicStyle(attr
);
3612 if (GetBasicStyle().HasCharacterStyleName())
3614 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3617 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3618 SetBasicStyle(attr
);
3623 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3626 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3627 // wxASSERT (para != NULL);
3631 // Combine paragraph and list styles. If there is a list style in the original attributes,
3632 // the current indentation overrides anything else and is used to find the item indentation.
3633 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3634 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3635 // exception as above).
3636 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3637 // So when changing a list style interactively, could retrieve level based on current style, then
3638 // set appropriate indent and apply new style.
3642 if (para
->GetAttributes().HasOutlineLevel())
3643 outline
= para
->GetAttributes().GetOutlineLevel();
3644 if (para
->GetAttributes().HasBulletNumber())
3645 num
= para
->GetAttributes().GetBulletNumber();
3647 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3649 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3651 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3652 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3653 if (paraDef
&& !listDef
)
3655 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3658 else if (listDef
&& !paraDef
)
3660 // Set overall style defined for the list style definition
3661 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3663 // Apply the style for this level
3664 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3667 else if (listDef
&& paraDef
)
3669 // Combines overall list style, style for level, and paragraph style
3670 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3674 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3676 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3678 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3680 // Overall list definition style
3681 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3683 // Style for this level
3684 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3688 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3690 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3693 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3699 para
->GetAttributes().SetOutlineLevel(outline
);
3701 para
->GetAttributes().SetBulletNumber(num
);
3704 node
= node
->GetNext();
3706 return foundCount
!= 0;
3710 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3712 wxRichTextBuffer
* buffer
= GetBuffer();
3713 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3715 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3716 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3717 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3718 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3720 // Current number, if numbering
3723 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3725 // If we are associated with a control, make undoable; otherwise, apply immediately
3728 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3730 wxRichTextAction
* action
= NULL
;
3732 if (haveControl
&& withUndo
)
3734 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3735 action
->SetRange(range
);
3736 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3739 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3742 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3743 // wxASSERT (para != NULL);
3745 if (para
&& para
->GetChildCount() > 0)
3747 // Stop searching if we're beyond the range of interest
3748 if (para
->GetRange().GetStart() > range
.GetEnd())
3751 if (!para
->GetRange().IsOutside(range
))
3753 // We'll be using a copy of the paragraph to make style changes,
3754 // not updating the buffer directly.
3755 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3757 if (haveControl
&& withUndo
)
3759 newPara
= new wxRichTextParagraph(*para
);
3760 action
->GetNewParagraphs().AppendChild(newPara
);
3762 // Also store the old ones for Undo
3763 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3770 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3771 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3773 // How is numbering going to work?
3774 // If we are renumbering, or numbering for the first time, we need to keep
3775 // track of the number for each level. But we might be simply applying a different
3777 // In Word, applying a style to several paragraphs, even if at different levels,
3778 // reverts the level back to the same one. So we could do the same here.
3779 // Renumbering will need to be done when we promote/demote a paragraph.
3781 // Apply the overall list style, and item style for this level
3782 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3783 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3785 // Now we need to do numbering
3788 newPara
->GetAttributes().SetBulletNumber(n
);
3793 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3795 // if def is NULL, remove list style, applying any associated paragraph style
3796 // to restore the attributes
3798 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3799 newPara
->GetAttributes().SetLeftIndent(0, 0);
3800 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3802 // Eliminate the main list-related attributes
3803 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
);
3805 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3807 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3810 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3817 node
= node
->GetNext();
3820 // Do action, or delay it until end of batch.
3821 if (haveControl
&& withUndo
)
3822 buffer
->SubmitAction(action
);
3827 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
3829 wxRichTextBuffer
* buffer
= GetBuffer();
3830 if (buffer
&& buffer
->GetStyleSheet())
3832 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
3834 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
3839 /// Clear list for given range
3840 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
3842 return SetListStyle(range
, NULL
, flags
);
3845 /// Number/renumber any list elements in the given range
3846 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3848 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
3851 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3852 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
3853 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3855 wxRichTextBuffer
* buffer
= GetBuffer();
3856 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3858 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3859 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3861 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3864 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3866 // Max number of levels
3867 const int maxLevels
= 10;
3869 // The level we're looking at now
3870 int currentLevel
= -1;
3872 // The item number for each level
3873 int levels
[maxLevels
];
3876 // Reset all numbering
3877 for (i
= 0; i
< maxLevels
; i
++)
3879 if (startFrom
!= -1)
3880 levels
[i
] = startFrom
-1;
3881 else if (renumber
) // start again
3884 levels
[i
] = -1; // start from the number we found, if any
3887 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3889 // If we are associated with a control, make undoable; otherwise, apply immediately
3892 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3894 wxRichTextAction
* action
= NULL
;
3896 if (haveControl
&& withUndo
)
3898 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3899 action
->SetRange(range
);
3900 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3903 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3906 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3907 // wxASSERT (para != NULL);
3909 if (para
&& para
->GetChildCount() > 0)
3911 // Stop searching if we're beyond the range of interest
3912 if (para
->GetRange().GetStart() > range
.GetEnd())
3915 if (!para
->GetRange().IsOutside(range
))
3917 // We'll be using a copy of the paragraph to make style changes,
3918 // not updating the buffer directly.
3919 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3921 if (haveControl
&& withUndo
)
3923 newPara
= new wxRichTextParagraph(*para
);
3924 action
->GetNewParagraphs().AppendChild(newPara
);
3926 // Also store the old ones for Undo
3927 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3932 wxRichTextListStyleDefinition
* defToUse
= def
;
3935 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
3936 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
3941 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3942 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
3944 // If we've specified a level to apply to all, change the level.
3945 if (specifiedLevel
!= -1)
3946 thisLevel
= specifiedLevel
;
3948 // Do promotion if specified
3949 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
3951 thisLevel
= thisLevel
- promoteBy
;
3958 // Apply the overall list style, and item style for this level
3959 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3960 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3962 // OK, we've (re)applied the style, now let's get the numbering right.
3964 if (currentLevel
== -1)
3965 currentLevel
= thisLevel
;
3967 // Same level as before, do nothing except increment level's number afterwards
3968 if (currentLevel
== thisLevel
)
3971 // A deeper level: start renumbering all levels after current level
3972 else if (thisLevel
> currentLevel
)
3974 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
3978 currentLevel
= thisLevel
;
3980 else if (thisLevel
< currentLevel
)
3982 currentLevel
= thisLevel
;
3985 // Use the current numbering if -1 and we have a bullet number already
3986 if (levels
[currentLevel
] == -1)
3988 if (newPara
->GetAttributes().HasBulletNumber())
3989 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
3991 levels
[currentLevel
] = 1;
3995 levels
[currentLevel
] ++;
3998 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4000 // Create the bullet text if an outline list
4001 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4004 for (i
= 0; i
<= currentLevel
; i
++)
4006 if (!text
.IsEmpty())
4008 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4010 newPara
->GetAttributes().SetBulletText(text
);
4016 node
= node
->GetNext();
4019 // Do action, or delay it until end of batch.
4020 if (haveControl
&& withUndo
)
4021 buffer
->SubmitAction(action
);
4026 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4028 wxRichTextBuffer
* buffer
= GetBuffer();
4029 if (buffer
->GetStyleSheet())
4031 wxRichTextListStyleDefinition
* def
= NULL
;
4032 if (!defName
.IsEmpty())
4033 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4034 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4039 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4040 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4043 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4044 // to NumberList with a flag indicating promotion is required within one of the ranges.
4045 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4046 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4047 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4048 // list position will start from 1.
4049 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4050 // We can end the renumbering at this point.
4052 // For now, only renumber within the promotion range.
4054 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4057 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4059 wxRichTextBuffer
* buffer
= GetBuffer();
4060 if (buffer
->GetStyleSheet())
4062 wxRichTextListStyleDefinition
* def
= NULL
;
4063 if (!defName
.IsEmpty())
4064 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4065 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4070 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4071 /// position of the paragraph that it had to start looking from.
4072 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4074 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4077 wxRichTextBuffer
* buffer
= GetBuffer();
4078 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4079 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4081 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4084 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4085 // int thisLevel = def->FindLevelForIndent(thisIndent);
4087 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4089 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4090 if (previousParagraph
->GetAttributes().HasBulletName())
4091 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4092 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4093 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4095 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4096 attr
.SetBulletNumber(nextNumber
);
4100 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4101 if (!text
.IsEmpty())
4103 int pos
= text
.Find(wxT('.'), true);
4104 if (pos
!= wxNOT_FOUND
)
4106 text
= text
.Mid(0, text
.Length() - pos
- 1);
4109 text
= wxEmptyString
;
4110 if (!text
.IsEmpty())
4112 text
+= wxString::Format(wxT("%d"), nextNumber
);
4113 attr
.SetBulletText(text
);
4127 * wxRichTextParagraph
4128 * This object represents a single paragraph (or in a straight text editor, a line).
4131 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4133 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4135 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4136 wxRichTextCompositeObject(parent
)
4139 SetAttributes(*style
);
4142 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4143 wxRichTextCompositeObject(parent
)
4146 SetAttributes(*paraStyle
);
4148 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4151 wxRichTextParagraph::~wxRichTextParagraph()
4157 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4162 // Currently we don't merge these attributes with the parent, but we
4163 // should consider whether we should (e.g. if we set a border colour
4164 // for all paragraphs). But generally box attributes are likely to be
4165 // different for different objects.
4166 wxRect paraRect
= GetRect();
4167 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4169 wxRichTextAttr attr
= GetCombinedAttributes();
4171 // Draw the bullet, if any
4172 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4174 if (attr
.GetLeftSubIndent() != 0)
4176 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4177 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4179 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4181 // Combine with the font of the first piece of content, if one is specified
4182 if (GetChildren().GetCount() > 0)
4184 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4185 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4187 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4191 // Get line height from first line, if any
4192 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4195 int lineHeight
wxDUMMY_INITIALIZE(0);
4198 lineHeight
= line
->GetSize().y
;
4199 linePos
= line
->GetPosition() + GetPosition();
4204 if (bulletAttr
.HasFont() && GetBuffer())
4205 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4207 font
= (*wxNORMAL_FONT
);
4209 wxCheckSetFont(dc
, font
);
4211 lineHeight
= dc
.GetCharHeight();
4212 linePos
= GetPosition();
4213 linePos
.y
+= spaceBeforePara
;
4216 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4218 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4220 if (wxRichTextBuffer::GetRenderer())
4221 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4223 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4225 if (wxRichTextBuffer::GetRenderer())
4226 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4230 wxString bulletText
= GetBulletText();
4232 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4233 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4238 // Draw the range for each line, one object at a time.
4240 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4243 wxRichTextLine
* line
= node
->GetData();
4244 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4246 // Lines are specified relative to the paragraph
4248 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4250 // Don't draw if off the screen
4251 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4253 wxPoint objectPosition
= linePosition
;
4254 int maxDescent
= line
->GetDescent();
4256 // Loop through objects until we get to the one within range
4257 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4262 wxRichTextObject
* child
= node2
->GetData();
4264 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4266 // Draw this part of the line at the correct position
4267 wxRichTextRange
objectRange(child
->GetRange());
4268 objectRange
.LimitTo(lineRange
);
4271 if (child
->IsTopLevel())
4273 objectSize
= child
->GetCachedSize();
4274 objectRange
= child
->GetOwnRange();
4278 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4279 if (i
< (int) line
->GetObjectSizes().GetCount())
4281 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4287 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4291 // Use the child object's width, but the whole line's height
4292 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4293 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4295 objectPosition
.x
+= objectSize
.x
;
4298 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4299 // Can break out of inner loop now since we've passed this line's range
4302 node2
= node2
->GetNext();
4306 node
= node
->GetNext();
4312 // Get the range width using partial extents calculated for the whole paragraph.
4313 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4315 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4317 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4320 int leftMostPos
= 0;
4321 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4322 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4324 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4326 int w
= rightMostPos
- leftMostPos
;
4331 /// Lay the item out
4332 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
4334 // Deal with floating objects firstly before the normal layout
4335 wxRichTextBuffer
* buffer
= GetBuffer();
4337 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4338 wxASSERT(collector
);
4339 LayoutFloat(dc
, rect
, style
, collector
);
4341 wxRichTextAttr attr
= GetCombinedAttributes();
4345 // Increase the size of the paragraph due to spacing
4346 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4347 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4348 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4349 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4350 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4352 int lineSpacing
= 0;
4354 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4355 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4357 wxCheckSetFont(dc
, attr
.GetFont());
4358 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4361 // Start position for each line relative to the paragraph
4362 int startPositionFirstLine
= leftIndent
;
4363 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4365 // If we have a bullet in this paragraph, the start position for the first line's text
4366 // is actually leftIndent + leftSubIndent.
4367 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4368 startPositionFirstLine
= startPositionSubsequentLines
;
4370 long lastEndPos
= GetRange().GetStart()-1;
4371 long lastCompletedEndPos
= lastEndPos
;
4373 int currentWidth
= 0;
4374 SetPosition(rect
.GetPosition());
4376 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4379 int maxHeight
= currentPosition
.y
;
4384 int lineDescent
= 0;
4386 wxRichTextObjectList::compatibility_iterator node
;
4388 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4390 wxArrayInt partialExtents
;
4393 int paraDescent
= 0;
4395 // This calculates the partial text extents
4396 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4398 node
= m_children
.GetFirst();
4401 wxRichTextObject
* child
= node
->GetData();
4403 //child->SetCachedSize(wxDefaultSize);
4404 child
->Layout(dc
, rect
, style
);
4406 node
= node
->GetNext();
4413 // We may need to go back to a previous child, in which case create the new line,
4414 // find the child corresponding to the start position of the string, and
4417 wxRect availableRect
;
4419 node
= m_children
.GetFirst();
4422 wxRichTextObject
* child
= node
->GetData();
4424 // If floating, ignore. We already laid out floats.
4425 // Also ignore if empty object, except if we haven't got any
4427 if (child
->IsFloating() || !child
->IsShown() ||
4428 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4431 node
= node
->GetNext();
4435 // If this is e.g. a composite text box, it will need to be laid out itself.
4436 // But if just a text fragment or image, for example, this will
4437 // do nothing. NB: won't we need to set the position after layout?
4438 // since for example if position is dependent on vertical line size, we
4439 // can't tell the position until the size is determined. So possibly introduce
4440 // another layout phase.
4442 // We may only be looking at part of a child, if we searched back for wrapping
4443 // and found a suitable point some way into the child. So get the size for the fragment
4446 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4447 long lastPosToUse
= child
->GetRange().GetEnd();
4448 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4450 if (lineBreakInThisObject
)
4451 lastPosToUse
= nextBreakPos
;
4454 int childDescent
= 0;
4456 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4457 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4458 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4460 if (child
->IsTopLevel())
4462 wxSize oldSize
= child
->GetCachedSize();
4464 child
->Invalidate(wxRICHTEXT_ALL
);
4465 child
->SetPosition(wxPoint(0, 0));
4467 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4468 // lays out the object again using the minimum size
4469 // The position will be determined by its location in its line,
4470 // and not by the child's actual position.
4471 child
->LayoutToBestSize(dc
, buffer
,
4472 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4474 if (oldSize
!= child
->GetCachedSize())
4476 partialExtents
.Clear();
4478 // Recalculate the partial text extents since the child object changed size
4479 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4483 // Problem: we need to layout composites here for which we need the available width,
4484 // but we can't get the available width without using the float collector which
4485 // needs to know the object height.
4487 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4489 childSize
= child
->GetCachedSize();
4490 childDescent
= child
->GetDescent();
4494 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4495 // Get height only, then the width using the partial extents
4496 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4497 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4499 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4504 int loopIterations
= 0;
4506 // If there are nested objects that need to lay themselves out, we have to do this in a
4507 // loop because the height of the object may well depend on the available width.
4508 // And because of floating object positioning, the available width depends on the
4509 // height of the object and whether it will clash with the floating objects.
4510 // So, we see whether the available width changes due to the presence of floating images.
4511 // If it does, then we'll use the new restricted width to find the object height again.
4512 // If this causes another restriction in the available width, we'll try again, until
4513 // either we lose patience or the available width settles down.
4518 wxRect oldAvailableRect
= availableRect
;
4520 // Available width depends on the floating objects and the line height.
4521 // Note: the floating objects may be placed vertically along the two side of
4522 // buffer, so we may have different available line widths with different
4523 // [startY, endY]. So, we can't determine how wide the available
4524 // space is until we know the exact line height.
4525 lineDescent
= wxMax(childDescent
, maxDescent
);
4526 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4527 lineHeight
= lineDescent
+ lineAscent
;
4528 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4530 // Adjust availableRect to the space that is available when taking floating objects into account.
4532 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4534 int newX
= floatAvailableRect
.x
+ startOffset
;
4535 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4536 availableRect
.x
= newX
;
4537 availableRect
.width
= newW
;
4540 if (floatAvailableRect
.width
< availableRect
.width
)
4541 availableRect
.width
= floatAvailableRect
.width
;
4543 currentPosition
.x
= availableRect
.x
- rect
.x
;
4545 if (child
->IsTopLevel() && loopIterations
<= 20)
4547 if (availableRect
!= oldAvailableRect
)
4549 wxSize oldSize
= child
->GetCachedSize();
4551 //child->SetCachedSize(wxDefaultSize);
4552 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4553 // lays out the object again using the minimum size
4554 child
->Invalidate(wxRICHTEXT_ALL
);
4555 child
->LayoutToBestSize(dc
, buffer
,
4556 GetAttributes(), child
->GetAttributes(), availableRect
, style
);
4557 childSize
= child
->GetCachedSize();
4558 childDescent
= child
->GetDescent();
4559 //child->SetPosition(availableRect.GetPosition());
4561 if (oldSize
!= child
->GetCachedSize())
4563 partialExtents
.Clear();
4565 // Recalculate the partial text extents since the child object changed size
4566 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4569 // Go around the loop finding the available rect for the given floating objects
4580 // 1) There was a line break BEFORE the natural break
4581 // 2) There was a line break AFTER the natural break
4582 // 3) It's the last line
4583 // 4) The child still fits (carry on) - 'else' clause
4585 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4587 (childSize
.x
+ currentWidth
> availableRect
.width
)
4589 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4593 if (child
->IsTopLevel())
4595 // We can move it to the correct position at this point
4596 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4599 long wrapPosition
= 0;
4600 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4601 wrapPosition
= child
->GetRange().GetEnd();
4604 // Find a place to wrap. This may walk back to previous children,
4605 // for example if a word spans several objects.
4606 // Note: one object must contains only one wxTextAtrr, so the line height will not
4607 // change inside one object. Thus, we can pass the remain line width to the
4608 // FindWrapPosition function.
4609 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4611 // If the function failed, just cut it off at the end of this child.
4612 wrapPosition
= child
->GetRange().GetEnd();
4615 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4616 if (wrapPosition
<= lastCompletedEndPos
)
4617 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4619 // Line end position shouldn't be the same as the end, or greater.
4620 if (wrapPosition
>= GetRange().GetEnd())
4621 wrapPosition
= GetRange().GetEnd()-1;
4623 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4625 // Let's find the actual size of the current line now
4627 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4629 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4630 /// for the fragment we're about to add.
4631 childDescent
= maxDescent
;
4633 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4634 if (!child
->IsEmpty())
4636 // Get height only, then the width using the partial extents
4637 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4638 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4642 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4644 currentWidth
= actualSize
.x
;
4645 maxDescent
= wxMax(childDescent
, maxDescent
);
4646 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4647 lineHeight
= maxDescent
+ maxAscent
;
4649 if (lineHeight
== 0 && buffer
)
4651 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4652 wxCheckSetFont(dc
, font
);
4653 lineHeight
= dc
.GetCharHeight();
4656 if (maxDescent
== 0)
4659 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4663 wxRichTextLine
* line
= AllocateLine(lineCount
);
4665 // Set relative range so we won't have to change line ranges when paragraphs are moved
4666 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4667 line
->SetPosition(currentPosition
);
4668 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4669 line
->SetDescent(maxDescent
);
4671 maxHeight
= currentPosition
.y
+ lineHeight
;
4673 // Now move down a line. TODO: add margins, spacing
4674 currentPosition
.y
+= lineHeight
;
4675 currentPosition
.y
+= lineSpacing
;
4678 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4683 // TODO: account for zero-length objects, such as fields
4684 // wxASSERT(wrapPosition > lastCompletedEndPos);
4686 lastEndPos
= wrapPosition
;
4687 lastCompletedEndPos
= lastEndPos
;
4691 if (wrapPosition
< GetRange().GetEnd()-1)
4693 // May need to set the node back to a previous one, due to searching back in wrapping
4694 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4695 if (childAfterWrapPosition
)
4696 node
= m_children
.Find(childAfterWrapPosition
);
4698 node
= node
->GetNext();
4701 node
= node
->GetNext();
4703 // Apply paragraph styles such as alignment to the wrapped line
4704 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4708 // We still fit, so don't add a line, and keep going
4709 currentWidth
+= childSize
.x
;
4710 maxDescent
= wxMax(childDescent
, maxDescent
);
4711 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4712 lineHeight
= maxDescent
+ maxAscent
;
4714 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4715 lastEndPos
= child
->GetRange().GetEnd();
4717 node
= node
->GetNext();
4721 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4723 // Remove remaining unused line objects, if any
4724 ClearUnusedLines(lineCount
);
4726 // We need to add back the margins etc.
4728 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4729 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4730 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4731 SetCachedSize(marginRect
.GetSize());
4734 // The maximum size is the length of the paragraph stretched out into a line.
4735 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4736 // this size. TODO: take into account line breaks.
4738 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4739 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4740 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4741 SetMaxSize(marginRect
.GetSize());
4744 // Find the greatest minimum size. Currently we only look at non-text objects,
4745 // which isn't ideal but it would be slow to find the maximum word width to
4746 // use as the minimum.
4749 node
= m_children
.GetFirst();
4752 wxRichTextObject
* child
= node
->GetData();
4754 // If floating, ignore. We already laid out floats.
4755 // Also ignore if empty object, except if we haven't got any
4757 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4759 if (child
->GetCachedSize().x
> minWidth
)
4760 minWidth
= child
->GetMinSize().x
;
4762 node
= node
->GetNext();
4765 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4766 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4767 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4768 SetMinSize(marginRect
.GetSize());
4772 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4773 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4774 // Use the text extents to calculate the size of each fragment in each line
4775 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4778 wxRichTextLine
* line
= lineNode
->GetData();
4779 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4781 // Loop through objects until we get to the one within range
4782 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4786 wxRichTextObject
* child
= node2
->GetData();
4788 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4790 wxRichTextRange rangeToUse
= lineRange
;
4791 rangeToUse
.LimitTo(child
->GetRange());
4793 // Find the size of the child from the text extents, and store in an array
4794 // for drawing later
4796 if (rangeToUse
.GetStart() > GetRange().GetStart())
4797 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4798 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4799 int sz
= right
- left
;
4800 line
->GetObjectSizes().Add(sz
);
4802 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4803 // Can break out of inner loop now since we've passed this line's range
4806 node2
= node2
->GetNext();
4809 lineNode
= lineNode
->GetNext();
4817 /// Apply paragraph styles, such as centering, to wrapped lines
4818 /// TODO: take into account box attributes, possibly
4819 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4821 if (!attr
.HasAlignment())
4824 wxPoint pos
= line
->GetPosition();
4825 wxSize size
= line
->GetSize();
4827 // centering, right-justification
4828 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
4830 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4831 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
4832 line
->SetPosition(pos
);
4834 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
4836 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4837 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
4838 line
->SetPosition(pos
);
4842 /// Insert text at the given position
4843 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
4845 wxRichTextObject
* childToUse
= NULL
;
4846 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
4848 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4851 wxRichTextObject
* child
= node
->GetData();
4852 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
4859 node
= node
->GetNext();
4864 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
4867 int posInString
= pos
- textObject
->GetRange().GetStart();
4869 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
4870 text
+ textObject
->GetText().Mid(posInString
);
4871 textObject
->SetText(newText
);
4873 int textLength
= text
.length();
4875 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
4876 textObject
->GetRange().GetEnd() + textLength
));
4878 // Increment the end range of subsequent fragments in this paragraph.
4879 // We'll set the paragraph range itself at a higher level.
4881 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
4884 wxRichTextObject
* child
= node
->GetData();
4885 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
4886 textObject
->GetRange().GetEnd() + textLength
));
4888 node
= node
->GetNext();
4895 // TODO: if not a text object, insert at closest position, e.g. in front of it
4901 // Don't pass parent initially to suppress auto-setting of parent range.
4902 // We'll do that at a higher level.
4903 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
4905 AppendChild(textObject
);
4912 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
4914 wxRichTextCompositeObject::Copy(obj
);
4917 /// Clear the cached lines
4918 void wxRichTextParagraph::ClearLines()
4920 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
4923 /// Get/set the object size for the given range. Returns false if the range
4924 /// is invalid for this object.
4925 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
4927 if (!range
.IsWithin(GetRange()))
4930 if (flags
& wxRICHTEXT_UNFORMATTED
)
4932 // Just use unformatted data, assume no line breaks
4933 // TODO: take into account line breaks
4937 wxArrayInt childExtents
;
4944 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4948 wxRichTextObject
* child
= node
->GetData();
4949 if (!child
->GetRange().IsOutside(range
))
4951 // Floating objects have a zero size within the paragraph.
4952 if (child
->IsFloating())
4957 if (partialExtents
->GetCount() > 0)
4958 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
4962 partialExtents
->Add(0 /* zero size */ + lastSize
);
4969 wxRichTextRange rangeToUse
= range
;
4970 rangeToUse
.LimitTo(child
->GetRange());
4972 if (child
->IsTopLevel())
4973 rangeToUse
= child
->GetOwnRange();
4975 int childDescent
= 0;
4977 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4978 // but it's only going to be used after caching has taken place.
4979 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
4981 childDescent
= child
->GetDescent();
4982 childSize
= child
->GetCachedSize();
4984 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4985 sz
.x
+= childSize
.x
;
4986 descent
= wxMax(descent
, childDescent
);
4988 else if (child
->IsTopLevel())
4990 childDescent
= child
->GetDescent();
4991 childSize
= child
->GetCachedSize();
4993 sz
.y
= wxMax(sz
.y
, childSize
.y
);
4994 sz
.x
+= childSize
.x
;
4995 descent
= wxMax(descent
, childDescent
);
4996 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
4998 child
->SetCachedSize(childSize
);
4999 child
->SetDescent(childDescent
);
5005 if (partialExtents
->GetCount() > 0)
5006 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5010 partialExtents
->Add(childSize
.x
+ lastSize
);
5013 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5015 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5016 sz
.x
+= childSize
.x
;
5017 descent
= wxMax(descent
, childDescent
);
5019 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5021 child
->SetCachedSize(childSize
);
5022 child
->SetDescent(childDescent
);
5028 if (partialExtents
->GetCount() > 0)
5029 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5034 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5036 partialExtents
->Add(childExtents
[i
] + lastSize
);
5046 node
= node
->GetNext();
5052 // Use formatted data, with line breaks
5055 // We're going to loop through each line, and then for each line,
5056 // call GetRangeSize for the fragment that comprises that line.
5057 // Only we have to do that multiple times within the line, because
5058 // the line may be broken into pieces. For now ignore line break commands
5059 // (so we can assume that getting the unformatted size for a fragment
5060 // within a line is the actual size)
5062 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5065 wxRichTextLine
* line
= node
->GetData();
5066 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5067 if (!lineRange
.IsOutside(range
))
5071 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5074 wxRichTextObject
* child
= node2
->GetData();
5076 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5078 wxRichTextRange rangeToUse
= lineRange
;
5079 rangeToUse
.LimitTo(child
->GetRange());
5080 if (child
->IsTopLevel())
5081 rangeToUse
= child
->GetOwnRange();
5084 int childDescent
= 0;
5085 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5087 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5088 lineSize
.x
+= childSize
.x
;
5090 descent
= wxMax(descent
, childDescent
);
5093 node2
= node2
->GetNext();
5096 // Increase size by a line (TODO: paragraph spacing)
5098 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5100 node
= node
->GetNext();
5107 /// Finds the absolute position and row height for the given character position
5108 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5112 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5114 *height
= line
->GetSize().y
;
5116 *height
= dc
.GetCharHeight();
5118 // -1 means 'the start of the buffer'.
5121 pt
= pt
+ line
->GetPosition();
5126 // The final position in a paragraph is taken to mean the position
5127 // at the start of the next paragraph.
5128 if (index
== GetRange().GetEnd())
5130 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5131 wxASSERT( parent
!= NULL
);
5133 // Find the height at the next paragraph, if any
5134 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5137 *height
= line
->GetSize().y
;
5138 pt
= line
->GetAbsolutePosition();
5142 *height
= dc
.GetCharHeight();
5143 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5144 pt
= wxPoint(indent
, GetCachedSize().y
);
5150 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5153 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5156 wxRichTextLine
* line
= node
->GetData();
5157 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5158 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5160 // If this is the last point in the line, and we're forcing the
5161 // returned value to be the start of the next line, do the required
5163 if (index
== lineRange
.GetEnd() && forceLineStart
)
5165 if (node
->GetNext())
5167 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5168 *height
= nextLine
->GetSize().y
;
5169 pt
= nextLine
->GetAbsolutePosition();
5174 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5176 wxRichTextRange
r(lineRange
.GetStart(), index
);
5180 // We find the size of the line up to this point,
5181 // then we can add this size to the line start position and
5182 // paragraph start position to find the actual position.
5184 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5186 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5187 *height
= line
->GetSize().y
;
5194 node
= node
->GetNext();
5200 /// Hit-testing: returns a flag indicating hit test details, plus
5201 /// information about position
5202 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5205 return wxRICHTEXT_HITTEST_NONE
;
5207 // If we're in the top-level container, then we can return
5208 // a suitable hit test code even if the point is outside the container area,
5209 // so that we can position the caret sensibly even if we don't
5210 // click on valid content. If we're not at the top-level, and the point
5211 // is not within this paragraph object, then we don't want to stop more
5212 // precise hit-testing from working prematurely, so return immediately.
5213 // NEW STRATEGY: use the parent boundary to test whether we're in the
5214 // right region, not the paragraph, since the paragraph may be positioned
5215 // some way in from where the user clicks.
5218 wxRichTextObject
* tempObj
, *tempContextObj
;
5219 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5220 return wxRICHTEXT_HITTEST_NONE
;
5223 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5226 wxRichTextObject
* child
= objNode
->GetData();
5227 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5230 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5231 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5236 objNode
= objNode
->GetNext();
5239 wxPoint paraPos
= GetPosition();
5241 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5244 wxRichTextLine
* line
= node
->GetData();
5245 wxPoint linePos
= paraPos
+ line
->GetPosition();
5246 wxSize lineSize
= line
->GetSize();
5247 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5249 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5251 if (pt
.x
< linePos
.x
)
5253 textPosition
= lineRange
.GetStart();
5254 *obj
= FindObjectAtPosition(textPosition
);
5255 *contextObj
= GetContainer();
5256 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5258 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5260 textPosition
= lineRange
.GetEnd();
5261 *obj
= FindObjectAtPosition(textPosition
);
5262 *contextObj
= GetContainer();
5263 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5267 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5268 wxArrayInt partialExtents
;
5273 // This calculates the partial text extents
5274 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5276 int lastX
= linePos
.x
;
5278 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5280 int nextX
= partialExtents
[i
] + linePos
.x
;
5282 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5284 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5286 *obj
= FindObjectAtPosition(textPosition
);
5287 *contextObj
= GetContainer();
5289 // So now we know it's between i-1 and i.
5290 // Let's see if we can be more precise about
5291 // which side of the position it's on.
5293 int midPoint
= (nextX
+ lastX
)/2;
5294 if (pt
.x
>= midPoint
)
5295 return wxRICHTEXT_HITTEST_AFTER
;
5297 return wxRICHTEXT_HITTEST_BEFORE
;
5304 int lastX
= linePos
.x
;
5305 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5310 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5312 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5314 int nextX
= childSize
.x
+ linePos
.x
;
5316 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5320 *obj
= FindObjectAtPosition(textPosition
);
5321 *contextObj
= GetContainer();
5323 // So now we know it's between i-1 and i.
5324 // Let's see if we can be more precise about
5325 // which side of the position it's on.
5327 int midPoint
= (nextX
+ lastX
)/2;
5328 if (pt
.x
>= midPoint
)
5329 return wxRICHTEXT_HITTEST_AFTER
;
5331 return wxRICHTEXT_HITTEST_BEFORE
;
5342 node
= node
->GetNext();
5345 return wxRICHTEXT_HITTEST_NONE
;
5348 /// Split an object at this position if necessary, and return
5349 /// the previous object, or NULL if inserting at beginning.
5350 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5352 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5355 wxRichTextObject
* child
= node
->GetData();
5357 if (pos
== child
->GetRange().GetStart())
5361 if (node
->GetPrevious())
5362 *previousObject
= node
->GetPrevious()->GetData();
5364 *previousObject
= NULL
;
5370 if (child
->GetRange().Contains(pos
))
5372 // This should create a new object, transferring part of
5373 // the content to the old object and the rest to the new object.
5374 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5376 // If we couldn't split this object, just insert in front of it.
5379 // Maybe this is an empty string, try the next one
5384 // Insert the new object after 'child'
5385 if (node
->GetNext())
5386 m_children
.Insert(node
->GetNext(), newObject
);
5388 m_children
.Append(newObject
);
5389 newObject
->SetParent(this);
5392 *previousObject
= child
;
5398 node
= node
->GetNext();
5401 *previousObject
= NULL
;
5405 /// Move content to a list from obj on
5406 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5408 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5411 wxRichTextObject
* child
= node
->GetData();
5414 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5416 node
= node
->GetNext();
5418 m_children
.DeleteNode(oldNode
);
5422 /// Add content back from list
5423 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5425 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5427 AppendChild((wxRichTextObject
*) node
->GetData());
5432 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5434 wxRichTextCompositeObject::CalculateRange(start
, end
);
5436 // Add one for end of paragraph
5439 m_range
.SetRange(start
, end
);
5442 /// Find the object at the given position
5443 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5445 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5448 wxRichTextObject
* obj
= node
->GetData();
5449 if (obj
->GetRange().Contains(position
) ||
5450 obj
->GetRange().GetStart() == position
||
5451 obj
->GetRange().GetEnd() == position
)
5454 node
= node
->GetNext();
5459 /// Get the plain text searching from the start or end of the range.
5460 /// The resulting string may be shorter than the range given.
5461 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5463 text
= wxEmptyString
;
5467 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5470 wxRichTextObject
* obj
= node
->GetData();
5471 if (!obj
->GetRange().IsOutside(range
))
5473 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5476 text
+= textObj
->GetTextForRange(range
);
5484 node
= node
->GetNext();
5489 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5492 wxRichTextObject
* obj
= node
->GetData();
5493 if (!obj
->GetRange().IsOutside(range
))
5495 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5498 text
= textObj
->GetTextForRange(range
) + text
;
5502 text
= wxT(" ") + text
;
5506 node
= node
->GetPrevious();
5513 /// Find a suitable wrap position.
5514 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5516 if (range
.GetLength() <= 0)
5519 // Find the first position where the line exceeds the available space.
5521 long breakPosition
= range
.GetEnd();
5523 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5524 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5528 if (range
.GetStart() > GetRange().GetStart())
5529 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5534 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5536 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5538 if (widthFromStartOfThisRange
> availableSpace
)
5540 breakPosition
= i
-1;
5548 // Binary chop for speed
5549 long minPos
= range
.GetStart();
5550 long maxPos
= range
.GetEnd();
5553 if (minPos
== maxPos
)
5556 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5558 if (sz
.x
> availableSpace
)
5559 breakPosition
= minPos
- 1;
5562 else if ((maxPos
- minPos
) == 1)
5565 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5567 if (sz
.x
> availableSpace
)
5568 breakPosition
= minPos
- 1;
5571 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5572 if (sz
.x
> availableSpace
)
5573 breakPosition
= maxPos
-1;
5579 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5582 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5584 if (sz
.x
> availableSpace
)
5596 // Now we know the last position on the line.
5597 // Let's try to find a word break.
5600 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5602 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5603 if (newLinePos
!= wxNOT_FOUND
)
5605 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5609 int spacePos
= plainText
.Find(wxT(' '), true);
5610 int tabPos
= plainText
.Find(wxT('\t'), true);
5611 int pos
= wxMax(spacePos
, tabPos
);
5612 if (pos
!= wxNOT_FOUND
)
5614 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5615 breakPosition
= breakPosition
- positionsFromEndOfString
;
5620 wrapPosition
= breakPosition
;
5625 /// Get the bullet text for this paragraph.
5626 wxString
wxRichTextParagraph::GetBulletText()
5628 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5629 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5630 return wxEmptyString
;
5632 int number
= GetAttributes().GetBulletNumber();
5635 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5637 text
.Printf(wxT("%d"), number
);
5639 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5641 // TODO: Unicode, and also check if number > 26
5642 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5644 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5646 // TODO: Unicode, and also check if number > 26
5647 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5649 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5651 text
= wxRichTextDecimalToRoman(number
);
5653 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5655 text
= wxRichTextDecimalToRoman(number
);
5658 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5660 text
= GetAttributes().GetBulletText();
5663 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5665 // The outline style relies on the text being computed statically,
5666 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5667 // should be stored in the attributes; if not, just use the number for this
5668 // level, as previously computed.
5669 if (!GetAttributes().GetBulletText().IsEmpty())
5670 text
= GetAttributes().GetBulletText();
5673 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5675 text
= wxT("(") + text
+ wxT(")");
5677 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5679 text
= text
+ wxT(")");
5682 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5690 /// Allocate or reuse a line object
5691 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5693 if (pos
< (int) m_cachedLines
.GetCount())
5695 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5701 wxRichTextLine
* line
= new wxRichTextLine(this);
5702 m_cachedLines
.Append(line
);
5707 /// Clear remaining unused line objects, if any
5708 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5710 int cachedLineCount
= m_cachedLines
.GetCount();
5711 if ((int) cachedLineCount
> lineCount
)
5713 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5715 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5716 wxRichTextLine
* line
= node
->GetData();
5717 m_cachedLines
.Erase(node
);
5724 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5725 /// retrieve the actual style.
5726 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5728 wxRichTextAttr attr
;
5729 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5732 attr
= buf
->GetBasicStyle();
5733 if (!includingBoxAttr
)
5735 attr
.GetTextBoxAttr().Reset();
5736 // The background colour will be painted by the container, and we don't
5737 // want to unnecessarily overwrite the background when we're drawing text
5738 // because this may erase the guideline (which appears just under the text
5739 // if there's no padding).
5740 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5742 wxRichTextApplyStyle(attr
, GetAttributes());
5745 attr
= GetAttributes();
5747 wxRichTextApplyStyle(attr
, contentStyle
);
5751 /// Get combined attributes of the base style and paragraph style.
5752 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5754 wxRichTextAttr attr
;
5755 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5758 attr
= buf
->GetBasicStyle();
5759 if (!includingBoxAttr
)
5760 attr
.GetTextBoxAttr().Reset();
5761 wxRichTextApplyStyle(attr
, GetAttributes());
5764 attr
= GetAttributes();
5769 // Create default tabstop array
5770 void wxRichTextParagraph::InitDefaultTabs()
5772 // create a default tab list at 10 mm each.
5773 for (int i
= 0; i
< 20; ++i
)
5775 sm_defaultTabs
.Add(i
*100);
5779 // Clear default tabstop array
5780 void wxRichTextParagraph::ClearDefaultTabs()
5782 sm_defaultTabs
.Clear();
5785 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5787 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5790 wxRichTextObject
* anchored
= node
->GetData();
5791 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5795 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5798 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5800 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5801 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5803 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5807 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5809 /* Update the offset */
5810 int newOffsetY
= pos
- rect
.y
;
5811 if (newOffsetY
!= offsetY
)
5813 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5814 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5815 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5818 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5820 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5821 x
= rect
.x
+ rect
.width
- size
.x
;
5823 anchored
->SetPosition(wxPoint(x
, pos
));
5824 anchored
->SetCachedSize(size
);
5825 floatCollector
->CollectFloat(this, anchored
);
5828 node
= node
->GetNext();
5832 // Get the first position from pos that has a line break character.
5833 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
5835 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5838 wxRichTextObject
* obj
= node
->GetData();
5839 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
5841 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5844 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
5849 node
= node
->GetNext();
5856 * This object represents a line in a paragraph, and stores
5857 * offsets from the start of the paragraph representing the
5858 * start and end positions of the line.
5861 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
5867 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
5870 m_range
.SetRange(-1, -1);
5871 m_pos
= wxPoint(0, 0);
5872 m_size
= wxSize(0, 0);
5874 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5875 m_objectSizes
.Clear();
5880 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
5882 m_range
= obj
.m_range
;
5883 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5884 m_objectSizes
= obj
.m_objectSizes
;
5888 /// Get the absolute object position
5889 wxPoint
wxRichTextLine::GetAbsolutePosition() const
5891 return m_parent
->GetPosition() + m_pos
;
5894 /// Get the absolute range
5895 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
5897 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
5898 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
5903 * wxRichTextPlainText
5904 * This object represents a single piece of text.
5907 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
5909 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
5910 wxRichTextObject(parent
)
5913 SetAttributes(*style
);
5918 #define USE_KERNING_FIX 1
5920 // If insufficient tabs are defined, this is the tab width used
5921 #define WIDTH_FOR_DEFAULT_TABS 50
5924 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
5926 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
5927 wxASSERT (para
!= NULL
);
5929 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5931 // Let's make the assumption for now that for content in a paragraph, including
5932 // text, we never have a discontinuous selection. So we only deal with a
5934 wxRichTextRange selectionRange
;
5935 if (selection
.IsValid())
5937 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
5938 if (selectionRanges
.GetCount() > 0)
5939 selectionRange
= selectionRanges
[0];
5941 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5944 selectionRange
= wxRICHTEXT_NO_SELECTION
;
5946 int offset
= GetRange().GetStart();
5948 // Replace line break characters with spaces
5949 wxString str
= m_text
;
5950 wxString toRemove
= wxRichTextLineBreakChar
;
5951 str
.Replace(toRemove
, wxT(" "));
5952 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
5955 long len
= range
.GetLength();
5956 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
5958 // Test for the optimized situations where all is selected, or none
5961 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
5962 wxCheckSetFont(dc
, textFont
);
5963 int charHeight
= dc
.GetCharHeight();
5966 if ( textFont
.IsOk() )
5968 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
5970 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5971 textFont
.SetPointSize( static_cast<int>(size
) );
5974 wxCheckSetFont(dc
, textFont
);
5976 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
5978 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
5979 textFont
.SetPointSize( static_cast<int>(size
) );
5981 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
5982 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
5983 wxCheckSetFont(dc
, textFont
);
5988 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5994 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
5997 // TODO: new selection code
5999 // (a) All selected.
6000 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6002 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6004 // (b) None selected.
6005 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6007 // Draw all unselected
6008 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6012 // (c) Part selected, part not
6013 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6015 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6017 // 1. Initial unselected chunk, if any, up until start of selection.
6018 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6020 int r1
= range
.GetStart();
6021 int s1
= selectionRange
.GetStart()-1;
6022 int fragmentLen
= s1
- r1
+ 1;
6023 if (fragmentLen
< 0)
6025 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6027 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6029 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6032 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6034 // Compensate for kerning difference
6035 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6036 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6038 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6039 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6040 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6041 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6043 int kerningDiff
= (w1
+ w3
) - w2
;
6044 x
= x
- kerningDiff
;
6049 // 2. Selected chunk, if any.
6050 if (selectionRange
.GetEnd() >= range
.GetStart())
6052 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6053 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6055 int fragmentLen
= s2
- s1
+ 1;
6056 if (fragmentLen
< 0)
6058 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6060 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6062 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6065 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6067 // Compensate for kerning difference
6068 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6069 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6071 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6072 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6073 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6074 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6076 int kerningDiff
= (w1
+ w3
) - w2
;
6077 x
= x
- kerningDiff
;
6082 // 3. Remaining unselected chunk, if any
6083 if (selectionRange
.GetEnd() < range
.GetEnd())
6085 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6086 int r2
= range
.GetEnd();
6088 int fragmentLen
= r2
- s2
+ 1;
6089 if (fragmentLen
< 0)
6091 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6093 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6095 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6102 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6104 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6106 wxArrayInt tabArray
;
6110 if (attr
.GetTabs().IsEmpty())
6111 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6113 tabArray
= attr
.GetTabs();
6114 tabCount
= tabArray
.GetCount();
6116 for (int i
= 0; i
< tabCount
; ++i
)
6118 int pos
= tabArray
[i
];
6119 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6126 int nextTabPos
= -1;
6132 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6133 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6135 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6136 wxCheckSetPen(dc
, wxPen(highlightColour
));
6137 dc
.SetTextForeground(highlightTextColour
);
6138 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6142 dc
.SetTextForeground(attr
.GetTextColour());
6144 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6146 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6147 dc
.SetTextBackground(attr
.GetBackgroundColour());
6150 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6153 wxCoord x_orig
= GetParent()->GetPosition().x
;
6156 // the string has a tab
6157 // break up the string at the Tab
6158 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6159 str
= str
.AfterFirst(wxT('\t'));
6160 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6162 bool not_found
= true;
6163 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6165 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6167 // Find the next tab position.
6168 // Even if we're at the end of the tab array, we must still draw the chunk.
6170 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6172 if (nextTabPos
<= tabPos
)
6174 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6175 nextTabPos
= tabPos
+ defaultTabWidth
;
6182 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6183 dc
.DrawRectangle(selRect
);
6185 dc
.DrawText(stringChunk
, x
, y
);
6187 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6189 wxPen oldPen
= dc
.GetPen();
6190 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6191 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6192 wxCheckSetPen(dc
, oldPen
);
6198 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6203 dc
.GetTextExtent(str
, & w
, & h
);
6206 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6207 dc
.DrawRectangle(selRect
);
6209 dc
.DrawText(str
, x
, y
);
6211 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6213 wxPen oldPen
= dc
.GetPen();
6214 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6215 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6216 wxCheckSetPen(dc
, oldPen
);
6225 /// Lay the item out
6226 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), int WXUNUSED(style
))
6228 // Only lay out if we haven't already cached the size
6230 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6232 // Eventually we want to have a reasonable estimate of minimum size.
6233 m_minSize
= wxSize(0, 0);
6238 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6240 wxRichTextObject::Copy(obj
);
6242 m_text
= obj
.m_text
;
6245 /// Get/set the object size for the given range. Returns false if the range
6246 /// is invalid for this object.
6247 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6249 if (!range
.IsWithin(GetRange()))
6252 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6253 wxASSERT (para
!= NULL
);
6255 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6257 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6259 // Always assume unformatted text, since at this level we have no knowledge
6260 // of line breaks - and we don't need it, since we'll calculate size within
6261 // formatted text by doing it in chunks according to the line ranges
6263 bool bScript(false);
6264 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6267 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6268 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6270 wxFont textFont
= font
;
6271 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6272 textFont
.SetPointSize( static_cast<int>(size
) );
6273 wxCheckSetFont(dc
, textFont
);
6278 wxCheckSetFont(dc
, font
);
6282 bool haveDescent
= false;
6283 int startPos
= range
.GetStart() - GetRange().GetStart();
6284 long len
= range
.GetLength();
6286 wxString
str(m_text
);
6287 wxString toReplace
= wxRichTextLineBreakChar
;
6288 str
.Replace(toReplace
, wxT(" "));
6290 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6292 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6293 stringChunk
.MakeUpper();
6297 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6299 // the string has a tab
6300 wxArrayInt tabArray
;
6301 if (textAttr
.GetTabs().IsEmpty())
6302 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6304 tabArray
= textAttr
.GetTabs();
6306 int tabCount
= tabArray
.GetCount();
6308 for (int i
= 0; i
< tabCount
; ++i
)
6310 int pos
= tabArray
[i
];
6311 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6315 int nextTabPos
= -1;
6317 while (stringChunk
.Find(wxT('\t')) >= 0)
6319 int absoluteWidth
= 0;
6321 // the string has a tab
6322 // break up the string at the Tab
6323 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6324 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6329 if (partialExtents
->GetCount() > 0)
6330 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6334 // Add these partial extents
6336 dc
.GetPartialTextExtents(stringFragment
, p
);
6338 for (j
= 0; j
< p
.GetCount(); j
++)
6339 partialExtents
->Add(oldWidth
+ p
[j
]);
6341 if (partialExtents
->GetCount() > 0)
6342 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6344 absoluteWidth
= relativeX
;
6348 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6350 absoluteWidth
= width
+ relativeX
;
6354 bool notFound
= true;
6355 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6357 nextTabPos
= tabArray
.Item(i
);
6359 // Find the next tab position.
6360 // Even if we're at the end of the tab array, we must still process the chunk.
6362 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6364 if (nextTabPos
<= absoluteWidth
)
6366 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6367 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6371 width
= nextTabPos
- relativeX
;
6374 partialExtents
->Add(width
);
6380 if (!stringChunk
.IsEmpty())
6385 if (partialExtents
->GetCount() > 0)
6386 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6390 // Add these partial extents
6392 dc
.GetPartialTextExtents(stringChunk
, p
);
6394 for (j
= 0; j
< p
.GetCount(); j
++)
6395 partialExtents
->Add(oldWidth
+ p
[j
]);
6399 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6407 int charHeight
= dc
.GetCharHeight();
6408 if ((*partialExtents
).GetCount() > 0)
6409 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6412 size
= wxSize(w
, charHeight
);
6416 size
= wxSize(width
, dc
.GetCharHeight());
6420 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6428 /// Do a split, returning an object containing the second part, and setting
6429 /// the first part in 'this'.
6430 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6432 long index
= pos
- GetRange().GetStart();
6434 if (index
< 0 || index
>= (int) m_text
.length())
6437 wxString firstPart
= m_text
.Mid(0, index
);
6438 wxString secondPart
= m_text
.Mid(index
);
6442 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6443 newObject
->SetAttributes(GetAttributes());
6445 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6446 GetRange().SetEnd(pos
-1);
6452 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6454 end
= start
+ m_text
.length() - 1;
6455 m_range
.SetRange(start
, end
);
6459 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6461 wxRichTextRange r
= range
;
6463 r
.LimitTo(GetRange());
6465 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6471 long startIndex
= r
.GetStart() - GetRange().GetStart();
6472 long len
= r
.GetLength();
6474 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6478 /// Get text for the given range.
6479 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6481 wxRichTextRange r
= range
;
6483 r
.LimitTo(GetRange());
6485 long startIndex
= r
.GetStart() - GetRange().GetStart();
6486 long len
= r
.GetLength();
6488 return m_text
.Mid(startIndex
, len
);
6491 /// Returns true if this object can merge itself with the given one.
6492 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6494 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6495 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6498 /// Returns true if this object merged itself with the given one.
6499 /// The calling code will then delete the given object.
6500 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6502 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6503 wxASSERT( textObject
!= NULL
);
6507 m_text
+= textObject
->GetText();
6508 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6515 /// Dump to output stream for debugging
6516 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6518 wxRichTextObject::Dump(stream
);
6519 stream
<< m_text
<< wxT("\n");
6522 /// Get the first position from pos that has a line break character.
6523 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6526 int len
= m_text
.length();
6527 int startPos
= pos
- m_range
.GetStart();
6528 for (i
= startPos
; i
< len
; i
++)
6530 wxChar ch
= m_text
[i
];
6531 if (ch
== wxRichTextLineBreakChar
)
6533 return i
+ m_range
.GetStart();
6541 * This is a kind of box, used to represent the whole buffer
6544 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6546 wxList
wxRichTextBuffer::sm_handlers
;
6547 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6548 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6549 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6552 void wxRichTextBuffer::Init()
6554 m_commandProcessor
= new wxCommandProcessor
;
6555 m_styleSheet
= NULL
;
6557 m_batchedCommandDepth
= 0;
6558 m_batchedCommand
= NULL
;
6565 wxRichTextBuffer::~wxRichTextBuffer()
6567 delete m_commandProcessor
;
6568 delete m_batchedCommand
;
6571 ClearEventHandlers();
6574 void wxRichTextBuffer::ResetAndClearCommands()
6578 GetCommandProcessor()->ClearCommands();
6581 Invalidate(wxRICHTEXT_ALL
);
6584 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6586 wxRichTextParagraphLayoutBox::Copy(obj
);
6588 m_styleSheet
= obj
.m_styleSheet
;
6589 m_modified
= obj
.m_modified
;
6590 m_batchedCommandDepth
= 0;
6591 if (m_batchedCommand
)
6592 delete m_batchedCommand
;
6593 m_batchedCommand
= NULL
;
6594 m_suppressUndo
= obj
.m_suppressUndo
;
6595 m_invalidRange
= obj
.m_invalidRange
;
6598 /// Push style sheet to top of stack
6599 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6602 styleSheet
->InsertSheet(m_styleSheet
);
6604 SetStyleSheet(styleSheet
);
6609 /// Pop style sheet from top of stack
6610 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6614 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6615 m_styleSheet
= oldSheet
->GetNextSheet();
6624 /// Submit command to insert paragraphs
6625 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6627 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(pos
, paragraphs
, ctrl
, this, flags
);
6630 /// Submit command to insert paragraphs
6631 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int WXUNUSED(flags
))
6633 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6635 action
->GetNewParagraphs() = paragraphs
;
6637 action
->SetPosition(pos
);
6639 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6640 if (!paragraphs
.GetPartialParagraph())
6641 range
.SetEnd(range
.GetEnd()+1);
6643 // Set the range we'll need to delete in Undo
6644 action
->SetRange(range
);
6646 buffer
->SubmitAction(action
);
6651 /// Submit command to insert the given text
6652 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6654 return ctrl
->GetFocusObject()->InsertTextWithUndo(pos
, text
, ctrl
, this, flags
);
6657 /// Submit command to insert the given text
6658 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6660 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6662 wxRichTextAttr
* p
= NULL
;
6663 wxRichTextAttr paraAttr
;
6664 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6666 // Get appropriate paragraph style
6667 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6668 if (!paraAttr
.IsDefault())
6672 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6674 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6676 if (!text
.empty() && text
.Last() != wxT('\n'))
6678 // Don't count the newline when undoing
6680 action
->GetNewParagraphs().SetPartialParagraph(true);
6682 else if (!text
.empty() && text
.Last() == wxT('\n'))
6685 action
->SetPosition(pos
);
6687 // Set the range we'll need to delete in Undo
6688 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6690 buffer
->SubmitAction(action
);
6695 /// Submit command to insert the given text
6696 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6698 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(pos
, ctrl
, this, flags
);
6701 /// Submit command to insert the given text
6702 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6704 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6706 wxRichTextAttr
* p
= NULL
;
6707 wxRichTextAttr paraAttr
;
6708 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6710 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6711 if (!paraAttr
.IsDefault())
6715 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6717 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6718 action
->GetNewParagraphs().AppendChild(newPara
);
6719 action
->GetNewParagraphs().UpdateRanges();
6720 action
->GetNewParagraphs().SetPartialParagraph(false);
6721 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6725 newPara
->SetAttributes(*p
);
6727 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6729 if (para
&& para
->GetRange().GetEnd() == pos
)
6732 // Now see if we need to number the paragraph.
6733 if (newPara
->GetAttributes().HasBulletNumber())
6735 wxRichTextAttr numberingAttr
;
6736 if (FindNextParagraphNumber(para
, numberingAttr
))
6737 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6741 action
->SetPosition(pos
);
6743 // Use the default character style
6744 // Use the default character style
6745 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6747 // Check whether the default style merely reflects the paragraph/basic style,
6748 // in which case don't apply it.
6749 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6750 wxRichTextAttr toApply
;
6753 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6754 wxRichTextAttr newAttr
;
6755 // This filters out attributes that are accounted for by the current
6756 // paragraph/basic style
6757 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6760 toApply
= defaultStyle
;
6762 if (!toApply
.IsDefault())
6763 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6766 // Set the range we'll need to delete in Undo
6767 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6769 buffer
->SubmitAction(action
);
6774 /// Submit command to insert the given image
6775 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6776 const wxRichTextAttr
& textAttr
)
6778 return ctrl
->GetFocusObject()->InsertImageWithUndo(pos
, imageBlock
, ctrl
, this, flags
, textAttr
);
6781 /// Submit command to insert the given image
6782 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
,
6783 wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
,
6784 const wxRichTextAttr
& textAttr
)
6786 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6788 wxRichTextAttr
* p
= NULL
;
6789 wxRichTextAttr paraAttr
;
6790 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6792 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6793 if (!paraAttr
.IsDefault())
6797 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6799 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6801 newPara
->SetAttributes(*p
);
6803 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6804 newPara
->AppendChild(imageObject
);
6805 imageObject
->SetAttributes(textAttr
);
6806 action
->GetNewParagraphs().AppendChild(newPara
);
6807 action
->GetNewParagraphs().UpdateRanges();
6809 action
->GetNewParagraphs().SetPartialParagraph(true);
6811 action
->SetPosition(pos
);
6813 // Set the range we'll need to delete in Undo
6814 action
->SetRange(wxRichTextRange(pos
, pos
));
6816 buffer
->SubmitAction(action
);
6821 // Insert an object with no change of it
6822 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6824 return ctrl
->GetFocusObject()->InsertObjectWithUndo(pos
, object
, ctrl
, this, flags
);
6827 // Insert an object with no change of it
6828 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
, int flags
)
6830 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6832 wxRichTextAttr
* p
= NULL
;
6833 wxRichTextAttr paraAttr
;
6834 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6836 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6837 if (!paraAttr
.IsDefault())
6841 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6843 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6845 newPara
->SetAttributes(*p
);
6847 newPara
->AppendChild(object
);
6848 action
->GetNewParagraphs().AppendChild(newPara
);
6849 action
->GetNewParagraphs().UpdateRanges();
6851 action
->GetNewParagraphs().SetPartialParagraph(true);
6853 action
->SetPosition(pos
);
6855 // Set the range we'll need to delete in Undo
6856 action
->SetRange(wxRichTextRange(pos
, pos
));
6858 buffer
->SubmitAction(action
);
6860 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
6864 /// Get the style that is appropriate for a new paragraph at this position.
6865 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6867 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
6869 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
6872 wxRichTextAttr attr
;
6873 bool foundAttributes
= false;
6875 // Look for a matching paragraph style
6876 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
6878 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
6881 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6882 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
6884 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
6887 foundAttributes
= true;
6888 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6892 // If we didn't find the 'next style', use this style instead.
6893 if (!foundAttributes
)
6895 foundAttributes
= true;
6896 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
6901 // Also apply list style if present
6902 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
6904 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
6907 int thisIndent
= para
->GetAttributes().GetLeftIndent();
6908 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
6910 // Apply the overall list style, and item style for this level
6911 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
6912 wxRichTextApplyStyle(attr
, listStyle
);
6913 attr
.SetOutlineLevel(thisLevel
);
6914 if (para
->GetAttributes().HasBulletNumber())
6915 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
6919 if (!foundAttributes
)
6921 attr
= para
->GetAttributes();
6922 int flags
= attr
.GetFlags();
6924 // Eliminate character styles
6925 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
6926 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
6927 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
6928 attr
.SetFlags(flags
);
6934 return wxRichTextAttr();
6937 /// Submit command to delete this range
6938 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
6940 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
6943 /// Submit command to delete this range
6944 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
6946 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
6948 action
->SetPosition(ctrl
->GetCaretPosition());
6950 // Set the range to delete
6951 action
->SetRange(range
);
6953 // Copy the fragment that we'll need to restore in Undo
6954 CopyFragment(range
, action
->GetOldParagraphs());
6956 // See if we're deleting a paragraph marker, in which case we need to
6957 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6958 if (range
.GetStart() == range
.GetEnd())
6960 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
6961 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
6963 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
6964 if (nextPara
&& nextPara
!= para
)
6966 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
6967 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
6972 buffer
->SubmitAction(action
);
6977 /// Collapse undo/redo commands
6978 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
6980 if (m_batchedCommandDepth
== 0)
6982 wxASSERT(m_batchedCommand
== NULL
);
6983 if (m_batchedCommand
)
6985 GetCommandProcessor()->Store(m_batchedCommand
);
6987 m_batchedCommand
= new wxRichTextCommand(cmdName
);
6990 m_batchedCommandDepth
++;
6995 /// Collapse undo/redo commands
6996 bool wxRichTextBuffer::EndBatchUndo()
6998 m_batchedCommandDepth
--;
7000 wxASSERT(m_batchedCommandDepth
>= 0);
7001 wxASSERT(m_batchedCommand
!= NULL
);
7003 if (m_batchedCommandDepth
== 0)
7005 GetCommandProcessor()->Store(m_batchedCommand
);
7006 m_batchedCommand
= NULL
;
7012 /// Submit immediately, or delay according to whether collapsing is on
7013 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7015 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7017 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7018 cmd
->AddAction(action
);
7020 cmd
->GetActions().Clear();
7023 m_batchedCommand
->AddAction(action
);
7027 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7028 cmd
->AddAction(action
);
7030 // Only store it if we're not suppressing undo.
7031 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7037 /// Begin suppressing undo/redo commands.
7038 bool wxRichTextBuffer::BeginSuppressUndo()
7045 /// End suppressing undo/redo commands.
7046 bool wxRichTextBuffer::EndSuppressUndo()
7053 /// Begin using a style
7054 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7056 wxRichTextAttr
newStyle(GetDefaultStyle());
7058 // Save the old default style
7059 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7061 wxRichTextApplyStyle(newStyle
, style
);
7062 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7064 SetDefaultStyle(newStyle
);
7070 bool wxRichTextBuffer::EndStyle()
7072 if (!m_attributeStack
.GetFirst())
7074 wxLogDebug(_("Too many EndStyle calls!"));
7078 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7079 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7080 m_attributeStack
.Erase(node
);
7082 SetDefaultStyle(*attr
);
7089 bool wxRichTextBuffer::EndAllStyles()
7091 while (m_attributeStack
.GetCount() != 0)
7096 /// Clear the style stack
7097 void wxRichTextBuffer::ClearStyleStack()
7099 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7100 delete (wxRichTextAttr
*) node
->GetData();
7101 m_attributeStack
.Clear();
7104 /// Begin using bold
7105 bool wxRichTextBuffer::BeginBold()
7107 wxRichTextAttr attr
;
7108 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7110 return BeginStyle(attr
);
7113 /// Begin using italic
7114 bool wxRichTextBuffer::BeginItalic()
7116 wxRichTextAttr attr
;
7117 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7119 return BeginStyle(attr
);
7122 /// Begin using underline
7123 bool wxRichTextBuffer::BeginUnderline()
7125 wxRichTextAttr attr
;
7126 attr
.SetFontUnderlined(true);
7128 return BeginStyle(attr
);
7131 /// Begin using point size
7132 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7134 wxRichTextAttr attr
;
7135 attr
.SetFontSize(pointSize
);
7137 return BeginStyle(attr
);
7140 /// Begin using this font
7141 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7143 wxRichTextAttr attr
;
7146 return BeginStyle(attr
);
7149 /// Begin using this colour
7150 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7152 wxRichTextAttr attr
;
7153 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7154 attr
.SetTextColour(colour
);
7156 return BeginStyle(attr
);
7159 /// Begin using alignment
7160 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7162 wxRichTextAttr attr
;
7163 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7164 attr
.SetAlignment(alignment
);
7166 return BeginStyle(attr
);
7169 /// Begin left indent
7170 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7172 wxRichTextAttr attr
;
7173 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7174 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7176 return BeginStyle(attr
);
7179 /// Begin right indent
7180 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7182 wxRichTextAttr attr
;
7183 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7184 attr
.SetRightIndent(rightIndent
);
7186 return BeginStyle(attr
);
7189 /// Begin paragraph spacing
7190 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7194 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7196 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7198 wxRichTextAttr attr
;
7199 attr
.SetFlags(flags
);
7200 attr
.SetParagraphSpacingBefore(before
);
7201 attr
.SetParagraphSpacingAfter(after
);
7203 return BeginStyle(attr
);
7206 /// Begin line spacing
7207 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7209 wxRichTextAttr attr
;
7210 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7211 attr
.SetLineSpacing(lineSpacing
);
7213 return BeginStyle(attr
);
7216 /// Begin numbered bullet
7217 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7219 wxRichTextAttr attr
;
7220 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7221 attr
.SetBulletStyle(bulletStyle
);
7222 attr
.SetBulletNumber(bulletNumber
);
7223 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7225 return BeginStyle(attr
);
7228 /// Begin symbol bullet
7229 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7231 wxRichTextAttr attr
;
7232 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7233 attr
.SetBulletStyle(bulletStyle
);
7234 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7235 attr
.SetBulletText(symbol
);
7237 return BeginStyle(attr
);
7240 /// Begin standard bullet
7241 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7243 wxRichTextAttr attr
;
7244 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7245 attr
.SetBulletStyle(bulletStyle
);
7246 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7247 attr
.SetBulletName(bulletName
);
7249 return BeginStyle(attr
);
7252 /// Begin named character style
7253 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7255 if (GetStyleSheet())
7257 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7260 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7261 return BeginStyle(attr
);
7267 /// Begin named paragraph style
7268 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7270 if (GetStyleSheet())
7272 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7275 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7276 return BeginStyle(attr
);
7282 /// Begin named list style
7283 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7285 if (GetStyleSheet())
7287 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7290 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7292 attr
.SetBulletNumber(number
);
7294 return BeginStyle(attr
);
7301 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7303 wxRichTextAttr attr
;
7305 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7307 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7310 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7315 return BeginStyle(attr
);
7318 /// Adds a handler to the end
7319 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7321 sm_handlers
.Append(handler
);
7324 /// Inserts a handler at the front
7325 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7327 sm_handlers
.Insert( handler
);
7330 /// Removes a handler
7331 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7333 wxRichTextFileHandler
*handler
= FindHandler(name
);
7336 sm_handlers
.DeleteObject(handler
);
7344 /// Finds a handler by filename or, if supplied, type
7345 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7346 wxRichTextFileType imageType
)
7348 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7349 return FindHandler(imageType
);
7350 else if (!filename
.IsEmpty())
7352 wxString path
, file
, ext
;
7353 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7354 return FindHandler(ext
, imageType
);
7361 /// Finds a handler by name
7362 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7364 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7367 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7368 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7370 node
= node
->GetNext();
7375 /// Finds a handler by extension and type
7376 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7378 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7381 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7382 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7383 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7385 node
= node
->GetNext();
7390 /// Finds a handler by type
7391 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7393 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7396 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7397 if (handler
->GetType() == type
) return handler
;
7398 node
= node
->GetNext();
7403 void wxRichTextBuffer::InitStandardHandlers()
7405 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7406 AddHandler(new wxRichTextPlainTextHandler
);
7409 void wxRichTextBuffer::CleanUpHandlers()
7411 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7414 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7415 wxList::compatibility_iterator next
= node
->GetNext();
7420 sm_handlers
.Clear();
7423 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7430 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7434 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7435 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7440 wildcard
+= wxT(";");
7441 wildcard
+= wxT("*.") + handler
->GetExtension();
7446 wildcard
+= wxT("|");
7447 wildcard
+= handler
->GetName();
7448 wildcard
+= wxT(" ");
7449 wildcard
+= _("files");
7450 wildcard
+= wxT(" (*.");
7451 wildcard
+= handler
->GetExtension();
7452 wildcard
+= wxT(")|*.");
7453 wildcard
+= handler
->GetExtension();
7455 types
->Add(handler
->GetType());
7460 node
= node
->GetNext();
7464 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7469 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7471 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7474 SetDefaultStyle(wxRichTextAttr());
7475 handler
->SetFlags(GetHandlerFlags());
7476 bool success
= handler
->LoadFile(this, filename
);
7477 Invalidate(wxRICHTEXT_ALL
);
7485 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7487 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7490 handler
->SetFlags(GetHandlerFlags());
7491 return handler
->SaveFile(this, filename
);
7497 /// Load from a stream
7498 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7500 wxRichTextFileHandler
* handler
= FindHandler(type
);
7503 SetDefaultStyle(wxRichTextAttr());
7504 handler
->SetFlags(GetHandlerFlags());
7505 bool success
= handler
->LoadFile(this, stream
);
7506 Invalidate(wxRICHTEXT_ALL
);
7513 /// Save to a stream
7514 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7516 wxRichTextFileHandler
* handler
= FindHandler(type
);
7519 handler
->SetFlags(GetHandlerFlags());
7520 return handler
->SaveFile(this, stream
);
7526 /// Copy the range to the clipboard
7527 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7529 bool success
= false;
7530 wxRichTextParagraphLayoutBox
* container
= this;
7531 if (GetRichTextCtrl())
7532 container
= GetRichTextCtrl()->GetFocusObject();
7534 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7536 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7538 wxTheClipboard
->Clear();
7540 // Add composite object
7542 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7545 wxString text
= container
->GetTextForRange(range
);
7548 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7551 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7554 // Add rich text buffer data object. This needs the XML handler to be present.
7556 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7558 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7559 container
->CopyFragment(range
, *richTextBuf
);
7561 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7564 if (wxTheClipboard
->SetData(compositeObject
))
7567 wxTheClipboard
->Close();
7576 /// Paste the clipboard content to the buffer
7577 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7579 bool success
= false;
7580 wxRichTextParagraphLayoutBox
* container
= this;
7581 if (GetRichTextCtrl())
7582 container
= GetRichTextCtrl()->GetFocusObject();
7584 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7585 if (CanPasteFromClipboard())
7587 if (wxTheClipboard
->Open())
7589 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7591 wxRichTextBufferDataObject data
;
7592 wxTheClipboard
->GetData(data
);
7593 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7596 container
->InsertParagraphsWithUndo(position
+1, *richTextBuffer
, GetRichTextCtrl(), this, 0);
7597 if (GetRichTextCtrl())
7598 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7599 delete richTextBuffer
;
7602 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7604 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7608 wxTextDataObject data
;
7609 wxTheClipboard
->GetData(data
);
7610 wxString
text(data
.GetText());
7613 text2
.Alloc(text
.Length()+1);
7615 for (i
= 0; i
< text
.Length(); i
++)
7617 wxChar ch
= text
[i
];
7618 if (ch
!= wxT('\r'))
7622 wxString text2
= text
;
7624 container
->InsertTextWithUndo(position
+1, text2
, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7626 if (GetRichTextCtrl())
7627 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7631 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7633 wxBitmapDataObject data
;
7634 wxTheClipboard
->GetData(data
);
7635 wxBitmap
bitmap(data
.GetBitmap());
7636 wxImage
image(bitmap
.ConvertToImage());
7638 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7640 action
->GetNewParagraphs().AddImage(image
);
7642 if (action
->GetNewParagraphs().GetChildCount() == 1)
7643 action
->GetNewParagraphs().SetPartialParagraph(true);
7645 action
->SetPosition(position
+1);
7647 // Set the range we'll need to delete in Undo
7648 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7650 SubmitAction(action
);
7654 wxTheClipboard
->Close();
7658 wxUnusedVar(position
);
7663 /// Can we paste from the clipboard?
7664 bool wxRichTextBuffer::CanPasteFromClipboard() const
7666 bool canPaste
= false;
7667 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7668 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7670 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7672 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7674 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7675 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7679 wxTheClipboard
->Close();
7685 /// Dumps contents of buffer for debugging purposes
7686 void wxRichTextBuffer::Dump()
7690 wxStringOutputStream
stream(& text
);
7691 wxTextOutputStream
textStream(stream
);
7698 /// Add an event handler
7699 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7701 m_eventHandlers
.Append(handler
);
7705 /// Remove an event handler
7706 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7708 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7711 m_eventHandlers
.Erase(node
);
7721 /// Clear event handlers
7722 void wxRichTextBuffer::ClearEventHandlers()
7724 m_eventHandlers
.Clear();
7727 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7728 /// otherwise will stop at the first successful one.
7729 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7731 bool success
= false;
7732 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7734 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7735 if (handler
->ProcessEvent(event
))
7745 /// Set style sheet and notify of the change
7746 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7748 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7750 wxWindowID winid
= wxID_ANY
;
7751 if (GetRichTextCtrl())
7752 winid
= GetRichTextCtrl()->GetId();
7754 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7755 event
.SetEventObject(GetRichTextCtrl());
7756 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7757 event
.SetOldStyleSheet(oldSheet
);
7758 event
.SetNewStyleSheet(sheet
);
7761 if (SendEvent(event
) && !event
.IsAllowed())
7763 if (sheet
!= oldSheet
)
7769 if (oldSheet
&& oldSheet
!= sheet
)
7772 SetStyleSheet(sheet
);
7774 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7775 event
.SetOldStyleSheet(NULL
);
7778 return SendEvent(event
);
7781 /// Set renderer, deleting old one
7782 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7786 sm_renderer
= renderer
;
7789 /// Hit-testing: returns a flag indicating hit test details, plus
7790 /// information about position
7791 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7793 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7794 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7800 textPosition
= m_ownRange
.GetEnd()-1;
7803 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7807 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7809 if (bulletAttr
.GetTextColour().IsOk())
7811 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7812 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7816 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7817 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7821 if (bulletAttr
.HasFont())
7823 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
7826 font
= (*wxNORMAL_FONT
);
7828 wxCheckSetFont(dc
, font
);
7830 int charHeight
= dc
.GetCharHeight();
7832 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
7833 int bulletHeight
= bulletWidth
;
7837 // Calculate the top position of the character (as opposed to the whole line height)
7838 int y
= rect
.y
+ (rect
.height
- charHeight
);
7840 // Calculate where the bullet should be positioned
7841 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
7843 // The margin between a bullet and text.
7844 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7846 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7847 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
7848 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7849 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
7851 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
7853 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
7855 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
7858 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
7859 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
7860 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
7861 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
7863 dc
.DrawPolygon(4, pts
);
7865 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
7868 pts
[0].x
= x
; pts
[0].y
= y
;
7869 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
7870 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
7872 dc
.DrawPolygon(3, pts
);
7874 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
7876 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
7877 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7879 else // "standard/circle", and catch-all
7881 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
7887 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
7892 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
7894 wxRichTextAttr fontAttr
;
7895 fontAttr
.SetFontSize(attr
.GetFontSize());
7896 fontAttr
.SetFontStyle(attr
.GetFontStyle());
7897 fontAttr
.SetFontWeight(attr
.GetFontWeight());
7898 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
7899 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
7900 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
7902 else if (attr
.HasFont())
7903 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
7905 font
= (*wxNORMAL_FONT
);
7907 wxCheckSetFont(dc
, font
);
7909 if (attr
.GetTextColour().IsOk())
7910 dc
.SetTextForeground(attr
.GetTextColour());
7912 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
7914 int charHeight
= dc
.GetCharHeight();
7916 dc
.GetTextExtent(text
, & tw
, & th
);
7920 // Calculate the top position of the character (as opposed to the whole line height)
7921 int y
= rect
.y
+ (rect
.height
- charHeight
);
7923 // The margin between a bullet and text.
7924 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
7926 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
7927 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
7928 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
7929 x
= x
+ (rect
.width
)/2 - tw
/2;
7931 dc
.DrawText(text
, x
, y
);
7939 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
7941 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7942 // with the buffer. The store will allow retrieval from memory, disk or other means.
7946 /// Enumerate the standard bullet names currently supported
7947 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
7949 bulletNames
.Add(wxTRANSLATE("standard/circle"));
7950 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
7951 bulletNames
.Add(wxTRANSLATE("standard/square"));
7952 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
7953 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
7962 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
7964 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
7965 wxRichTextParagraphLayoutBox(parent
)
7970 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
7975 // TODO: if the active object in the control, draw an indication.
7976 // We need to add the concept of active object, and not just focus object,
7977 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
7978 // Ultimately we would like to be able to interactively resize an active object
7979 // using drag handles.
7980 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
7984 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
7986 wxRichTextParagraphLayoutBox::Copy(obj
);
7989 // Edit properties via a GUI
7990 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
7992 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
7993 boxDlg
.SetAttributes(GetAttributes());
7995 if (boxDlg
.ShowModal() == wxID_OK
)
7997 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
7998 // indeterminate in the object.
7999 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8006 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8008 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8009 wxRichTextBox(parent
)
8014 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8016 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8020 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8022 wxRichTextBox::Copy(obj
);
8025 // Edit properties via a GUI
8026 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8028 // We need to gather common attributes for all selected cells.
8030 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8031 bool multipleCells
= false;
8032 wxRichTextAttr attr
;
8034 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8035 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8037 wxRichTextAttr clashingAttr
, absentAttr
;
8038 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8040 int selectedCellCount
= 0;
8041 for (i
= 0; i
< sel
.GetCount(); i
++)
8043 const wxRichTextRange
& range
= sel
[i
];
8044 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8047 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8049 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8051 selectedCellCount
++;
8054 multipleCells
= selectedCellCount
> 1;
8058 attr
= GetAttributes();
8063 caption
= _("Multiple Cell Properties");
8065 caption
= _("Cell Properties");
8067 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8068 cellDlg
.SetAttributes(attr
);
8070 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8073 // We don't want position and floating controls for a cell.
8074 sizePage
->ShowPositionControls(false);
8075 sizePage
->ShowFloatingControls(false);
8078 if (cellDlg
.ShowModal() == wxID_OK
)
8082 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8083 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8084 // since it may represent clashing attributes across multiple objects.
8085 table
->SetCellStyle(sel
, attr
);
8088 // For a single object, indeterminate attributes set by the user should be reflected in the
8089 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8090 // the style directly instead of applying (which ignores indeterminate attributes,
8091 // leaving them as they were).
8092 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8099 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8101 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8103 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8109 // Draws the object.
8110 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8112 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8115 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8116 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8118 // Lays the object out. rect is the space available for layout. Often it will
8119 // be the specified overall space for this object, if trying to constrain
8120 // layout to a particular size, or it could be the total space available in the
8121 // parent. rect is the overall size, so we must subtract margins and padding.
8122 // to get the actual available space.
8123 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, int style
)
8125 SetPosition(rect
.GetPosition());
8127 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8128 // minimum size if within alloted size, then divide up remaining size
8129 // between rows/cols.
8132 wxRichTextBuffer
* buffer
= GetBuffer();
8133 if (buffer
) scale
= buffer
->GetScale();
8135 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8136 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8138 // If we have no fixed table size, and assuming we're not pushed for
8139 // space, then we don't have to try to stretch the table to fit the contents.
8140 bool stretchToFitTableWidth
= false;
8142 int tableWidth
= rect
.width
;
8143 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8145 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8147 // Fixed table width, so we do want to stretch columns out if necessary.
8148 stretchToFitTableWidth
= true;
8150 // Shouldn't be able to exceed the size passed to this function
8151 tableWidth
= wxMin(rect
.width
, tableWidth
);
8154 // Get internal padding
8155 int paddingLeft
= 0, paddingTop
= 0;
8156 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8157 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8158 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8159 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8161 // Assume that left and top padding are also used for inter-cell padding.
8162 int paddingX
= paddingLeft
;
8163 int paddingY
= paddingTop
;
8165 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8166 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8168 // Internal table width - the area for content
8169 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8171 int rowCount
= m_cells
.GetCount();
8172 if (m_colCount
== 0 || rowCount
== 0)
8174 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8175 SetCachedSize(overallRect
.GetSize());
8177 // Zero content size
8178 SetMinSize(overallRect
.GetSize());
8179 SetMaxSize(GetMinSize());
8183 // The final calculated widths
8184 wxArrayInt
colWidths(m_colCount
);
8186 wxArrayInt
absoluteColWidths(m_colCount
);
8187 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8188 wxArrayInt
percentageColWidths(m_colCount
);
8189 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8190 // These are only relevant when the first column contains spanning information.
8191 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8192 wxArrayInt
maxColWidths(m_colCount
);
8193 wxArrayInt
minColWidths(m_colCount
);
8195 wxSize
tableSize(tableWidth
, 0);
8199 for (i
= 0; i
< m_colCount
; i
++)
8201 absoluteColWidths
[i
] = 0;
8202 // absoluteColWidthsSpanning[i] = 0;
8203 percentageColWidths
[i
] = -1;
8204 // percentageColWidthsSpanning[i] = -1;
8206 maxColWidths
[i
] = 0;
8207 minColWidths
[i
] = 0;
8208 // columnSpans[i] = 1;
8211 // (0) Determine which cells are visible according to spans
8213 // __________________
8218 // |------------------|
8219 // |__________________| 4
8221 // To calculate cell visibility:
8222 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8223 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8224 // that cell, hide the cell.
8226 // We can also use this array to match the size of spanning cells to the grid. Or just do
8227 // this when we iterate through all cells.
8229 // 0.1: add spanning cells to an array
8230 wxRichTextRectArray rectArray
;
8231 for (j
= 0; j
< m_rowCount
; j
++)
8233 for (i
= 0; i
< m_colCount
; i
++)
8235 wxRichTextBox
* cell
= GetCell(j
, i
);
8236 int colSpan
= 1, rowSpan
= 1;
8237 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8238 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8239 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8240 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8241 if (colSpan
> 1 || rowSpan
> 1)
8243 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8247 // 0.2: find which cells are subsumed by a spanning cell
8248 for (j
= 0; j
< m_rowCount
; j
++)
8250 for (i
= 0; i
< m_colCount
; i
++)
8252 wxRichTextBox
* cell
= GetCell(j
, i
);
8253 if (rectArray
.GetCount() == 0)
8259 int colSpan
= 1, rowSpan
= 1;
8260 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8261 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8262 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8263 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8264 if (colSpan
> 1 || rowSpan
> 1)
8266 // Assume all spanning cells are shown
8272 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8274 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8286 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8287 // overlap with a spanned cell starting at a previous column position.
8288 // This means we need to keep an array of rects so we can check. However
8289 // it does also mean that some spans simply may not be taken into account
8290 // where there are different spans happening on different rows. In these cases,
8291 // they will simply be as wide as their constituent columns.
8293 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8294 // the absolute or percentage width of each column.
8296 for (j
= 0; j
< m_rowCount
; j
++)
8298 // First get the overall margins so we can calculate percentage widths based on
8299 // the available content space for all cells on the row
8301 int overallRowContentMargin
= 0;
8302 int visibleCellCount
= 0;
8304 for (i
= 0; i
< m_colCount
; i
++)
8306 wxRichTextBox
* cell
= GetCell(j
, i
);
8307 if (cell
->IsShown())
8309 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8310 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8312 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8313 visibleCellCount
++;
8317 // Add in inter-cell padding
8318 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8320 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8321 wxSize
rowTableSize(rowContentWidth
, 0);
8322 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8324 for (i
= 0; i
< m_colCount
; i
++)
8326 wxRichTextBox
* cell
= GetCell(j
, i
);
8327 if (cell
->IsShown())
8330 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8331 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8333 // Lay out cell to find min/max widths
8334 cell
->Invalidate(wxRICHTEXT_ALL
);
8335 cell
->Layout(dc
, availableSpace
, style
);
8339 int absoluteCellWidth
= -1;
8340 int percentageCellWidth
= -1;
8342 // I think we need to calculate percentages from the internal table size,
8343 // minus the padding between cells which we'll need to calculate from the
8344 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8345 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8346 // so if we want to conform to that we'll need to add in the overall cell margins.
8347 // However, this will make it difficult to specify percentages that add up to
8348 // 100% and still fit within the table width.
8349 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8350 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8351 // If we're using internal content size for the width, we would calculate the
8352 // the overall cell width for n cells as:
8353 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8354 // + thisOverallCellMargin
8355 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8356 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8358 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8360 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8361 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8363 percentageCellWidth
= w
;
8367 absoluteCellWidth
= w
;
8369 // Override absolute width with minimum width if necessary
8370 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8371 absoluteCellWidth
= cell
->GetMinSize().x
;
8374 if (absoluteCellWidth
!= -1)
8376 if (absoluteCellWidth
> absoluteColWidths
[i
])
8377 absoluteColWidths
[i
] = absoluteCellWidth
;
8380 if (percentageCellWidth
!= -1)
8382 if (percentageCellWidth
> percentageColWidths
[i
])
8383 percentageColWidths
[i
] = percentageCellWidth
;
8386 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8387 minColWidths
[i
] = cell
->GetMinSize().x
;
8388 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8389 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8395 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8396 // TODO: simply merge this into (1).
8397 for (i
= 0; i
< m_colCount
; i
++)
8399 if (absoluteColWidths
[i
] > 0)
8401 colWidths
[i
] = absoluteColWidths
[i
];
8403 else if (percentageColWidths
[i
] > 0)
8405 colWidths
[i
] = percentageColWidths
[i
];
8407 // This is rubbish - we calculated the absolute widths from percentages, so
8408 // we can't do it again here.
8409 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8413 // (3) Process absolute or proportional widths of spanning columns,
8414 // now that we know what our fixed column widths are going to be.
8415 // Spanned cells will try to adjust columns so the span will fit.
8416 // Even existing fixed column widths can be expanded if necessary.
8417 // Actually, currently fixed columns widths aren't adjusted; instead,
8418 // the algorithm favours earlier rows and adjusts unspecified column widths
8419 // the first time only. After that, we can't know whether the column has been
8420 // specified explicitly or not. (We could make a note if necessary.)
8421 for (j
= 0; j
< m_rowCount
; j
++)
8423 // First get the overall margins so we can calculate percentage widths based on
8424 // the available content space for all cells on the row
8426 int overallRowContentMargin
= 0;
8427 int visibleCellCount
= 0;
8429 for (i
= 0; i
< m_colCount
; i
++)
8431 wxRichTextBox
* cell
= GetCell(j
, i
);
8432 if (cell
->IsShown())
8434 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8435 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8437 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8438 visibleCellCount
++;
8442 // Add in inter-cell padding
8443 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8445 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8446 wxSize
rowTableSize(rowContentWidth
, 0);
8447 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8449 for (i
= 0; i
< m_colCount
; i
++)
8451 wxRichTextBox
* cell
= GetCell(j
, i
);
8452 if (cell
->IsShown())
8455 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8456 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8460 int spans
= wxMin(colSpan
, m_colCount
- i
);
8464 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8466 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8467 // Override absolute width with minimum width if necessary
8468 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8469 cellWidth
= cell
->GetMinSize().x
;
8473 // Do we want to do this? It's the only chance we get to
8474 // use the cell's min/max sizes, so we need to work out
8475 // how we're going to balance the unspecified spanning cell
8476 // width with the possibility more-constrained constituent cell widths.
8477 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8478 // don't want to constraint all the spanned columns to fit into this cell.
8479 // OK, let's say that if any of the constituent columns don't fit,
8480 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8481 // cells to the columns later.
8482 cellWidth
= cell
->GetMinSize().x
;
8483 if (cell
->GetMaxSize().x
> cellWidth
)
8484 cellWidth
= cell
->GetMaxSize().x
;
8487 // Subtract the padding between cells
8488 int spanningWidth
= cellWidth
;
8489 spanningWidth
-= paddingX
* (spans
-1);
8491 if (spanningWidth
> 0)
8493 // Now share the spanning width between columns within that span
8494 // TODO: take into account min widths of columns within the span
8495 int spanningWidthLeft
= spanningWidth
;
8496 int stretchColCount
= 0;
8497 for (k
= i
; k
< (i
+spans
); k
++)
8499 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8500 spanningWidthLeft
-= colWidths
[k
];
8504 // Now divide what's left between the remaining columns
8506 if (stretchColCount
> 0)
8507 colShare
= spanningWidthLeft
/ stretchColCount
;
8508 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8510 // If fixed-width columns are currently too big, then we'll later
8511 // stretch the spanned cell to fit.
8513 if (spanningWidthLeft
> 0)
8515 for (k
= i
; k
< (i
+spans
); k
++)
8517 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8519 int newWidth
= colShare
;
8520 if (k
== (i
+spans
-1))
8521 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8522 colWidths
[k
] = newWidth
;
8533 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8534 // TODO: take into account min widths of columns within the span
8535 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8536 int widthLeft
= tableWidthMinusPadding
;
8537 int stretchColCount
= 0;
8538 for (i
= 0; i
< m_colCount
; i
++)
8540 // TODO: we need to take into account min widths.
8541 // Subtract min width from width left, then
8542 // add the colShare to the min width
8543 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8544 widthLeft
-= colWidths
[i
];
8547 if (minColWidths
[i
] > 0)
8548 widthLeft
-= minColWidths
[i
];
8554 // Now divide what's left between the remaining columns
8556 if (stretchColCount
> 0)
8557 colShare
= widthLeft
/ stretchColCount
;
8558 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8560 // Check we don't have enough space, in which case shrink all columns, overriding
8561 // any absolute/proportional widths
8562 // TODO: actually we would like to divide up the shrinkage according to size.
8563 // How do we calculate the proportions that will achieve this?
8564 // Could first choose an arbitrary value for stretching cells, and then calculate
8565 // factors to multiply each width by.
8566 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8567 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8569 colShare
= tableWidthMinusPadding
/ m_colCount
;
8570 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8571 for (i
= 0; i
< m_colCount
; i
++)
8574 minColWidths
[i
] = 0;
8578 // We have to adjust the columns if either we need to shrink the
8579 // table to fit the parent/table width, or we explicitly set the
8580 // table width and need to stretch out the table.
8581 if (widthLeft
< 0 || stretchToFitTableWidth
)
8583 for (i
= 0; i
< m_colCount
; i
++)
8585 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8587 if (minColWidths
[i
] > 0)
8588 colWidths
[i
] = minColWidths
[i
] + colShare
;
8590 colWidths
[i
] = colShare
;
8591 if (i
== (m_colCount
-1))
8592 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8597 // TODO: if spanned cells have no specified or max width, make them the
8598 // as big as the columns they span. Do this for all spanned cells in all
8599 // rows, of course. Size any spanned cells left over at the end - even if they
8600 // have width > 0, make sure they're limited to the appropriate column edge.
8604 Sort out confusion between content width
8605 and overall width later. For now, assume we specify overall width.
8607 So, now we've laid out the table to fit into the given space
8608 and have used specified widths and minimum widths.
8610 Now we need to consider how we will try to take maximum width into account.
8614 // (??) TODO: take max width into account
8616 // (6) Lay out all cells again with the current values
8619 int y
= availableSpace
.y
;
8620 for (j
= 0; j
< m_rowCount
; j
++)
8622 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8623 int maxCellHeight
= 0;
8624 int maxSpecifiedCellHeight
= 0;
8626 wxArrayInt
actualWidths(m_colCount
);
8628 wxTextAttrDimensionConverter
converter(dc
, scale
);
8629 for (i
= 0; i
< m_colCount
; i
++)
8631 wxRichTextCell
* cell
= GetCell(j
, i
);
8632 if (cell
->IsShown())
8634 // Get max specified cell height
8635 // Don't handle percentages for height
8636 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8638 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8639 if (h
> maxSpecifiedCellHeight
)
8640 maxSpecifiedCellHeight
= h
;
8643 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8646 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8647 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8649 wxRect availableCellSpace
;
8651 // TODO: take into acount spans
8654 // Calculate the size of this spanning cell from its constituent columns
8656 int spans
= wxMin(colSpan
, m_colCount
- i
);
8657 for (k
= i
; k
< spans
; k
++)
8663 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8666 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8668 // Store actual width so we can force cell to be the appropriate width on the final loop
8669 actualWidths
[i
] = availableCellSpace
.GetWidth();
8672 cell
->Invalidate(wxRICHTEXT_ALL
);
8673 cell
->Layout(dc
, availableCellSpace
, style
);
8675 // TODO: use GetCachedSize().x to compute 'natural' size
8677 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8678 if (cell
->GetCachedSize().y
> maxCellHeight
)
8679 maxCellHeight
= cell
->GetCachedSize().y
;
8684 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8686 for (i
= 0; i
< m_colCount
; i
++)
8688 wxRichTextCell
* cell
= GetCell(j
, i
);
8689 if (cell
->IsShown())
8691 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8692 // Lay out cell with new height
8693 cell
->Invalidate(wxRICHTEXT_ALL
);
8694 cell
->Layout(dc
, availableCellSpace
, style
);
8696 // Make sure the cell size really is the appropriate size,
8697 // not the calculated box size
8698 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8700 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8705 if (j
< (m_rowCount
-1))
8709 // We need to add back the margins etc.
8711 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8712 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8713 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8714 SetCachedSize(marginRect
.GetSize());
8717 // TODO: calculate max size
8719 SetMaxSize(GetCachedSize());
8722 // TODO: calculate min size
8724 SetMinSize(GetCachedSize());
8727 // TODO: currently we use either a fixed table width or the parent's size.
8728 // We also want to be able to calculate the table width from its content,
8729 // whether using fixed column widths or cell content min/max width.
8730 // Probably need a boolean flag to say whether we need to stretch cells
8731 // to fit the table width, or to simply use min/max cell widths. The
8732 // trouble with this is that if cell widths are not specified, they
8733 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8734 // Anyway, ignoring that problem, we probably need to factor layout into a function
8735 // that can can calculate the maximum unconstrained layout in case table size is
8736 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8737 // constrain Layout(), or the previously-calculated max size to constraint layout.
8742 // Finds the absolute position and row height for the given character position
8743 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8745 wxRichTextCell
* child
= GetCell(index
+1);
8748 // Find the position at the start of the child cell, since the table doesn't
8749 // have any caret position of its own.
8750 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8756 // Get the cell at the given character position (in the range of the table).
8757 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8759 int row
= 0, col
= 0;
8760 if (GetCellRowColumnPosition(pos
, row
, col
))
8762 return GetCell(row
, col
);
8768 // Get the row/column for a given character position
8769 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8771 if (m_colCount
== 0 || m_rowCount
== 0)
8774 row
= (int) (pos
/ m_colCount
);
8775 col
= pos
- (row
* m_colCount
);
8777 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8779 if (row
< m_rowCount
&& col
< m_colCount
)
8785 // Calculate range, taking row/cell ordering into account instead of relying
8786 // on list ordering.
8787 void wxRichTextTable::CalculateRange(long start
, long& end
)
8789 long current
= start
;
8790 long lastEnd
= current
;
8799 for (i
= 0; i
< m_rowCount
; i
++)
8801 for (j
= 0; j
< m_colCount
; j
++)
8803 wxRichTextCell
* child
= GetCell(i
, j
);
8808 child
->CalculateRange(current
, childEnd
);
8811 current
= childEnd
+ 1;
8816 // A top-level object always has a range of size 1,
8817 // because its children don't count at this level.
8819 m_range
.SetRange(start
, start
);
8821 // An object with no children has zero length
8822 if (m_children
.GetCount() == 0)
8824 m_ownRange
.SetRange(0, lastEnd
);
8827 // Gets the range size.
8828 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8830 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
8833 // Deletes content in the given range.
8834 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
8836 // TODO: implement deletion of cells
8840 // Gets any text in this object for the given range.
8841 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
8843 return wxRichTextBox::GetTextForRange(range
);
8846 // Copies this object.
8847 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
8849 wxRichTextBox::Copy(obj
);
8853 m_rowCount
= obj
.m_rowCount
;
8854 m_colCount
= obj
.m_colCount
;
8856 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
8859 for (i
= 0; i
< m_rowCount
; i
++)
8861 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8862 for (j
= 0; j
< m_colCount
; j
++)
8864 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
8872 void wxRichTextTable::ClearTable()
8878 bool wxRichTextTable::CreateTable(int rows
, int cols
)
8885 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
8888 for (i
= 0; i
< rows
; i
++)
8890 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
8891 for (j
= 0; j
< cols
; j
++)
8893 wxRichTextCell
* cell
= new wxRichTextCell
;
8895 cell
->AddParagraph(wxEmptyString
);
8904 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
8906 wxASSERT(row
< m_rowCount
);
8907 wxASSERT(col
< m_colCount
);
8909 if (row
< m_rowCount
&& col
< m_colCount
)
8911 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
8912 wxRichTextObject
* obj
= colArray
[col
];
8913 return wxDynamicCast(obj
, wxRichTextCell
);
8919 // Returns a selection object specifying the selections between start and end character positions.
8920 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8921 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
8923 wxRichTextSelection selection
;
8924 selection
.SetContainer((wxRichTextTable
*) this);
8933 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
8935 if (end
>= (m_colCount
* m_rowCount
))
8938 // We need to find the rectangle of cells that is described by the rectangle
8939 // with start, end as the diagonal. Make sure we don't add cells that are
8940 // not currenty visible because they are overlapped by spanning cells.
8942 --------------------------
8943 | 0 | 1 | 2 | 3 | 4 |
8944 --------------------------
8945 | 5 | 6 | 7 | 8 | 9 |
8946 --------------------------
8947 | 10 | 11 | 12 | 13 | 14 |
8948 --------------------------
8949 | 15 | 16 | 17 | 18 | 19 |
8950 --------------------------
8952 Let's say we select 6 -> 18.
8954 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8955 which is left and which is right.
8957 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8959 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8965 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
8966 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
8968 int topRow
= int(start
/m_colCount
);
8969 int bottomRow
= int(end
/m_colCount
);
8971 if (leftCol
> rightCol
)
8978 if (topRow
> bottomRow
)
8980 int tmp
= bottomRow
;
8986 for (i
= topRow
; i
<= bottomRow
; i
++)
8988 for (j
= leftCol
; j
<= rightCol
; j
++)
8990 wxRichTextCell
* cell
= GetCell(i
, j
);
8991 if (cell
&& cell
->IsShown())
8992 selection
.Add(cell
->GetRange());
8999 // Sets the attributes for the cells specified by the selection.
9000 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9002 if (selection
.GetContainer() != this)
9005 wxRichTextBuffer
* buffer
= GetBuffer();
9006 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9007 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9010 buffer
->BeginBatchUndo(_("Set Cell Style"));
9012 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9015 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9016 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9017 SetStyle(cell
, style
, flags
);
9018 node
= node
->GetNext();
9021 // Do action, or delay it until end of batch.
9023 buffer
->EndBatchUndo();
9028 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9030 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9031 if ((startRow
+ noRows
) >= m_rowCount
)
9035 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9037 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9038 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9040 wxRichTextObject
* cell
= colArray
[j
];
9041 RemoveChild(cell
, true);
9044 // Keep deleting at the same position, since we move all
9046 m_cells
.RemoveAt(startRow
);
9049 m_rowCount
= m_rowCount
- noRows
;
9054 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9056 wxASSERT((startCol
+ noCols
) < m_colCount
);
9057 if ((startCol
+ noCols
) >= m_colCount
)
9060 bool deleteRows
= (noCols
== m_colCount
);
9063 for (i
= 0; i
< m_rowCount
; i
++)
9065 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9066 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9068 wxRichTextObject
* cell
= colArray
[j
];
9069 RemoveChild(cell
, true);
9073 m_cells
.RemoveAt(0);
9078 m_colCount
= m_colCount
- noCols
;
9083 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9085 wxASSERT(startRow
<= m_rowCount
);
9086 if (startRow
> m_rowCount
)
9090 for (i
= 0; i
< noRows
; i
++)
9093 if (startRow
== m_rowCount
)
9095 m_cells
.Add(wxRichTextObjectPtrArray());
9096 idx
= m_cells
.GetCount() - 1;
9100 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9104 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9105 for (j
= 0; j
< m_colCount
; j
++)
9107 wxRichTextCell
* cell
= new wxRichTextCell
;
9108 cell
->GetAttributes() = attr
;
9115 m_rowCount
= m_rowCount
+ noRows
;
9119 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9121 wxASSERT(startCol
<= m_colCount
);
9122 if (startCol
> m_colCount
)
9126 for (i
= 0; i
< m_rowCount
; i
++)
9128 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9129 for (j
= 0; j
< noCols
; j
++)
9131 wxRichTextCell
* cell
= new wxRichTextCell
;
9132 cell
->GetAttributes() = attr
;
9136 if (startCol
== m_colCount
)
9139 colArray
.Insert(cell
, startCol
+j
);
9143 m_colCount
= m_colCount
+ noCols
;
9148 // Edit properties via a GUI
9149 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9151 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9152 boxDlg
.SetAttributes(GetAttributes());
9154 if (boxDlg
.ShowModal() == wxID_OK
)
9156 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9164 * Module to initialise and clean up handlers
9167 class wxRichTextModule
: public wxModule
9169 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9171 wxRichTextModule() {}
9174 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9175 wxRichTextBuffer::InitStandardHandlers();
9176 wxRichTextParagraph::InitDefaultTabs();
9181 wxRichTextBuffer::CleanUpHandlers();
9182 wxRichTextDecimalToRoman(-1);
9183 wxRichTextParagraph::ClearDefaultTabs();
9184 wxRichTextCtrl::ClearAvailableFontNames();
9185 wxRichTextBuffer::SetRenderer(NULL
);
9189 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9192 // If the richtext lib is dynamically loaded after the app has already started
9193 // (such as from wxPython) then the built-in module system will not init this
9194 // module. Provide this function to do it manually.
9195 void wxRichTextModuleInit()
9197 wxModule
* module = new wxRichTextModule
;
9199 wxModule::RegisterModule(module);
9204 * Commands for undo/redo
9208 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9209 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9211 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9214 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9218 wxRichTextCommand::~wxRichTextCommand()
9223 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9225 if (!m_actions
.Member(action
))
9226 m_actions
.Append(action
);
9229 bool wxRichTextCommand::Do()
9231 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9233 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9240 bool wxRichTextCommand::Undo()
9242 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9244 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9251 void wxRichTextCommand::ClearActions()
9253 WX_CLEAR_LIST(wxList
, m_actions
);
9261 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9262 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9263 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9267 m_containerAddress
.Create(buffer
, container
);
9268 m_ignoreThis
= ignoreFirstTime
;
9273 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9274 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9276 cmd
->AddAction(this);
9279 wxRichTextAction::~wxRichTextAction()
9285 // Returns the container that this action refers to, using the container address and top-level buffer.
9286 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9288 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9293 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9295 // Store a list of line start character and y positions so we can figure out which area
9296 // we need to refresh
9298 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9299 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9300 wxASSERT(container
!= NULL
);
9304 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9305 // If we had several actions, which only invalidate and leave layout until the
9306 // paint handler is called, then this might not be true. So we may need to switch
9307 // optimisation on only when we're simply adding text and not simultaneously
9308 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9309 // first, but of course this means we'll be doing it twice.
9310 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9312 wxSize clientSize
= m_ctrl
->GetClientSize();
9313 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9314 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9316 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9317 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9320 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9321 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9324 wxRichTextLine
* line
= node2
->GetData();
9325 wxPoint pt
= line
->GetAbsolutePosition();
9326 wxRichTextRange range
= line
->GetAbsoluteRange();
9330 node2
= wxRichTextLineList::compatibility_iterator();
9331 node
= wxRichTextObjectList::compatibility_iterator();
9333 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9335 optimizationLineCharPositions
.Add(range
.GetStart());
9336 optimizationLineYPositions
.Add(pt
.y
);
9340 node2
= node2
->GetNext();
9344 node
= node
->GetNext();
9350 bool wxRichTextAction::Do()
9352 m_buffer
->Modify(true);
9354 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9355 wxASSERT(container
!= NULL
);
9361 case wxRICHTEXT_INSERT
:
9363 // Store a list of line start character and y positions so we can figure out which area
9364 // we need to refresh
9365 wxArrayInt optimizationLineCharPositions
;
9366 wxArrayInt optimizationLineYPositions
;
9368 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9369 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9372 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9373 container
->UpdateRanges();
9375 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9376 // Layout() would stop prematurely at the top level.
9377 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9379 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9381 // Character position to caret position
9382 newCaretPosition
--;
9384 // Don't take into account the last newline
9385 if (m_newParagraphs
.GetPartialParagraph())
9386 newCaretPosition
--;
9388 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9390 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9391 if (p
->GetRange().GetLength() == 1)
9392 newCaretPosition
--;
9395 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9397 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9399 wxRichTextEvent
cmdEvent(
9400 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9401 m_ctrl
? m_ctrl
->GetId() : -1);
9402 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9403 cmdEvent
.SetRange(GetRange());
9404 cmdEvent
.SetPosition(GetRange().GetStart());
9405 cmdEvent
.SetContainer(container
);
9407 m_buffer
->SendEvent(cmdEvent
);
9411 case wxRICHTEXT_DELETE
:
9413 wxArrayInt optimizationLineCharPositions
;
9414 wxArrayInt optimizationLineYPositions
;
9416 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9417 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9420 container
->DeleteRange(GetRange());
9421 container
->UpdateRanges();
9422 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9423 // Layout() would stop prematurely at the top level.
9424 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9426 long caretPos
= GetRange().GetStart()-1;
9427 if (caretPos
>= container
->GetOwnRange().GetEnd())
9430 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9432 wxRichTextEvent
cmdEvent(
9433 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9434 m_ctrl
? m_ctrl
->GetId() : -1);
9435 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9436 cmdEvent
.SetRange(GetRange());
9437 cmdEvent
.SetPosition(GetRange().GetStart());
9438 cmdEvent
.SetContainer(container
);
9440 m_buffer
->SendEvent(cmdEvent
);
9444 case wxRICHTEXT_CHANGE_STYLE
:
9446 ApplyParagraphs(GetNewParagraphs());
9448 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9449 // Layout() would stop prematurely at the top level.
9450 container
->InvalidateHierarchy(GetRange());
9452 UpdateAppearance(GetPosition());
9454 wxRichTextEvent
cmdEvent(
9455 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9456 m_ctrl
? m_ctrl
->GetId() : -1);
9457 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9458 cmdEvent
.SetRange(GetRange());
9459 cmdEvent
.SetPosition(GetRange().GetStart());
9460 cmdEvent
.SetContainer(container
);
9462 m_buffer
->SendEvent(cmdEvent
);
9466 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9468 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9471 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9472 obj
->GetAttributes() = m_attributes
;
9473 m_attributes
= oldAttr
;
9476 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9477 // Layout() would stop prematurely at the top level.
9478 container
->InvalidateHierarchy(GetRange());
9480 UpdateAppearance(GetPosition());
9482 wxRichTextEvent
cmdEvent(
9483 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9484 m_ctrl
? m_ctrl
->GetId() : -1);
9485 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9486 cmdEvent
.SetRange(GetRange());
9487 cmdEvent
.SetPosition(GetRange().GetStart());
9488 cmdEvent
.SetContainer(container
);
9490 m_buffer
->SendEvent(cmdEvent
);
9494 case wxRICHTEXT_CHANGE_OBJECT
:
9496 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9497 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9498 if (obj
&& m_object
)
9500 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9503 wxRichTextObject
* obj
= node
->GetData();
9504 node
->SetData(m_object
);
9509 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9510 // Layout() would stop prematurely at the top level.
9511 container
->InvalidateHierarchy(GetRange());
9513 UpdateAppearance(GetPosition());
9515 // TODO: send new kind of modification event
9526 bool wxRichTextAction::Undo()
9528 m_buffer
->Modify(true);
9530 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9531 wxASSERT(container
!= NULL
);
9537 case wxRICHTEXT_INSERT
:
9539 wxArrayInt optimizationLineCharPositions
;
9540 wxArrayInt optimizationLineYPositions
;
9542 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9543 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9546 container
->DeleteRange(GetRange());
9547 container
->UpdateRanges();
9548 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9549 // Layout() would stop prematurely at the top level.
9550 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9552 long newCaretPosition
= GetPosition() - 1;
9554 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9556 wxRichTextEvent
cmdEvent(
9557 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9558 m_ctrl
? m_ctrl
->GetId() : -1);
9559 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9560 cmdEvent
.SetRange(GetRange());
9561 cmdEvent
.SetPosition(GetRange().GetStart());
9562 cmdEvent
.SetContainer(container
);
9564 m_buffer
->SendEvent(cmdEvent
);
9568 case wxRICHTEXT_DELETE
:
9570 wxArrayInt optimizationLineCharPositions
;
9571 wxArrayInt optimizationLineYPositions
;
9573 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9574 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9577 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9578 container
->UpdateRanges();
9579 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9580 // Layout() would stop prematurely at the top level.
9581 container
->InvalidateHierarchy(GetRange());
9583 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9585 wxRichTextEvent
cmdEvent(
9586 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9587 m_ctrl
? m_ctrl
->GetId() : -1);
9588 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9589 cmdEvent
.SetRange(GetRange());
9590 cmdEvent
.SetPosition(GetRange().GetStart());
9591 cmdEvent
.SetContainer(container
);
9593 m_buffer
->SendEvent(cmdEvent
);
9597 case wxRICHTEXT_CHANGE_STYLE
:
9599 ApplyParagraphs(GetOldParagraphs());
9600 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9601 // Layout() would stop prematurely at the top level.
9602 container
->InvalidateHierarchy(GetRange());
9604 UpdateAppearance(GetPosition());
9606 wxRichTextEvent
cmdEvent(
9607 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9608 m_ctrl
? m_ctrl
->GetId() : -1);
9609 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9610 cmdEvent
.SetRange(GetRange());
9611 cmdEvent
.SetPosition(GetRange().GetStart());
9612 cmdEvent
.SetContainer(container
);
9614 m_buffer
->SendEvent(cmdEvent
);
9618 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9619 case wxRICHTEXT_CHANGE_OBJECT
:
9630 /// Update the control appearance
9631 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9633 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9634 wxASSERT(container
!= NULL
);
9640 m_ctrl
->SetFocusObject(container
);
9641 m_ctrl
->SetCaretPosition(caretPosition
);
9643 if (!m_ctrl
->IsFrozen())
9645 wxRect containerRect
= container
->GetRect();
9647 m_ctrl
->LayoutContent();
9649 // Refresh everything if there were floating objects or the container changed size
9650 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9651 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9653 m_ctrl
->Refresh(false);
9657 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9658 // Find refresh rectangle if we are in a position to optimise refresh
9659 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9663 wxSize clientSize
= m_ctrl
->GetClientSize();
9664 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9666 // Start/end positions
9668 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9670 bool foundEnd
= false;
9672 // position offset - how many characters were inserted
9673 int positionOffset
= GetRange().GetLength();
9675 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9676 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9677 positionOffset
= - positionOffset
;
9679 // find the first line which is being drawn at the same position as it was
9680 // before. Since we're talking about a simple insertion, we can assume
9681 // that the rest of the window does not need to be redrawn.
9683 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9684 // Since we support floating layout, we should redraw the whole para instead of just
9685 // the first line touching the invalid range.
9688 firstY
= para
->GetPosition().y
;
9691 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9694 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9695 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9698 wxRichTextLine
* line
= node2
->GetData();
9699 wxPoint pt
= line
->GetAbsolutePosition();
9700 wxRichTextRange range
= line
->GetAbsoluteRange();
9702 // we want to find the first line that is in the same position
9703 // as before. This will mean we're at the end of the changed text.
9705 if (pt
.y
> lastY
) // going past the end of the window, no more info
9707 node2
= wxRichTextLineList::compatibility_iterator();
9708 node
= wxRichTextObjectList::compatibility_iterator();
9710 // Detect last line in the buffer
9711 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9713 // If deleting text, make sure we refresh below as well as above
9714 if (positionOffset
>= 0)
9717 lastY
= pt
.y
+ line
->GetSize().y
;
9720 node2
= wxRichTextLineList::compatibility_iterator();
9721 node
= wxRichTextObjectList::compatibility_iterator();
9727 // search for this line being at the same position as before
9728 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9730 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9731 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9733 // Stop, we're now the same as we were
9738 node2
= wxRichTextLineList::compatibility_iterator();
9739 node
= wxRichTextObjectList::compatibility_iterator();
9747 node2
= node2
->GetNext();
9751 node
= node
->GetNext();
9754 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9756 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9758 // Convert to device coordinates
9759 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9760 m_ctrl
->RefreshRect(rect
);
9764 m_ctrl
->Refresh(false);
9766 m_ctrl
->PositionCaret();
9768 // This causes styles to persist when doing programmatic
9769 // content creation except when Freeze/Thaw is used, so
9770 // disable this and check for the consequences.
9771 // m_ctrl->SetDefaultStyleToCursorStyle();
9773 if (sendUpdateEvent
)
9774 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9779 /// Replace the buffer paragraphs with the new ones.
9780 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9782 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9783 wxASSERT(container
!= NULL
);
9787 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9790 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9791 wxASSERT (para
!= NULL
);
9793 // We'll replace the existing paragraph by finding the paragraph at this position,
9794 // delete its node data, and setting a copy as the new node data.
9795 // TODO: make more efficient by simply swapping old and new paragraph objects.
9797 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9800 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9803 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9804 newPara
->SetParent(container
);
9806 bufferParaNode
->SetData(newPara
);
9808 delete existingPara
;
9812 node
= node
->GetNext();
9819 * This stores beginning and end positions for a range of data.
9822 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
9824 /// Limit this range to be within 'range'
9825 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
9827 if (m_start
< range
.m_start
)
9828 m_start
= range
.m_start
;
9830 if (m_end
> range
.m_end
)
9831 m_end
= range
.m_end
;
9837 * wxRichTextImage implementation
9838 * This object represents an image.
9841 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
9843 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9844 wxRichTextObject(parent
)
9846 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
9848 SetAttributes(*charStyle
);
9851 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
9852 wxRichTextObject(parent
)
9854 m_imageBlock
= imageBlock
;
9856 SetAttributes(*charStyle
);
9859 /// Create a cached image at the required size
9860 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
9862 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9864 if (!m_imageBlock
.IsOk())
9868 m_imageBlock
.Load(image
);
9872 int width
= image
.GetWidth();
9873 int height
= image
.GetHeight();
9875 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9877 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9878 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9880 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9882 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9884 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
9885 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9887 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9890 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
9891 m_imageCache
= wxBitmap(image
);
9894 // If the original width and height is small, e.g. 400 or below,
9895 // scale up and then down to improve image quality. This can make
9896 // a big difference, with not much performance hit.
9897 int upscaleThreshold
= 400;
9899 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
9901 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
9902 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9905 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
9906 m_imageCache
= wxBitmap(img
);
9910 return m_imageCache
.IsOk();
9914 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
9919 // Don't need cached size AFAIK
9920 // wxSize size = GetCachedSize();
9921 if (!LoadImageCache(dc
))
9924 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9927 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
9929 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
9932 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9933 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9934 marginRect
= rect
; // outer rectangle, will calculate contentRect
9935 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9937 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
9939 if (selection
.WithinSelection(range
.GetStart(), this))
9941 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9942 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9943 dc
.SetLogicalFunction(wxINVERT
);
9944 dc
.DrawRectangle(contentRect
);
9945 dc
.SetLogicalFunction(wxCOPY
);
9951 /// Lay the item out
9952 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, int WXUNUSED(style
))
9954 if (!LoadImageCache(dc
))
9957 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9958 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9959 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9960 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9962 wxSize overallSize
= marginRect
.GetSize();
9964 SetCachedSize(overallSize
);
9965 SetMaxSize(overallSize
);
9966 SetMinSize(overallSize
);
9967 SetPosition(rect
.GetPosition());
9972 /// Get/set the object size for the given range. Returns false if the range
9973 /// is invalid for this object.
9974 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
9976 if (!range
.IsWithin(GetRange()))
9979 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
9981 size
.x
= 0; size
.y
= 0;
9983 partialExtents
->Add(0);
9987 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
9988 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9989 contentRect
= wxRect(wxPoint(0,0), imageSize
);
9990 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9992 wxSize overallSize
= marginRect
.GetSize();
9995 partialExtents
->Add(overallSize
.x
);
10002 // Get the 'natural' size for an object. For an image, it would be the
10004 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10006 wxTextAttrSize size
;
10007 if (GetImageCache().IsOk())
10009 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10010 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10017 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10019 wxRichTextObject::Copy(obj
);
10021 m_imageBlock
= obj
.m_imageBlock
;
10024 /// Edit properties via a GUI
10025 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10027 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10028 imageDlg
.SetAttributes(GetAttributes());
10030 if (imageDlg
.ShowModal() == wxID_OK
)
10032 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10033 // indeterminate in the object.
10034 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10046 /// Compare two attribute objects
10047 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10049 return (attr1
== attr2
);
10052 // Partial equality test taking flags into account
10053 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10055 return attr1
.EqPartial(attr2
);
10059 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10061 if (tabs1
.GetCount() != tabs2
.GetCount())
10065 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10067 if (tabs1
[i
] != tabs2
[i
])
10073 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10075 return destStyle
.Apply(style
, compareWith
);
10078 // Remove attributes
10079 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10081 return destStyle
.RemoveStyle(style
);
10084 /// Combine two bitlists, specifying the bits of interest with separate flags.
10085 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10087 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10090 /// Compare two bitlists
10091 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10093 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10096 /// Split into paragraph and character styles
10097 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10099 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10102 /// Convert a decimal to Roman numerals
10103 wxString
wxRichTextDecimalToRoman(long n
)
10105 static wxArrayInt decimalNumbers
;
10106 static wxArrayString romanNumbers
;
10111 decimalNumbers
.Clear();
10112 romanNumbers
.Clear();
10113 return wxEmptyString
;
10116 if (decimalNumbers
.GetCount() == 0)
10118 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10120 wxRichTextAddDecRom(1000, wxT("M"));
10121 wxRichTextAddDecRom(900, wxT("CM"));
10122 wxRichTextAddDecRom(500, wxT("D"));
10123 wxRichTextAddDecRom(400, wxT("CD"));
10124 wxRichTextAddDecRom(100, wxT("C"));
10125 wxRichTextAddDecRom(90, wxT("XC"));
10126 wxRichTextAddDecRom(50, wxT("L"));
10127 wxRichTextAddDecRom(40, wxT("XL"));
10128 wxRichTextAddDecRom(10, wxT("X"));
10129 wxRichTextAddDecRom(9, wxT("IX"));
10130 wxRichTextAddDecRom(5, wxT("V"));
10131 wxRichTextAddDecRom(4, wxT("IV"));
10132 wxRichTextAddDecRom(1, wxT("I"));
10138 while (n
> 0 && i
< 13)
10140 if (n
>= decimalNumbers
[i
])
10142 n
-= decimalNumbers
[i
];
10143 roman
+= romanNumbers
[i
];
10150 if (roman
.IsEmpty())
10156 * wxRichTextFileHandler
10157 * Base class for file handlers
10160 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10162 #if wxUSE_FFILE && wxUSE_STREAMS
10163 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10165 wxFFileInputStream
stream(filename
);
10167 return LoadFile(buffer
, stream
);
10172 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10174 wxFFileOutputStream
stream(filename
);
10176 return SaveFile(buffer
, stream
);
10180 #endif // wxUSE_FFILE && wxUSE_STREAMS
10182 /// Can we handle this filename (if using files)? By default, checks the extension.
10183 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10185 wxString path
, file
, ext
;
10186 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10188 return (ext
.Lower() == GetExtension());
10192 * wxRichTextTextHandler
10193 * Plain text handler
10196 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10199 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10201 if (!stream
.IsOk())
10207 while (!stream
.Eof())
10209 int ch
= stream
.GetC();
10213 if (ch
== 10 && lastCh
!= 13)
10216 if (ch
> 0 && ch
!= 10)
10223 buffer
->ResetAndClearCommands();
10225 buffer
->AddParagraphs(str
);
10226 buffer
->UpdateRanges();
10231 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10233 if (!stream
.IsOk())
10236 wxString text
= buffer
->GetText();
10238 wxString newLine
= wxRichTextLineBreakChar
;
10239 text
.Replace(newLine
, wxT("\n"));
10241 wxCharBuffer buf
= text
.ToAscii();
10243 stream
.Write((const char*) buf
, text
.length());
10246 #endif // wxUSE_STREAMS
10249 * Stores information about an image, in binary in-memory form
10252 wxRichTextImageBlock::wxRichTextImageBlock()
10257 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10263 wxRichTextImageBlock::~wxRichTextImageBlock()
10268 void wxRichTextImageBlock::Init()
10272 m_imageType
= wxBITMAP_TYPE_INVALID
;
10275 void wxRichTextImageBlock::Clear()
10279 m_imageType
= wxBITMAP_TYPE_INVALID
;
10283 // Load the original image into a memory block.
10284 // If the image is not a JPEG, we must convert it into a JPEG
10285 // to conserve space.
10286 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10287 // load the image a 2nd time.
10289 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10290 wxImage
& image
, bool convertToJPEG
)
10292 m_imageType
= imageType
;
10294 wxString
filenameToRead(filename
);
10295 bool removeFile
= false;
10297 if (imageType
== wxBITMAP_TYPE_INVALID
)
10298 return false; // Could not determine image type
10300 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10302 wxString tempFile
=
10303 wxFileName::CreateTempFileName(_("image"));
10305 wxASSERT(!tempFile
.IsEmpty());
10307 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10308 filenameToRead
= tempFile
;
10311 m_imageType
= wxBITMAP_TYPE_JPEG
;
10314 if (!file
.Open(filenameToRead
))
10317 m_dataSize
= (size_t) file
.Length();
10322 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10325 wxRemoveFile(filenameToRead
);
10327 return (m_data
!= NULL
);
10330 // Make an image block from the wxImage in the given
10332 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10334 image
.SetOption(wxT("quality"), quality
);
10336 if (imageType
== wxBITMAP_TYPE_INVALID
)
10337 return false; // Could not determine image type
10339 return DoMakeImageBlock(image
, imageType
);
10342 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10343 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10345 if (imageType
== wxBITMAP_TYPE_INVALID
)
10346 return false; // Could not determine image type
10348 return DoMakeImageBlock(image
, imageType
);
10351 // Makes the image block
10352 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10354 wxMemoryOutputStream memStream
;
10355 if (!image
.SaveFile(memStream
, imageType
))
10360 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10368 m_imageType
= imageType
;
10369 m_dataSize
= memStream
.GetSize();
10371 memStream
.CopyTo(m_data
, m_dataSize
);
10373 return (m_data
!= NULL
);
10377 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10379 return WriteBlock(filename
, m_data
, m_dataSize
);
10382 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10384 m_imageType
= block
.m_imageType
;
10386 m_dataSize
= block
.m_dataSize
;
10387 if (m_dataSize
== 0)
10390 m_data
= new unsigned char[m_dataSize
];
10392 for (i
= 0; i
< m_dataSize
; i
++)
10393 m_data
[i
] = block
.m_data
[i
];
10397 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10402 // Load a wxImage from the block
10403 bool wxRichTextImageBlock::Load(wxImage
& image
)
10408 // Read in the image.
10410 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10411 bool success
= image
.LoadFile(mstream
, GetImageType());
10413 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10414 wxASSERT(!tempFile
.IsEmpty());
10416 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10420 success
= image
.LoadFile(tempFile
, GetImageType());
10421 wxRemoveFile(tempFile
);
10427 // Write data in hex to a stream
10428 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10430 if (m_dataSize
== 0)
10433 int bufSize
= 100000;
10434 if (int(2*m_dataSize
) < bufSize
)
10435 bufSize
= 2*m_dataSize
;
10436 char* buf
= new char[bufSize
+1];
10438 int left
= m_dataSize
;
10443 if (left
*2 > bufSize
)
10445 n
= bufSize
; left
-= (bufSize
/2);
10449 n
= left
*2; left
= 0;
10453 for (i
= 0; i
< (n
/2); i
++)
10455 wxDecToHex(m_data
[j
], b
, b
+1);
10460 stream
.Write((const char*) buf
, n
);
10466 // Read data in hex from a stream
10467 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10469 int dataSize
= length
/2;
10474 // create a null terminated temporary string:
10478 m_data
= new unsigned char[dataSize
];
10480 for (i
= 0; i
< dataSize
; i
++)
10482 str
[0] = (char)stream
.GetC();
10483 str
[1] = (char)stream
.GetC();
10485 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10488 m_dataSize
= dataSize
;
10489 m_imageType
= imageType
;
10494 // Allocate and read from stream as a block of memory
10495 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10497 unsigned char* block
= new unsigned char[size
];
10501 stream
.Read(block
, size
);
10506 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10508 wxFileInputStream
stream(filename
);
10509 if (!stream
.IsOk())
10512 return ReadBlock(stream
, size
);
10515 // Write memory block to stream
10516 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10518 stream
.Write((void*) block
, size
);
10519 return stream
.IsOk();
10523 // Write memory block to file
10524 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10526 wxFileOutputStream
outStream(filename
);
10527 if (!outStream
.IsOk())
10530 return WriteBlock(outStream
, block
, size
);
10533 // Gets the extension for the block's type
10534 wxString
wxRichTextImageBlock::GetExtension() const
10536 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10538 return handler
->GetExtension();
10540 return wxEmptyString
;
10546 * The data object for a wxRichTextBuffer
10549 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10551 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10553 m_richTextBuffer
= richTextBuffer
;
10555 // this string should uniquely identify our format, but is otherwise
10557 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10559 SetFormat(m_formatRichTextBuffer
);
10562 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10564 delete m_richTextBuffer
;
10567 // after a call to this function, the richTextBuffer is owned by the caller and it
10568 // is responsible for deleting it!
10569 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10571 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10572 m_richTextBuffer
= NULL
;
10574 return richTextBuffer
;
10577 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10579 return m_formatRichTextBuffer
;
10582 size_t wxRichTextBufferDataObject::GetDataSize() const
10584 if (!m_richTextBuffer
)
10590 wxStringOutputStream
stream(& bufXML
);
10591 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10593 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10599 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10600 return strlen(buffer
) + 1;
10602 return bufXML
.Length()+1;
10606 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10608 if (!pBuf
|| !m_richTextBuffer
)
10614 wxStringOutputStream
stream(& bufXML
);
10615 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10617 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10623 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10624 size_t len
= strlen(buffer
);
10625 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10626 ((char*) pBuf
)[len
] = 0;
10628 size_t len
= bufXML
.Length();
10629 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10630 ((char*) pBuf
)[len
] = 0;
10636 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10638 wxDELETE(m_richTextBuffer
);
10640 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10642 m_richTextBuffer
= new wxRichTextBuffer
;
10644 wxStringInputStream
stream(bufXML
);
10645 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10647 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10649 wxDELETE(m_richTextBuffer
);
10661 * wxRichTextFontTable
10662 * Manages quick access to a pool of fonts for rendering rich text
10665 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10667 class wxRichTextFontTableData
: public wxObjectRefData
10670 wxRichTextFontTableData() {}
10672 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10674 wxRichTextFontTableHashMap m_hashMap
;
10677 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10679 wxString
facename(fontSpec
.GetFontFaceName());
10680 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()));
10681 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10683 if ( entry
== m_hashMap
.end() )
10685 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10686 m_hashMap
[spec
] = font
;
10691 return entry
->second
;
10695 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10697 wxRichTextFontTable::wxRichTextFontTable()
10699 m_refData
= new wxRichTextFontTableData
;
10702 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10708 wxRichTextFontTable::~wxRichTextFontTable()
10713 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10715 return (m_refData
== table
.m_refData
);
10718 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10723 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10725 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10727 return data
->FindFont(fontSpec
);
10732 void wxRichTextFontTable::Clear()
10734 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10736 data
->m_hashMap
.clear();
10742 void wxTextBoxAttr::Reset()
10745 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10746 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10747 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10748 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10752 m_position
.Reset();
10761 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10764 m_flags
== attr
.m_flags
&&
10765 m_floatMode
== attr
.m_floatMode
&&
10766 m_clearMode
== attr
.m_clearMode
&&
10767 m_collapseMode
== attr
.m_collapseMode
&&
10768 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10770 m_margins
== attr
.m_margins
&&
10771 m_padding
== attr
.m_padding
&&
10772 m_position
== attr
.m_position
&&
10774 m_size
== attr
.m_size
&&
10776 m_border
== attr
.m_border
&&
10777 m_outline
== attr
.m_outline
10781 // Partial equality test
10782 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10784 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10787 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10790 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10793 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10798 if (!m_position
.EqPartial(attr
.m_position
))
10803 if (!m_margins
.EqPartial(attr
.m_margins
))
10808 if (!m_padding
.EqPartial(attr
.m_padding
))
10813 if (!GetBorder().EqPartial(attr
.GetBorder()))
10818 if (!GetOutline().EqPartial(attr
.GetOutline()))
10824 // Merges the given attributes. If compareWith
10825 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10826 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10827 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
10829 if (attr
.HasFloatMode())
10831 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
10832 SetFloatMode(attr
.GetFloatMode());
10835 if (attr
.HasClearMode())
10837 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
10838 SetClearMode(attr
.GetClearMode());
10841 if (attr
.HasCollapseBorders())
10843 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
10844 SetCollapseBorders(attr
.GetCollapseBorders());
10847 if (attr
.HasVerticalAlignment())
10849 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
10850 SetVerticalAlignment(attr
.GetVerticalAlignment());
10853 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
10854 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
10855 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
10857 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
10859 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
10860 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
10865 // Remove specified attributes from this object
10866 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
10868 if (attr
.HasFloatMode())
10869 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10871 if (attr
.HasClearMode())
10872 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10874 if (attr
.HasCollapseBorders())
10875 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10877 if (attr
.HasVerticalAlignment())
10878 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10880 m_margins
.RemoveStyle(attr
.m_margins
);
10881 m_padding
.RemoveStyle(attr
.m_padding
);
10882 m_position
.RemoveStyle(attr
.m_position
);
10884 m_size
.RemoveStyle(attr
.m_size
);
10886 m_border
.RemoveStyle(attr
.m_border
);
10887 m_outline
.RemoveStyle(attr
.m_outline
);
10892 // Collects the attributes that are common to a range of content, building up a note of
10893 // which attributes are absent in some objects and which clash in some objects.
10894 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
10896 if (attr
.HasFloatMode())
10898 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
10900 if (HasFloatMode())
10902 if (GetFloatMode() != attr
.GetFloatMode())
10904 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10905 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
10909 SetFloatMode(attr
.GetFloatMode());
10913 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
10915 if (attr
.HasClearMode())
10917 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
10919 if (HasClearMode())
10921 if (GetClearMode() != attr
.GetClearMode())
10923 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10924 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
10928 SetClearMode(attr
.GetClearMode());
10932 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
10934 if (attr
.HasCollapseBorders())
10936 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
10938 if (HasCollapseBorders())
10940 if (GetCollapseBorders() != attr
.GetCollapseBorders())
10942 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10943 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10947 SetCollapseBorders(attr
.GetCollapseBorders());
10951 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
10953 if (attr
.HasVerticalAlignment())
10955 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
10957 if (HasVerticalAlignment())
10959 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
10961 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10962 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10966 SetVerticalAlignment(attr
.GetVerticalAlignment());
10970 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
10972 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
10973 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
10974 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
10976 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
10978 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
10979 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
10984 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
10986 wxTextAttr::Copy(attr
);
10988 m_textBoxAttr
= attr
.m_textBoxAttr
;
10991 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
10993 if (!(wxTextAttr::operator==(attr
)))
10996 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
10999 // Partial equality test taking comparison object into account
11000 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11002 if (!(wxTextAttr::EqPartial(attr
)))
11005 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11008 // Merges the given attributes. If compareWith
11009 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11010 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11011 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11013 wxTextAttr::Apply(style
, compareWith
);
11015 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11018 // Remove specified attributes from this object
11019 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11021 wxTextAttr::RemoveStyle(*this, attr
);
11023 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11026 // Collects the attributes that are common to a range of content, building up a note of
11027 // which attributes are absent in some objects and which clash in some objects.
11028 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11030 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11032 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11035 // Partial equality test
11036 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11038 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11041 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11044 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11050 // Apply border to 'this', but not if the same as compareWith
11051 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11053 if (border
.HasStyle())
11055 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11056 SetStyle(border
.GetStyle());
11058 if (border
.HasColour())
11060 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11061 SetColour(border
.GetColourLong());
11063 if (border
.HasWidth())
11065 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11066 SetWidth(border
.GetWidth());
11072 // Remove specified attributes from this object
11073 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11075 if (attr
.HasStyle() && HasStyle())
11076 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11077 if (attr
.HasColour() && HasColour())
11078 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11079 if (attr
.HasWidth() && HasWidth())
11080 m_borderWidth
.Reset();
11085 // Collects the attributes that are common to a range of content, building up a note of
11086 // which attributes are absent in some objects and which clash in some objects.
11087 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11089 if (attr
.HasStyle())
11091 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11095 if (GetStyle() != attr
.GetStyle())
11097 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11098 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11102 SetStyle(attr
.GetStyle());
11106 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11108 if (attr
.HasColour())
11110 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11114 if (GetColour() != attr
.GetColour())
11116 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11117 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11121 SetColour(attr
.GetColourLong());
11125 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11127 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11130 // Partial equality test
11131 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11133 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11134 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11137 // Apply border to 'this', but not if the same as compareWith
11138 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11140 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11141 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11142 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11143 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11147 // Remove specified attributes from this object
11148 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11150 m_left
.RemoveStyle(attr
.m_left
);
11151 m_right
.RemoveStyle(attr
.m_right
);
11152 m_top
.RemoveStyle(attr
.m_top
);
11153 m_bottom
.RemoveStyle(attr
.m_bottom
);
11157 // Collects the attributes that are common to a range of content, building up a note of
11158 // which attributes are absent in some objects and which clash in some objects.
11159 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11161 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11162 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11163 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11164 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11167 // Set style of all borders
11168 void wxTextAttrBorders::SetStyle(int style
)
11170 m_left
.SetStyle(style
);
11171 m_right
.SetStyle(style
);
11172 m_top
.SetStyle(style
);
11173 m_bottom
.SetStyle(style
);
11176 // Set colour of all borders
11177 void wxTextAttrBorders::SetColour(unsigned long colour
)
11179 m_left
.SetColour(colour
);
11180 m_right
.SetColour(colour
);
11181 m_top
.SetColour(colour
);
11182 m_bottom
.SetColour(colour
);
11185 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11187 m_left
.SetColour(colour
);
11188 m_right
.SetColour(colour
);
11189 m_top
.SetColour(colour
);
11190 m_bottom
.SetColour(colour
);
11193 // Set width of all borders
11194 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11196 m_left
.SetWidth(width
);
11197 m_right
.SetWidth(width
);
11198 m_top
.SetWidth(width
);
11199 m_bottom
.SetWidth(width
);
11202 // Partial equality test
11203 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11205 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11211 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11215 if (!(compareWith
&& dim
== (*compareWith
)))
11222 // Collects the attributes that are common to a range of content, building up a note of
11223 // which attributes are absent in some objects and which clash in some objects.
11224 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11226 if (attr
.IsValid())
11228 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11232 if (!((*this) == attr
))
11234 clashingAttr
.SetValid(true);
11243 absentAttr
.SetValid(true);
11246 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11248 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11251 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11253 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11256 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11258 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11261 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11263 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11266 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11268 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11269 return ConvertTenthsMMToPixels(dim
.GetValue());
11270 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11271 return dim
.GetValue();
11272 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11274 wxASSERT(m_parentSize
!= wxDefaultSize
);
11275 if (direction
== wxHORIZONTAL
)
11276 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11278 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11287 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11289 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11290 return dim
.GetValue();
11291 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11292 return ConvertPixelsToTenthsMM(dim
.GetValue());
11300 // Partial equality test
11301 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11303 if (!m_left
.EqPartial(dims
.m_left
))
11306 if (!m_right
.EqPartial(dims
.m_right
))
11309 if (!m_top
.EqPartial(dims
.m_top
))
11312 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11318 // Apply border to 'this', but not if the same as compareWith
11319 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11321 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11322 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11323 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11324 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11329 // Remove specified attributes from this object
11330 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11332 if (attr
.m_left
.IsValid())
11334 if (attr
.m_right
.IsValid())
11336 if (attr
.m_top
.IsValid())
11338 if (attr
.m_bottom
.IsValid())
11344 // Collects the attributes that are common to a range of content, building up a note of
11345 // which attributes are absent in some objects and which clash in some objects.
11346 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11348 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11349 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11350 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11351 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11354 // Partial equality test
11355 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11357 if (!m_width
.EqPartial(size
.m_width
))
11360 if (!m_height
.EqPartial(size
.m_height
))
11366 // Apply border to 'this', but not if the same as compareWith
11367 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11369 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11370 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11375 // Remove specified attributes from this object
11376 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11378 if (attr
.m_width
.IsValid())
11380 if (attr
.m_height
.IsValid())
11386 // Collects the attributes that are common to a range of content, building up a note of
11387 // which attributes are absent in some objects and which clash in some objects.
11388 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11390 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11391 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11394 // Collects the attributes that are common to a range of content, building up a note of
11395 // which attributes are absent in some objects and which clash in some objects.
11396 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11398 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11399 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11401 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11403 if (attr
.HasFont())
11405 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11407 if (currentStyle
.HasFontSize())
11409 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11411 // Clash of attr - mark as such
11412 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11413 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11417 currentStyle
.SetFontSize(attr
.GetFontSize());
11420 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11422 if (currentStyle
.HasFontItalic())
11424 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11426 // Clash of attr - mark as such
11427 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11428 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11432 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11435 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11437 if (currentStyle
.HasFontFamily())
11439 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11441 // Clash of attr - mark as such
11442 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11443 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11447 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11450 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11452 if (currentStyle
.HasFontWeight())
11454 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11456 // Clash of attr - mark as such
11457 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11458 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11462 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11465 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11467 if (currentStyle
.HasFontFaceName())
11469 wxString
faceName1(currentStyle
.GetFontFaceName());
11470 wxString
faceName2(attr
.GetFontFaceName());
11472 if (faceName1
!= faceName2
)
11474 // Clash of attr - mark as such
11475 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11476 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11480 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11483 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11485 if (currentStyle
.HasFontUnderlined())
11487 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11489 // Clash of attr - mark as such
11490 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11491 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11495 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11499 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11501 if (currentStyle
.HasTextColour())
11503 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11505 // Clash of attr - mark as such
11506 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11507 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11511 currentStyle
.SetTextColour(attr
.GetTextColour());
11514 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11516 if (currentStyle
.HasBackgroundColour())
11518 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11520 // Clash of attr - mark as such
11521 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11522 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11526 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11529 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11531 if (currentStyle
.HasAlignment())
11533 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11535 // Clash of attr - mark as such
11536 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11537 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11541 currentStyle
.SetAlignment(attr
.GetAlignment());
11544 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11546 if (currentStyle
.HasTabs())
11548 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11550 // Clash of attr - mark as such
11551 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11552 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11556 currentStyle
.SetTabs(attr
.GetTabs());
11559 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11561 if (currentStyle
.HasLeftIndent())
11563 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11565 // Clash of attr - mark as such
11566 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11567 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11571 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11574 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11576 if (currentStyle
.HasRightIndent())
11578 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11580 // Clash of attr - mark as such
11581 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11582 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11586 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11589 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11591 if (currentStyle
.HasParagraphSpacingAfter())
11593 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11595 // Clash of attr - mark as such
11596 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11597 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11601 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11604 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11606 if (currentStyle
.HasParagraphSpacingBefore())
11608 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11610 // Clash of attr - mark as such
11611 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11612 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11616 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11619 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11621 if (currentStyle
.HasLineSpacing())
11623 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11625 // Clash of attr - mark as such
11626 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11627 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11631 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11634 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11636 if (currentStyle
.HasCharacterStyleName())
11638 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11640 // Clash of attr - mark as such
11641 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11642 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11646 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11649 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11651 if (currentStyle
.HasParagraphStyleName())
11653 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11655 // Clash of attr - mark as such
11656 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11657 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11661 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11664 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11666 if (currentStyle
.HasListStyleName())
11668 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11670 // Clash of attr - mark as such
11671 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11672 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11676 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11679 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11681 if (currentStyle
.HasBulletStyle())
11683 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11685 // Clash of attr - mark as such
11686 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11687 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11691 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11694 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11696 if (currentStyle
.HasBulletNumber())
11698 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11700 // Clash of attr - mark as such
11701 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11702 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11706 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11709 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11711 if (currentStyle
.HasBulletText())
11713 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11715 // Clash of attr - mark as such
11716 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11717 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11722 currentStyle
.SetBulletText(attr
.GetBulletText());
11723 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11727 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11729 if (currentStyle
.HasBulletName())
11731 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11733 // Clash of attr - mark as such
11734 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11735 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11740 currentStyle
.SetBulletName(attr
.GetBulletName());
11744 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11746 if (currentStyle
.HasURL())
11748 if (currentStyle
.GetURL() != attr
.GetURL())
11750 // Clash of attr - mark as such
11751 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11752 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
11757 currentStyle
.SetURL(attr
.GetURL());
11761 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
11763 if (currentStyle
.HasTextEffects())
11765 // We need to find the bits in the new attr that are different:
11766 // just look at those bits that are specified by the new attr.
11768 // We need to remove the bits and flags that are not common between current attr
11769 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11770 // previous styles.
11772 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
11773 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
11775 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
11777 // Find the text effects that were different, using XOR
11778 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
11780 // Clash of attr - mark as such
11781 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
11782 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
11787 currentStyle
.SetTextEffects(attr
.GetTextEffects());
11788 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
11791 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11792 // that we've looked at so far
11793 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
11794 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
11796 if (currentStyle
.GetTextEffectFlags() == 0)
11797 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
11800 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
11802 if (currentStyle
.HasOutlineLevel())
11804 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
11806 // Clash of attr - mark as such
11807 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11808 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
11812 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
11816 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
11818 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
11820 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
11822 if (m_properties
.GetCount() != props
.GetCount())
11826 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11828 const wxVariant
& var1
= m_properties
[i
];
11829 int idx
= props
.Find(var1
.GetName());
11832 const wxVariant
& var2
= props
.m_properties
[idx
];
11833 if (!(var1
== var2
))
11840 wxArrayString
wxRichTextProperties::GetPropertyNames() const
11844 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11846 arr
.Add(m_properties
[i
].GetName());
11851 int wxRichTextProperties::Find(const wxString
& name
) const
11854 for (i
= 0; i
< m_properties
.GetCount(); i
++)
11856 if (m_properties
[i
].GetName() == name
)
11862 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
11864 int idx
= Find(name
);
11865 if (idx
== wxNOT_FOUND
)
11866 SetProperty(name
, wxString());
11868 if (idx
!= wxNOT_FOUND
)
11870 return & (*this)[idx
];
11876 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
11878 static const wxVariant nullVariant
;
11879 int idx
= Find(name
);
11881 return m_properties
[idx
];
11883 return nullVariant
;
11886 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
11888 return GetProperty(name
).GetString();
11891 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
11893 return GetProperty(name
).GetLong();
11896 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
11898 return GetProperty(name
).GetBool();
11901 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
11903 return GetProperty(name
).GetDouble();
11906 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
11908 wxASSERT(!variant
.GetName().IsEmpty());
11910 int idx
= Find(variant
.GetName());
11913 m_properties
.Add(variant
);
11915 m_properties
[idx
] = variant
;
11918 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
11920 int idx
= Find(name
);
11921 wxVariant
var(variant
);
11925 m_properties
.Add(var
);
11927 m_properties
[idx
] = var
;
11930 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
11932 SetProperty(name
, wxVariant(value
, name
));
11935 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
11937 SetProperty(name
, wxVariant(value
, name
));
11940 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
11942 SetProperty(name
, wxVariant(value
, name
));
11945 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
11947 SetProperty(name
, wxVariant(value
, name
));
11950 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
11952 if (m_address
.GetCount() == 0)
11953 return topLevelContainer
;
11955 wxRichTextCompositeObject
* p
= topLevelContainer
;
11957 while (p
&& i
< m_address
.GetCount())
11959 int pos
= m_address
[i
];
11960 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
11961 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
11964 wxRichTextObject
* p1
= p
->GetChild(pos
);
11965 if (i
== (m_address
.GetCount()-1))
11968 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
11974 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
11978 if (topLevelContainer
== obj
)
11981 wxRichTextObject
* o
= obj
;
11984 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
11988 int pos
= p
->GetChildren().IndexOf(o
);
11992 m_address
.Insert(pos
, 0);
11994 if (p
== topLevelContainer
)
12003 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12005 if (m_container
!= sel
.m_container
)
12007 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12010 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12011 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12016 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12017 // or none at the level of the object's container.
12018 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12022 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12024 if (container
== m_container
)
12027 container
= obj
->GetContainer();
12030 if (container
->GetParent())
12032 // If we found that our object's container is within the range of
12033 // a selection higher up, then assume the whole original object
12034 // is also selected.
12035 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12036 if (parentContainer
== m_container
)
12038 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12040 wxRichTextRangeArray ranges
;
12041 ranges
.Add(obj
->GetRange());
12046 container
= parentContainer
;
12055 return wxRichTextRangeArray();
12058 // Is the given position within the selection?
12059 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12065 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12066 return WithinSelection(pos
, selectionRanges
);
12070 // Is the given position within the selection range?
12071 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12074 for (i
= 0; i
< ranges
.GetCount(); i
++)
12076 const wxRichTextRange
& range
= ranges
[i
];
12077 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12083 // Is the given range completely within the selection range?
12084 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12087 for (i
= 0; i
< ranges
.GetCount(); i
++)
12089 const wxRichTextRange
& eachRange
= ranges
[i
];
12090 if (range
.IsWithin(eachRange
))