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
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, 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
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, 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
, wxRichTextDrawingContext
& context
, 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
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector
::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), 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
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, context
, 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
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
603 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
604 marginRect
= outerRect
;
605 wxRichTextAttr
attr(GetAttributes());
606 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
607 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
611 // Invalidate the buffer. With no argument, invalidates whole buffer.
612 void wxRichTextObject
::Invalidate(const wxRichTextRange
& invalidRange
)
614 if (invalidRange
!= wxRICHTEXT_NONE
)
616 // If this is a floating object, size may not be recalculated
617 // after floats have been collected in an early stage of Layout.
618 // So avoid resetting the cache for floating objects during layout.
620 SetCachedSize(wxDefaultSize
);
621 SetMaxSize(wxDefaultSize
);
622 SetMinSize(wxDefaultSize
);
626 // Convert units in tenths of a millimetre to device units
627 int wxRichTextObject
::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
632 scale
= GetBuffer()->GetScale();
633 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
638 // Convert units in tenths of a millimetre to device units
639 int wxRichTextObject
::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
641 // There are ppi pixels in 254.1 "1/10 mm"
643 double pixels
= ((double) units
* (double)ppi
) / 254.1;
647 // If the result is very small, make it at least one pixel in size.
648 if (pixels
== 0 && units
> 0)
654 // Convert units in pixels to tenths of a millimetre
655 int wxRichTextObject
::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
660 scale
= GetBuffer()->GetScale();
662 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
665 int wxRichTextObject
::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
667 // There are ppi pixels in 254.1 "1/10 mm"
669 double p
= double(pixels
);
674 int units
= int( p
* 254.1 / (double) ppi
);
678 // Draw the borders and background for the given rectangle and attributes.
679 // Width and height are taken to be the outer margin size, not the content.
680 bool wxRichTextObject
::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
682 // Assume boxRect is the area around the content
683 wxRect marginRect
= boxRect
;
684 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
686 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
688 // Margin is transparent. Draw background from margin.
689 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
692 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
694 // TODO: get selection colour from control?
695 colour
= wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
698 colour
= attr
.GetBackgroundColour();
701 wxBrush
brush(colour
);
705 dc
.DrawRectangle(borderRect
);
708 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
710 wxRichTextAttr editBorderAttr
= attr
;
711 // TODO: make guideline colour configurable
712 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
713 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
714 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
716 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
719 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
720 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
722 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
723 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
729 bool wxRichTextObject
::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
731 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
732 wxTextAttrDimensionConverter
converter(dc
, buffer ? buffer
->GetScale() : 1.0);
734 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
736 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
737 wxColour
col(attr
.GetLeft().GetColour());
739 // If pen width is > 1, resorts to a solid rectangle.
742 int penStyle
= wxSOLID
;
743 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
745 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
746 penStyle
= wxLONG_DASH
;
747 wxPen
pen(col
, 1, penStyle
);
749 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
752 else if (borderLeft
> 1)
758 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
762 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
764 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
766 wxColour
col(attr
.GetRight().GetColour());
768 // If pen width is > 1, resorts to a solid rectangle.
769 if (borderRight
== 1)
771 int penStyle
= wxSOLID
;
772 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
774 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
775 penStyle
= wxLONG_DASH
;
776 wxPen
pen(col
, 1, penStyle
);
778 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
781 else if (borderRight
> 1)
787 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
791 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
793 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
795 wxColour
col(attr
.GetTop().GetColour());
797 // If pen width is > 1, resorts to a solid rectangle.
800 int penStyle
= wxSOLID
;
801 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
803 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
804 penStyle
= wxLONG_DASH
;
805 wxPen
pen(col
, 1, penStyle
);
807 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
810 else if (borderTop
> 1)
816 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
820 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
822 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
823 wxColour
col(attr
.GetTop().GetColour());
825 // If pen width is > 1, resorts to a solid rectangle.
826 if (borderBottom
== 1)
828 int penStyle
= wxSOLID
;
829 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
831 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
832 penStyle
= wxLONG_DASH
;
833 wxPen
pen(col
, 1, penStyle
);
835 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
838 else if (borderBottom
> 1)
844 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
851 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
852 // or marginRect (outer), and the other must be the default rectangle (no width or height).
853 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
856 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
858 bool wxRichTextObject
::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
860 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
861 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
862 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
863 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
865 wxTextAttrDimensionConverter
converter(dc
, buffer ? buffer
->GetScale() : 1.0);
867 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
868 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
869 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
870 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
871 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
872 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
873 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
874 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
876 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
877 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
878 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
879 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
880 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
881 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
882 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
883 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
885 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
886 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
887 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
888 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
889 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
890 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
891 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
892 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
894 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
895 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
896 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
897 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
898 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
899 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
900 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
901 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
903 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
904 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
905 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
906 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
908 if (marginRect
!= wxRect())
910 contentRect
.x
= marginRect
.x
+ leftTotal
;
911 contentRect
.y
= marginRect
.y
+ topTotal
;
912 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
913 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
917 marginRect
.x
= contentRect
.x
- leftTotal
;
918 marginRect
.y
= contentRect
.y
- topTotal
;
919 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
920 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
923 borderRect
.x
= marginRect
.x
+ marginLeft
;
924 borderRect
.y
= marginRect
.y
+ marginTop
;
925 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
926 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
928 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
929 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
930 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
931 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
933 // The outline is outside the margin and doesn't influence the overall box position or content size.
934 outlineRect
.x
= marginRect
.x
- outlineLeft
;
935 outlineRect
.y
= marginRect
.y
- outlineTop
;
936 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
937 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
942 // Get the total margin for the object in pixels, taking into account margin, padding and border size
943 bool wxRichTextObject
::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
944 int& topMargin
, int& bottomMargin
)
946 // Assume boxRect is the area around the content
947 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
948 marginRect
= wxRect(0, 0, 1000, 1000);
950 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
952 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
953 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
954 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
955 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
960 // Returns the rectangle which the child has available to it given restrictions specified in the
961 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
962 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
963 // E.g. a cell that's 50% of its parent.
964 wxRect wxRichTextObject
::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
966 wxRect rect
= availableParentSpace
;
969 scale
= buffer
->GetScale();
971 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
973 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
974 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
976 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
977 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
979 // Can specify either left or right for the position (we're assuming we can't
980 // set the left and right edges to effectively set the size. Would we want to do that?)
981 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
983 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
985 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
987 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
988 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
989 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
994 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
996 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
998 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
1000 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
1001 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
1002 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1007 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1008 rect
.SetWidth(availableParentSpace
.GetWidth());
1013 // Dump to output stream for debugging
1014 void wxRichTextObject
::Dump(wxTextOutputStream
& stream
)
1016 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1017 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");
1018 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");
1021 // Gets the containing buffer
1022 wxRichTextBuffer
* wxRichTextObject
::GetBuffer() const
1024 const wxRichTextObject
* obj
= this;
1025 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1026 obj
= obj
->GetParent();
1027 return wxDynamicCast(obj
, wxRichTextBuffer
);
1030 // Get the absolute object position, by traversing up the child/parent hierarchy
1031 wxPoint wxRichTextObject
::GetAbsolutePosition() const
1033 wxPoint pt
= GetPosition();
1035 wxRichTextObject
* p
= GetParent();
1038 pt
= pt
+ p
->GetPosition();
1045 // Hit-testing: returns a flag indicating hit test details, plus
1046 // information about position
1047 int wxRichTextObject
::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1050 return wxRICHTEXT_HITTEST_NONE
;
1052 wxRect rect
= GetRect();
1053 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1054 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1057 *contextObj
= GetParentContainer();
1058 textPosition
= GetRange().GetStart();
1059 return wxRICHTEXT_HITTEST_ON
;
1062 return wxRICHTEXT_HITTEST_NONE
;
1065 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1066 // lays out the object again using the maximum ('best') size
1067 bool wxRichTextObject
::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1068 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1069 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1072 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1073 wxRect originalAvailableRect
= availableChildRect
;
1074 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1076 wxSize maxSize
= GetMaxSize();
1078 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1080 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1082 // Redo the layout with a fixed, minimum size this time.
1083 Invalidate(wxRICHTEXT_ALL
);
1084 wxRichTextAttr
newAttr(attr
);
1085 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1086 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1088 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1090 // If a paragraph, align the whole paragraph.
1091 // Problem with this: if we're limited by a floating object, a line may be centered
1092 // w.r.t. the smaller resulting box rather than the actual available width.
1093 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1095 // centering, right-justification
1096 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1098 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1100 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1102 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1106 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1120 // Move the object recursively, by adding the offset from old to new
1121 void wxRichTextObject
::Move(const wxPoint
& pt
)
1128 * wxRichTextCompositeObject
1129 * This is the base for drawable objects.
1132 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1134 wxRichTextCompositeObject
::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1135 wxRichTextObject(parent
)
1139 wxRichTextCompositeObject
::~wxRichTextCompositeObject()
1144 /// Get the nth child
1145 wxRichTextObject
* wxRichTextCompositeObject
::GetChild(size_t n
) const
1147 wxASSERT ( n
< m_children
.GetCount() );
1149 return m_children
.Item(n
)->GetData();
1152 /// Append a child, returning the position
1153 size_t wxRichTextCompositeObject
::AppendChild(wxRichTextObject
* child
)
1155 m_children
.Append(child
);
1156 child
->SetParent(this);
1157 return m_children
.GetCount() - 1;
1160 /// Insert the child in front of the given object, or at the beginning
1161 bool wxRichTextCompositeObject
::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1165 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1166 m_children
.Insert(node
, child
);
1169 m_children
.Insert(child
);
1170 child
->SetParent(this);
1175 /// Delete the child
1176 bool wxRichTextCompositeObject
::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1178 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(child
);
1181 wxRichTextObject
* obj
= node
->GetData();
1182 m_children
.Erase(node
);
1191 /// Delete all children
1192 bool wxRichTextCompositeObject
::DeleteChildren()
1194 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1197 wxRichTextObjectList
::compatibility_iterator oldNode
= node
;
1199 wxRichTextObject
* child
= node
->GetData();
1200 child
->Dereference(); // Only delete if reference count is zero
1202 node
= node
->GetNext();
1203 m_children
.Erase(oldNode
);
1209 /// Get the child count
1210 size_t wxRichTextCompositeObject
::GetChildCount() const
1212 return m_children
.GetCount();
1216 void wxRichTextCompositeObject
::Copy(const wxRichTextCompositeObject
& obj
)
1218 wxRichTextObject
::Copy(obj
);
1222 wxRichTextObjectList
::compatibility_iterator node
= obj
.m_children
.GetFirst();
1225 wxRichTextObject
* child
= node
->GetData();
1226 wxRichTextObject
* newChild
= child
->Clone();
1227 newChild
->SetParent(this);
1228 m_children
.Append(newChild
);
1230 node
= node
->GetNext();
1234 /// Hit-testing: returns a flag indicating hit test details, plus
1235 /// information about position
1236 int wxRichTextCompositeObject
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1239 return wxRICHTEXT_HITTEST_NONE
;
1241 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1244 wxRichTextObject
* child
= node
->GetData();
1246 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1248 // Just check if we hit the overall object
1249 int ret
= child
->wxRichTextObject
::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1250 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1253 else if (child
->IsShown())
1255 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1256 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1260 node
= node
->GetNext();
1263 return wxRICHTEXT_HITTEST_NONE
;
1266 /// Finds the absolute position and row height for the given character position
1267 bool wxRichTextCompositeObject
::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1269 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1272 wxRichTextObject
* child
= node
->GetData();
1274 // Don't recurse if the child is a top-level object,
1275 // such as a text box, because the character position will no longer
1276 // apply. By definition, a top-level object has its own range of
1277 // character positions.
1278 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1281 node
= node
->GetNext();
1288 void wxRichTextCompositeObject
::CalculateRange(long start
, long& end
)
1290 long current
= start
;
1291 long lastEnd
= current
;
1299 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1302 wxRichTextObject
* child
= node
->GetData();
1305 child
->CalculateRange(current
, childEnd
);
1308 current
= childEnd
+ 1;
1310 node
= node
->GetNext();
1315 // A top-level object always has a range of size 1,
1316 // because its children don't count at this level.
1318 m_range
.SetRange(start
, start
);
1320 // An object with no children has zero length
1321 if (m_children
.GetCount() == 0)
1323 m_ownRange
.SetRange(0, lastEnd
);
1329 // An object with no children has zero length
1330 if (m_children
.GetCount() == 0)
1333 m_range
.SetRange(start
, end
);
1337 /// Delete range from layout.
1338 bool wxRichTextCompositeObject
::DeleteRange(const wxRichTextRange
& range
)
1340 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1344 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1345 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
1347 // Delete the range in each paragraph
1349 // When a chunk has been deleted, internally the content does not
1350 // now match the ranges.
1351 // However, so long as deletion is not done on the same object twice this is OK.
1352 // If you may delete content from the same object twice, recalculate
1353 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1354 // adjust the range you're deleting accordingly.
1356 if (!obj
->GetRange().IsOutside(range
))
1358 // No need to delete within a top-level object; just removing this object will do fine
1359 if (!obj
->IsTopLevel())
1360 obj
->DeleteRange(range
);
1362 // Delete an empty object, or paragraph within this range.
1363 if (obj
->IsEmpty() ||
1364 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1366 // An empty paragraph has length 1, so won't be deleted unless the
1367 // whole range is deleted.
1368 RemoveChild(obj
, true);
1378 /// Get any text in this object for the given range
1379 wxString wxRichTextCompositeObject
::GetTextForRange(const wxRichTextRange
& range
) const
1382 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1385 wxRichTextObject
* child
= node
->GetData();
1386 wxRichTextRange childRange
= range
;
1387 if (!child
->GetRange().IsOutside(range
))
1389 childRange
.LimitTo(child
->GetRange());
1391 wxString childText
= child
->GetTextForRange(childRange
);
1395 node
= node
->GetNext();
1401 /// Get the child object at the given character position
1402 wxRichTextObject
* wxRichTextCompositeObject
::GetChildAtPosition(long pos
) const
1404 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1407 wxRichTextObject
* child
= node
->GetData();
1408 if (child
->GetRange().GetStart() == pos
)
1410 node
= node
->GetNext();
1415 /// Recursively merge all pieces that can be merged.
1416 bool wxRichTextCompositeObject
::Defragment(const wxRichTextRange
& range
)
1418 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1421 wxRichTextObject
* child
= node
->GetData();
1422 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1424 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1426 composite
->Defragment();
1428 if (node
->GetNext())
1430 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1431 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1433 nextChild
->Dereference();
1434 m_children
.Erase(node
->GetNext());
1436 // Don't set node -- we'll see if we can merge again with the next
1440 node
= node
->GetNext();
1443 node
= node
->GetNext();
1446 node
= node
->GetNext();
1449 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1450 if (GetChildCount() > 1)
1452 node
= m_children
.GetFirst();
1455 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
1456 wxRichTextObject
* child
= node
->GetData();
1457 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1459 if (child
->IsEmpty())
1461 child
->Dereference();
1462 m_children
.Erase(node
);
1467 node
= node
->GetNext();
1474 /// Dump to output stream for debugging
1475 void wxRichTextCompositeObject
::Dump(wxTextOutputStream
& stream
)
1477 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1480 wxRichTextObject
* child
= node
->GetData();
1481 child
->Dump(stream
);
1482 node
= node
->GetNext();
1486 /// Get/set the object size for the given range. Returns false if the range
1487 /// is invalid for this object.
1488 bool wxRichTextCompositeObject
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1490 if (!range
.IsWithin(GetRange()))
1495 wxArrayInt childExtents
;
1502 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1505 wxRichTextObject
* child
= node
->GetData();
1506 if (!child
->GetRange().IsOutside(range
))
1508 // Floating objects have a zero size within the paragraph.
1509 if (child
->IsFloating())
1514 if (partialExtents
->GetCount() > 0)
1515 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1519 partialExtents
->Add(0 /* zero size */ + lastSize
);
1526 wxRichTextRange rangeToUse
= range
;
1527 rangeToUse
.LimitTo(child
->GetRange());
1528 if (child
->IsTopLevel())
1529 rangeToUse
= child
->GetOwnRange();
1531 int childDescent
= 0;
1533 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1534 // but it's only going to be used after caching has taken place.
1535 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1537 childDescent
= child
->GetDescent();
1538 childSize
= child
->GetCachedSize();
1540 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1541 sz
.x
+= childSize
.x
;
1542 descent
= wxMax(descent
, childDescent
);
1544 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1546 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1547 sz
.x
+= childSize
.x
;
1548 descent
= wxMax(descent
, childDescent
);
1550 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1552 child
->SetCachedSize(childSize
);
1553 child
->SetDescent(childDescent
);
1559 if (partialExtents
->GetCount() > 0)
1560 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1565 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1567 partialExtents
->Add(childExtents
[i
] + lastSize
);
1577 node
= node
->GetNext();
1583 // Invalidate the buffer. With no argument, invalidates whole buffer.
1584 void wxRichTextCompositeObject
::Invalidate(const wxRichTextRange
& invalidRange
)
1586 wxRichTextObject
::Invalidate(invalidRange
);
1588 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1591 wxRichTextObject
* child
= node
->GetData();
1592 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1596 else if (child
->IsTopLevel())
1598 if (invalidRange
== wxRICHTEXT_NONE
)
1599 child
->Invalidate(wxRICHTEXT_NONE
);
1601 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1604 child
->Invalidate(invalidRange
);
1605 node
= node
->GetNext();
1609 // Move the object recursively, by adding the offset from old to new
1610 void wxRichTextCompositeObject
::Move(const wxPoint
& pt
)
1612 wxPoint oldPos
= GetPosition();
1614 wxPoint offset
= pt
- oldPos
;
1616 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1619 wxRichTextObject
* child
= node
->GetData();
1620 wxPoint childPos
= child
->GetPosition() + offset
;
1621 child
->Move(childPos
);
1622 node
= node
->GetNext();
1628 * wxRichTextParagraphLayoutBox
1629 * This box knows how to lay out paragraphs.
1632 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1634 wxRichTextParagraphLayoutBox
::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1635 wxRichTextCompositeObject(parent
)
1640 wxRichTextParagraphLayoutBox
::~wxRichTextParagraphLayoutBox()
1642 if (m_floatCollector
)
1644 delete m_floatCollector
;
1645 m_floatCollector
= NULL
;
1649 /// Initialize the object.
1650 void wxRichTextParagraphLayoutBox
::Init()
1654 // For now, assume is the only box and has no initial size.
1655 m_range
= wxRichTextRange(0, -1);
1656 m_ownRange
= wxRichTextRange(0, -1);
1658 m_invalidRange
= wxRICHTEXT_ALL
;
1660 m_partialParagraph
= false;
1661 m_floatCollector
= NULL
;
1664 void wxRichTextParagraphLayoutBox
::Clear()
1668 if (m_floatCollector
)
1669 delete m_floatCollector
;
1670 m_floatCollector
= NULL
;
1671 m_partialParagraph
= false;
1675 void wxRichTextParagraphLayoutBox
::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1679 wxRichTextCompositeObject
::Copy(obj
);
1681 m_partialParagraph
= obj
.m_partialParagraph
;
1682 m_defaultAttributes
= obj
.m_defaultAttributes
;
1685 // Gather information about floating objects; only gather floats for those paragraphs that
1686 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1688 bool wxRichTextParagraphLayoutBox
::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1690 if (m_floatCollector
!= NULL
)
1691 delete m_floatCollector
;
1692 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1693 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1694 // Only gather floats up to the point we'll start formatting paragraphs.
1695 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1697 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1698 wxASSERT (child
!= NULL
);
1700 m_floatCollector
->CollectFloat(child
);
1701 node
= node
->GetNext();
1707 // Returns the style sheet associated with the overall buffer.
1708 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox
::GetStyleSheet() const
1710 return GetBuffer() ?
GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1713 // Get the number of floating objects at this level
1714 int wxRichTextParagraphLayoutBox
::GetFloatingObjectCount() const
1716 if (m_floatCollector
)
1717 return m_floatCollector
->GetFloatingObjectCount();
1722 // Get a list of floating objects
1723 bool wxRichTextParagraphLayoutBox
::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1725 if (m_floatCollector
)
1727 return m_floatCollector
->GetFloatingObjects(objects
);
1734 void wxRichTextParagraphLayoutBox
::UpdateRanges()
1738 start
= GetRange().GetStart();
1740 CalculateRange(start
, end
);
1744 int wxRichTextParagraphLayoutBox
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1747 return wxRICHTEXT_HITTEST_NONE
;
1749 int ret
= wxRICHTEXT_HITTEST_NONE
;
1750 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1751 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1753 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1754 return wxRichTextCompositeObject
::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1762 /// Draw the floating objects
1763 void wxRichTextParagraphLayoutBox
::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1765 if (m_floatCollector
)
1766 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1769 void wxRichTextParagraphLayoutBox
::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1774 from
->RemoveChild(obj
);
1775 to
->AppendChild(obj
);
1779 bool wxRichTextParagraphLayoutBox
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1784 wxRect
thisRect(GetPosition(), GetCachedSize());
1786 wxRichTextAttr
attr(GetAttributes());
1787 context
.ApplyVirtualAttributes(attr
, this);
1790 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1791 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1793 // Don't draw guidelines if at top level
1794 int theseFlags
= flags
;
1796 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1797 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1799 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1800 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1803 wxRichTextObject
* child
= node
->GetData();
1805 if (child
&& !child
->GetRange().IsOutside(range
))
1807 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1808 wxRichTextRange childRange
= range
;
1809 if (child
->IsTopLevel())
1811 childRange
= child
->GetOwnRange();
1814 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1819 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1824 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1827 node
= node
->GetNext();
1832 /// Lay the item out
1833 bool wxRichTextParagraphLayoutBox
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1835 SetPosition(rect
.GetPosition());
1840 wxRect availableSpace
;
1841 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1843 wxRichTextAttr
attr(GetAttributes());
1844 context
.ApplyVirtualAttributes(attr
, this);
1846 // If only laying out a specific area, the passed rect has a different meaning:
1847 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1848 // so that during a size, only the visible part will be relaid out, or
1849 // it would take too long causing flicker. As an approximation, we assume that
1850 // everything up to the start of the visible area is laid out correctly.
1853 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1854 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1856 // Invalidate the part of the buffer from the first visible line
1857 // to the end. If other parts of the buffer are currently invalid,
1858 // then they too will be taken into account if they are above
1859 // the visible point.
1861 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1863 startPos
= line
->GetAbsoluteRange().GetStart();
1865 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1869 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1872 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1873 wxRichTextObject
::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1874 topMargin
, bottomMargin
);
1879 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1880 int maxMaxWidth
= 0;
1882 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1883 int maxMinWidth
= 0;
1885 // If we have vertical alignment, we must recalculate everything.
1886 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1887 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1889 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1891 bool layoutAll
= true;
1893 // Get invalid range, rounding to paragraph start/end.
1894 wxRichTextRange invalidRange
= GetInvalidRange(true);
1896 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1899 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1901 else // If we know what range is affected, start laying out from that point on.
1902 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1904 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1907 wxRichTextObjectList
::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1908 wxRichTextObjectList
::compatibility_iterator previousNode
;
1910 previousNode
= firstNode
->GetPrevious();
1915 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1916 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1919 // Now we're going to start iterating from the first affected paragraph.
1927 // Gather information about only those floating objects that will not be formatted,
1928 // after which floats will be gathered per-paragraph during layout.
1929 UpdateFloatingObjects(availableSpace
, node ? node
->GetData() : (wxRichTextObject
*) NULL
);
1931 // A way to force speedy rest-of-buffer layout (the 'else' below)
1932 bool forceQuickLayout
= false;
1934 // First get the size of the paragraphs we won't be laying out
1935 wxRichTextObjectList
::compatibility_iterator n
= m_children
.GetFirst();
1936 while (n
&& n
!= node
)
1938 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1941 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1942 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1943 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1950 // Assume this box only contains paragraphs
1952 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1953 // Unsure if this is needed
1954 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1956 if (child
&& child
->IsShown())
1958 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1959 if ( !forceQuickLayout
&&
1961 child
->GetLines().IsEmpty() ||
1962 !child
->GetRange().IsOutside(invalidRange
)) )
1964 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1965 // lays out the object again using the minimum size
1966 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1967 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1969 // Layout must set the cached size
1970 availableSpace
.y
+= child
->GetCachedSize().y
;
1971 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1972 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1973 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1975 // If we're just formatting the visible part of the buffer,
1976 // and we're now past the bottom of the window, and we don't have any
1977 // floating objects (since they may cause wrapping to change for the rest of the
1978 // the buffer), start quick layout.
1979 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1980 forceQuickLayout
= true;
1984 // We're outside the immediately affected range, so now let's just
1985 // move everything up or down. This assumes that all the children have previously
1986 // been laid out and have wrapped line lists associated with them.
1987 // TODO: check all paragraphs before the affected range.
1989 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1993 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1996 if (child
->GetLines().GetCount() == 0)
1998 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1999 // lays out the object again using the minimum size
2000 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2001 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2003 //child->Layout(dc, availableChildRect, style);
2006 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2008 availableSpace
.y
+= child
->GetCachedSize().y
;
2009 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2010 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2011 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2014 node
= node
->GetNext();
2020 node
= node
->GetNext();
2023 node
= m_children
.GetLast();
2024 if (node
&& node
->GetData()->IsShown())
2026 wxRichTextObject
* child
= node
->GetData();
2027 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2030 maxHeight
= 0; // topMargin + bottomMargin;
2032 // Check the bottom edge of any floating object
2033 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2035 int bottom
= GetFloatCollector()->GetLastRectBottom();
2036 if (bottom
> maxHeight
)
2040 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2042 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2043 int w
= r
.GetWidth();
2045 // Convert external to content rect
2046 w
= w
- leftMargin
- rightMargin
;
2047 maxWidth
= wxMax(maxWidth
, w
);
2048 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2051 // TODO: (also in para layout) should set the
2052 // object's size to an absolute one if specified,
2053 // but if not specified, calculate it from content.
2055 // We need to add back the margins etc.
2057 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2058 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2059 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2060 SetCachedSize(marginRect
.GetSize());
2063 // The maximum size is the greatest of all maximum widths for all paragraphs.
2065 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2066 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2067 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2068 SetMaxSize(marginRect
.GetSize());
2071 // The minimum size is the greatest of all minimum widths for all paragraphs.
2073 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2074 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2075 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2076 SetMinSize(marginRect
.GetSize());
2079 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2080 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2083 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2084 if (leftOverSpace
> 0)
2086 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2088 yOffset
= (leftOverSpace
/2);
2090 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2092 yOffset
= leftOverSpace
;
2096 // Move all the children to vertically align the content
2097 // This doesn't take into account floating objects, unfortunately.
2100 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2103 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2105 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2107 node
= node
->GetNext();
2112 m_invalidRange
= wxRICHTEXT_NONE
;
2117 /// Get/set the size for the given range.
2118 bool wxRichTextParagraphLayoutBox
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2122 wxRichTextObjectList
::compatibility_iterator startPara
= wxRichTextObjectList
::compatibility_iterator();
2123 wxRichTextObjectList
::compatibility_iterator endPara
= wxRichTextObjectList
::compatibility_iterator();
2125 // First find the first paragraph whose starting position is within the range.
2126 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2129 // child is a paragraph
2130 wxRichTextObject
* child
= node
->GetData();
2131 const wxRichTextRange
& r
= child
->GetRange();
2133 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2139 node
= node
->GetNext();
2142 // Next find the last paragraph containing part of the range
2143 node
= m_children
.GetFirst();
2146 // child is a paragraph
2147 wxRichTextObject
* child
= node
->GetData();
2148 const wxRichTextRange
& r
= child
->GetRange();
2150 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2156 node
= node
->GetNext();
2159 if (!startPara
|| !endPara
)
2162 // Now we can add up the sizes
2163 for (node
= startPara
; node
; node
= node
->GetNext())
2165 // child is a paragraph
2166 wxRichTextObject
* child
= node
->GetData();
2167 const wxRichTextRange
& childRange
= child
->GetRange();
2168 wxRichTextRange rangeToFind
= range
;
2169 rangeToFind
.LimitTo(childRange
);
2171 if (child
->IsTopLevel())
2172 rangeToFind
= child
->GetOwnRange();
2176 int childDescent
= 0;
2177 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2179 descent
= wxMax(childDescent
, descent
);
2181 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2182 sz
.y
+= childSize
.y
;
2184 if (node
== endPara
)
2193 /// Get the paragraph at the given position
2194 wxRichTextParagraph
* wxRichTextParagraphLayoutBox
::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2199 // First find the first paragraph whose starting position is within the range.
2200 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2203 // child is a paragraph
2204 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2205 // wxASSERT (child != NULL);
2209 // Return first child in buffer if position is -1
2213 if (child
->GetRange().Contains(pos
))
2217 node
= node
->GetNext();
2222 /// Get the line at the given position
2223 wxRichTextLine
* wxRichTextParagraphLayoutBox
::GetLineAtPosition(long pos
, bool caretPosition
) const
2228 // First find the first paragraph whose starting position is within the range.
2229 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2232 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2233 if (obj
->GetRange().Contains(pos
))
2235 // child is a paragraph
2236 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2237 // wxASSERT (child != NULL);
2241 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2244 wxRichTextLine
* line
= node2
->GetData();
2246 wxRichTextRange range
= line
->GetAbsoluteRange();
2248 if (range
.Contains(pos
) ||
2250 // If the position is end-of-paragraph, then return the last line of
2251 // of the paragraph.
2252 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2255 node2
= node2
->GetNext();
2260 node
= node
->GetNext();
2263 int lineCount
= GetLineCount();
2265 return GetLineForVisibleLineNumber(lineCount
-1);
2270 /// Get the line at the given y pixel position, or the last line.
2271 wxRichTextLine
* wxRichTextParagraphLayoutBox
::GetLineAtYPosition(int y
) const
2273 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2276 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2277 // wxASSERT (child != NULL);
2281 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2284 wxRichTextLine
* line
= node2
->GetData();
2286 wxRect
rect(line
->GetRect());
2288 if (y
<= rect
.GetBottom())
2291 node2
= node2
->GetNext();
2295 node
= node
->GetNext();
2299 int lineCount
= GetLineCount();
2301 return GetLineForVisibleLineNumber(lineCount
-1);
2306 /// Get the number of visible lines
2307 int wxRichTextParagraphLayoutBox
::GetLineCount() const
2311 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2314 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2315 // wxASSERT (child != NULL);
2318 count
+= child
->GetLines().GetCount();
2320 node
= node
->GetNext();
2326 /// Get the paragraph for a given line
2327 wxRichTextParagraph
* wxRichTextParagraphLayoutBox
::GetParagraphForLine(wxRichTextLine
* line
) const
2329 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2332 /// Get the line size at the given position
2333 wxSize wxRichTextParagraphLayoutBox
::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2335 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2338 return line
->GetSize();
2341 return wxSize(0, 0);
2345 /// Convenience function to add a paragraph of text
2346 wxRichTextRange wxRichTextParagraphLayoutBox
::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2348 // Don't use the base style, just the default style, and the base style will
2349 // be combined at display time.
2350 // Divide into paragraph and character styles.
2352 wxRichTextAttr defaultCharStyle
;
2353 wxRichTextAttr defaultParaStyle
;
2355 // If the default style is a named paragraph style, don't apply any character formatting
2356 // to the initial text string.
2357 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2359 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2361 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2364 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2366 wxRichTextAttr
* pStyle
= paraStyle ? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2367 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2369 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2375 return para
->GetRange();
2378 /// Adds multiple paragraphs, based on newlines.
2379 wxRichTextRange wxRichTextParagraphLayoutBox
::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2381 // Don't use the base style, just the default style, and the base style will
2382 // be combined at display time.
2383 // Divide into paragraph and character styles.
2385 wxRichTextAttr defaultCharStyle
;
2386 wxRichTextAttr defaultParaStyle
;
2388 // If the default style is a named paragraph style, don't apply any character formatting
2389 // to the initial text string.
2390 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2392 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2394 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2397 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2399 wxRichTextAttr
* pStyle
= paraStyle ? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2400 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2402 wxRichTextParagraph
* firstPara
= NULL
;
2403 wxRichTextParagraph
* lastPara
= NULL
;
2405 wxRichTextRange
range(-1, -1);
2408 size_t len
= text
.length();
2410 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2419 wxChar ch
= text
[i
];
2420 if (ch
== wxT('\n') || ch
== wxT('\r'))
2424 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2425 plainText
->SetText(line
);
2427 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2432 line
= wxEmptyString
;
2443 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2444 plainText
->SetText(line
);
2449 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2452 /// Convenience function to add an image
2453 wxRichTextRange wxRichTextParagraphLayoutBox
::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2455 // Don't use the base style, just the default style, and the base style will
2456 // be combined at display time.
2457 // Divide into paragraph and character styles.
2459 wxRichTextAttr defaultCharStyle
;
2460 wxRichTextAttr defaultParaStyle
;
2462 // If the default style is a named paragraph style, don't apply any character formatting
2463 // to the initial text string.
2464 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2466 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2468 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2471 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2473 wxRichTextAttr
* pStyle
= paraStyle ? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2474 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2476 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2478 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2482 return para
->GetRange();
2486 /// Insert fragment into this box at the given position. If partialParagraph is true,
2487 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2490 bool wxRichTextParagraphLayoutBox
::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2492 // First, find the first paragraph whose starting position is within the range.
2493 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2496 wxRichTextAttr originalAttr
= para
->GetAttributes();
2498 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(para
);
2500 // Now split at this position, returning the object to insert the new
2501 // ones in front of.
2502 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2504 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2505 // text, for example, so let's optimize.
2507 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2509 // Add the first para to this para...
2510 wxRichTextObjectList
::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2514 // Iterate through the fragment paragraph inserting the content into this paragraph.
2515 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2516 wxASSERT (firstPara
!= NULL
);
2518 wxRichTextObjectList
::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2521 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2526 para
->AppendChild(newObj
);
2530 // Insert before nextObject
2531 para
->InsertChild(newObj
, nextObject
);
2534 objectNode
= objectNode
->GetNext();
2541 // Procedure for inserting a fragment consisting of a number of
2544 // 1. Remove and save the content that's after the insertion point, for adding
2545 // back once we've added the fragment.
2546 // 2. Add the content from the first fragment paragraph to the current
2548 // 3. Add remaining fragment paragraphs after the current paragraph.
2549 // 4. Add back the saved content from the first paragraph. If partialParagraph
2550 // is true, add it to the last paragraph added and not a new one.
2552 // 1. Remove and save objects after split point.
2553 wxList savedObjects
;
2555 para
->MoveToList(nextObject
, savedObjects
);
2557 // 2. Add the content from the 1st fragment paragraph.
2558 wxRichTextObjectList
::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2562 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2563 wxASSERT(firstPara
!= NULL
);
2565 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2566 para
->SetAttributes(firstPara
->GetAttributes());
2568 // Save empty paragraph attributes for appending later
2569 // These are character attributes deliberately set for a new paragraph. Without this,
2570 // we couldn't pass default attributes when appending a new paragraph.
2571 wxRichTextAttr emptyParagraphAttributes
;
2573 wxRichTextObjectList
::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2575 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2576 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2580 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2583 para
->AppendChild(newObj
);
2585 objectNode
= objectNode
->GetNext();
2588 // 3. Add remaining fragment paragraphs after the current paragraph.
2589 wxRichTextObjectList
::compatibility_iterator nextParagraphNode
= node
->GetNext();
2590 wxRichTextObject
* nextParagraph
= NULL
;
2591 if (nextParagraphNode
)
2592 nextParagraph
= nextParagraphNode
->GetData();
2594 wxRichTextObjectList
::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2595 wxRichTextParagraph
* finalPara
= para
;
2597 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2599 // If there was only one paragraph, we need to insert a new one.
2602 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2603 wxASSERT( para
!= NULL
);
2605 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2608 InsertChild(finalPara
, nextParagraph
);
2610 AppendChild(finalPara
);
2615 // If there was only one paragraph, or we have full paragraphs in our fragment,
2616 // we need to insert a new one.
2619 finalPara
= new wxRichTextParagraph
;
2622 InsertChild(finalPara
, nextParagraph
);
2624 AppendChild(finalPara
);
2627 // 4. Add back the remaining content.
2631 finalPara
->MoveFromList(savedObjects
);
2633 // Ensure there's at least one object
2634 if (finalPara
->GetChildCount() == 0)
2636 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2637 text
->SetAttributes(emptyParagraphAttributes
);
2639 finalPara
->AppendChild(text
);
2643 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2644 finalPara
->SetAttributes(firstPara
->GetAttributes());
2645 else if (finalPara
&& finalPara
!= para
)
2646 finalPara
->SetAttributes(originalAttr
);
2654 wxRichTextObjectList
::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2657 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2658 wxASSERT( para
!= NULL
);
2660 AppendChild(para
->Clone());
2669 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2671 bool wxRichTextParagraphLayoutBox
::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2673 wxRichTextObjectList
::compatibility_iterator i
= GetChildren().GetFirst();
2676 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2677 wxASSERT( para
!= NULL
);
2679 if (!para
->GetRange().IsOutside(range
))
2681 fragment
.AppendChild(para
->Clone());
2686 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687 if (!fragment
.IsEmpty())
2689 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2690 wxASSERT( firstPara
!= NULL
);
2692 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2693 wxASSERT( lastPara
!= NULL
);
2695 if (!firstPara
|| !lastPara
)
2698 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2700 long firstPos
= firstPara
->GetRange().GetStart();
2702 // Adjust for renumbering from zero
2703 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2706 fragment
.CalculateRange(0, end
);
2708 // Chop off the start of the paragraph
2709 if (topTailRange
.GetStart() > 0)
2711 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2712 firstPara
->DeleteRange(r
);
2714 // Make sure the numbering is correct
2715 fragment
.CalculateRange(0, end
);
2717 // Now, we've deleted some positions, so adjust the range
2719 topTailRange
.SetStart(range
.GetLength());
2720 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2724 topTailRange
.SetStart(range
.GetLength());
2725 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2728 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2730 lastPara
->DeleteRange(topTailRange
);
2732 // Make sure the numbering is correct
2734 fragment
.CalculateRange(0, end
);
2736 // We only have part of a paragraph at the end
2737 fragment
.SetPartialParagraph(true);
2741 // We have a partial paragraph (don't save last new paragraph marker)
2742 // or complete paragraph
2743 fragment
.SetPartialParagraph(isFragment
);
2750 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2751 /// starting from zero at the start of the buffer.
2752 long wxRichTextParagraphLayoutBox
::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2759 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2762 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2763 // wxASSERT( child != NULL );
2767 if (child
->GetRange().Contains(pos
))
2769 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2772 wxRichTextLine
* line
= node2
->GetData();
2773 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2775 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2777 // If the caret is displayed at the end of the previous wrapped line,
2778 // we want to return the line it's _displayed_ at (not the actual line
2779 // containing the position).
2780 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2781 return lineCount
- 1;
2788 node2
= node2
->GetNext();
2790 // If we didn't find it in the lines, it must be
2791 // the last position of the paragraph. So return the last line.
2795 lineCount
+= child
->GetLines().GetCount();
2798 node
= node
->GetNext();
2805 /// Given a line number, get the corresponding wxRichTextLine object.
2806 wxRichTextLine
* wxRichTextParagraphLayoutBox
::GetLineForVisibleLineNumber(long lineNumber
) const
2810 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2813 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2814 // wxASSERT(child != NULL);
2818 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2820 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2823 wxRichTextLine
* line
= node2
->GetData();
2825 if (lineCount
== lineNumber
)
2830 node2
= node2
->GetNext();
2834 lineCount
+= child
->GetLines().GetCount();
2837 node
= node
->GetNext();
2844 /// Delete range from layout.
2845 bool wxRichTextParagraphLayoutBox
::DeleteRange(const wxRichTextRange
& range
)
2847 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2849 wxRichTextParagraph
* firstPara
= NULL
;
2852 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2853 // wxASSERT (obj != NULL);
2855 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
2859 // Delete the range in each paragraph
2861 if (!obj
->GetRange().IsOutside(range
))
2863 // Deletes the content of this object within the given range
2864 obj
->DeleteRange(range
);
2866 wxRichTextRange thisRange
= obj
->GetRange();
2867 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2869 // If the whole paragraph is within the range to delete,
2870 // delete the whole thing.
2871 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2873 // Delete the whole object
2874 RemoveChild(obj
, true);
2877 else if (!firstPara
)
2880 // If the range includes the paragraph end, we need to join this
2881 // and the next paragraph.
2882 if (range
.GetEnd() <= thisRange
.GetEnd())
2884 // We need to move the objects from the next paragraph
2885 // to this paragraph
2887 wxRichTextParagraph
* nextParagraph
= NULL
;
2888 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2889 nextParagraph
= obj
;
2892 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2894 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2897 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2899 wxRichTextAttr nextParaAttr
;
2900 if (applyFinalParagraphStyle
)
2902 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903 // not the next one.
2904 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2905 nextParaAttr
= thisAttr
;
2907 nextParaAttr
= nextParagraph
->GetAttributes();
2910 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2912 // Move the objects to the previous para
2913 wxRichTextObjectList
::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2917 wxRichTextObject
* obj1
= node1
->GetData();
2919 firstPara
->AppendChild(obj1
);
2921 wxRichTextObjectList
::compatibility_iterator next1
= node1
->GetNext();
2922 nextParagraph
->GetChildren().Erase(node1
);
2927 // Delete the paragraph
2928 RemoveChild(nextParagraph
, true);
2931 // Avoid empty paragraphs
2932 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2934 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2935 firstPara
->AppendChild(text
);
2938 if (applyFinalParagraphStyle
)
2939 firstPara
->SetAttributes(nextParaAttr
);
2952 /// Get any text in this object for the given range
2953 wxString wxRichTextParagraphLayoutBox
::GetTextForRange(const wxRichTextRange
& range
) const
2957 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2960 wxRichTextObject
* child
= node
->GetData();
2961 if (!child
->GetRange().IsOutside(range
))
2963 wxRichTextRange childRange
= range
;
2964 childRange
.LimitTo(child
->GetRange());
2966 wxString childText
= child
->GetTextForRange(childRange
);
2970 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2975 node
= node
->GetNext();
2981 /// Get all the text
2982 wxString wxRichTextParagraphLayoutBox
::GetText() const
2984 return GetTextForRange(GetOwnRange());
2987 /// Get the paragraph by number
2988 wxRichTextParagraph
* wxRichTextParagraphLayoutBox
::GetParagraphAtLine(long paragraphNumber
) const
2990 if ((size_t) paragraphNumber
>= GetChildCount())
2993 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2996 /// Get the length of the paragraph
2997 int wxRichTextParagraphLayoutBox
::GetParagraphLength(long paragraphNumber
) const
2999 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3001 return para
->GetRange().GetLength() - 1; // don't include newline
3006 /// Get the text of the paragraph
3007 wxString wxRichTextParagraphLayoutBox
::GetParagraphText(long paragraphNumber
) const
3009 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3011 return para
->GetTextForRange(para
->GetRange());
3013 return wxEmptyString
;
3016 /// Convert zero-based line column and paragraph number to a position.
3017 long wxRichTextParagraphLayoutBox
::XYToPosition(long x
, long y
) const
3019 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3022 return para
->GetRange().GetStart() + x
;
3028 /// Convert zero-based position to line column and paragraph number
3029 bool wxRichTextParagraphLayoutBox
::PositionToXY(long pos
, long* x
, long* y
) const
3031 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3035 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3038 wxRichTextObject
* child
= node
->GetData();
3042 node
= node
->GetNext();
3046 *x
= pos
- para
->GetRange().GetStart();
3054 /// Get the leaf object in a paragraph at this position.
3055 /// Given a line number, get the corresponding wxRichTextLine object.
3056 wxRichTextObject
* wxRichTextParagraphLayoutBox
::GetLeafObjectAtPosition(long position
) const
3058 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3061 wxRichTextObjectList
::compatibility_iterator node
= para
->GetChildren().GetFirst();
3065 wxRichTextObject
* child
= node
->GetData();
3066 if (child
->GetRange().Contains(position
))
3069 node
= node
->GetNext();
3071 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3072 return para
->GetChildren().GetLast()->GetData();
3077 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3078 bool wxRichTextParagraphLayoutBox
::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3080 bool characterStyle
= false;
3081 bool paragraphStyle
= false;
3083 if (style
.IsCharacterStyle())
3084 characterStyle
= true;
3085 if (style
.IsParagraphStyle())
3086 paragraphStyle
= true;
3088 wxRichTextBuffer
* buffer
= GetBuffer();
3090 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3091 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3092 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3093 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3094 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3095 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3097 // Apply paragraph style first, if any
3098 wxRichTextAttr
wholeStyle(style
);
3100 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3102 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3104 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3107 // Limit the attributes to be set to the content to only character attributes.
3108 wxRichTextAttr
characterAttributes(wholeStyle
);
3109 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3111 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3113 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3115 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3118 // If we are associated with a control, make undoable; otherwise, apply immediately
3121 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3123 wxRichTextAction
* action
= NULL
;
3125 if (haveControl
&& withUndo
)
3127 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3128 action
->SetRange(range
);
3129 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3132 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3135 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3136 // wxASSERT (para != NULL);
3138 if (para
&& para
->GetChildCount() > 0)
3140 // Stop searching if we're beyond the range of interest
3141 if (para
->GetRange().GetStart() > range
.GetEnd())
3144 if (!para
->GetRange().IsOutside(range
))
3146 // We'll be using a copy of the paragraph to make style changes,
3147 // not updating the buffer directly.
3148 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3150 if (haveControl
&& withUndo
)
3152 newPara
= new wxRichTextParagraph(*para
);
3153 action
->GetNewParagraphs().AppendChild(newPara
);
3155 // Also store the old ones for Undo
3156 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3161 // If we're specifying paragraphs only, then we really mean character formatting
3162 // to be included in the paragraph style
3163 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3167 // Removes the given style from the paragraph
3168 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3170 else if (resetExistingStyle
)
3171 newPara
->GetAttributes() = wholeStyle
;
3176 // Only apply attributes that will make a difference to the combined
3177 // style as seen on the display
3178 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3179 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3182 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3186 // When applying paragraph styles dynamically, don't change the text objects' attributes
3187 // since they will computed as needed. Only apply the character styling if it's _only_
3188 // character styling. This policy is subject to change and might be put under user control.
3190 // Hm. we might well be applying a mix of paragraph and character styles, in which
3191 // case we _do_ want to apply character styles regardless of what para styles are set.
3192 // But if we're applying a paragraph style, which has some character attributes, but
3193 // we only want the paragraphs to hold this character style, then we _don't_ want to
3194 // apply the character style. So we need to be able to choose.
3196 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3198 wxRichTextRange
childRange(range
);
3199 childRange
.LimitTo(newPara
->GetRange());
3201 // Find the starting position and if necessary split it so
3202 // we can start applying a different style.
3203 // TODO: check that the style actually changes or is different
3204 // from style outside of range
3205 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3206 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3208 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3209 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3211 firstObject
= newPara
->SplitAt(range
.GetStart());
3213 // Increment by 1 because we're apply the style one _after_ the split point
3214 long splitPoint
= childRange
.GetEnd();
3215 if (splitPoint
!= newPara
->GetRange().GetEnd())
3219 if (splitPoint
== newPara
->GetRange().GetEnd())
3220 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3222 // lastObject is set as a side-effect of splitting. It's
3223 // returned as the object before the new object.
3224 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3226 wxASSERT(firstObject
!= NULL
);
3227 wxASSERT(lastObject
!= NULL
);
3229 if (!firstObject
|| !lastObject
)
3232 wxRichTextObjectList
::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3233 wxRichTextObjectList
::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3235 wxASSERT(firstNode
);
3238 wxRichTextObjectList
::compatibility_iterator node2
= firstNode
;
3242 wxRichTextObject
* child
= node2
->GetData();
3246 // Removes the given style from the paragraph
3247 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3249 else if (resetExistingStyle
)
3250 child
->GetAttributes() = characterAttributes
;
3255 // Only apply attributes that will make a difference to the combined
3256 // style as seen on the display
3257 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3258 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3261 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3264 if (node2
== lastNode
)
3267 node2
= node2
->GetNext();
3273 node
= node
->GetNext();
3276 // Do action, or delay it until end of batch.
3277 if (haveControl
&& withUndo
)
3278 buffer
->SubmitAction(action
);
3283 // Just change the attributes for this single object.
3284 void wxRichTextParagraphLayoutBox
::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3286 wxRichTextBuffer
* buffer
= GetBuffer();
3287 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3288 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3289 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3291 wxRichTextAction
*action
= NULL
;
3292 wxRichTextAttr newAttr
= obj
->GetAttributes();
3293 if (resetExistingStyle
)
3296 newAttr
.Apply(textAttr
);
3298 if (haveControl
&& withUndo
)
3300 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3301 action
->SetRange(obj
->GetRange().FromInternal());
3302 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3303 action
->MakeObject(obj
);
3305 action
->GetAttributes() = newAttr
;
3308 obj
->GetAttributes() = newAttr
;
3310 if (haveControl
&& withUndo
)
3311 buffer
->SubmitAction(action
);
3314 /// Get the text attributes for this position.
3315 bool wxRichTextParagraphLayoutBox
::GetStyle(long position
, wxRichTextAttr
& style
)
3317 return DoGetStyle(position
, style
, true);
3320 bool wxRichTextParagraphLayoutBox
::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3322 return DoGetStyle(position
, style
, false);
3325 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326 /// context attributes.
3327 bool wxRichTextParagraphLayoutBox
::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3329 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3331 if (style
.IsParagraphStyle())
3333 obj
= GetParagraphAtPosition(position
);
3338 // Start with the base style
3339 style
= GetAttributes();
3341 // Apply the paragraph style
3342 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3345 style
= obj
->GetAttributes();
3352 obj
= GetLeafObjectAtPosition(position
);
3357 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3358 style
= para ? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3361 style
= obj
->GetAttributes();
3369 static bool wxHasStyle(long flags
, long style
)
3371 return (flags
& style
) != 0;
3374 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3376 bool wxRichTextParagraphLayoutBox
::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3378 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3383 /// Get the combined style for a range - if any attribute is different within the range,
3384 /// that attribute is not present within the flags.
3385 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3387 bool wxRichTextParagraphLayoutBox
::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3389 style
= wxRichTextAttr();
3391 wxRichTextAttr clashingAttr
;
3392 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3394 wxRichTextObjectList
::compatibility_iterator node
= GetChildren().GetFirst();
3397 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3398 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3400 if (para
->GetChildren().GetCount() == 0)
3402 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3404 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3408 wxRichTextRange
paraRange(para
->GetRange());
3409 paraRange
.LimitTo(range
);
3411 // First collect paragraph attributes only
3412 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3413 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3414 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3416 wxRichTextObjectList
::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3420 wxRichTextObject
* child
= childNode
->GetData();
3421 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3423 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3425 // Now collect character attributes only
3426 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3428 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3431 childNode
= childNode
->GetNext();
3435 node
= node
->GetNext();
3440 /// Set default style
3441 bool wxRichTextParagraphLayoutBox
::SetDefaultStyle(const wxRichTextAttr
& style
)
3443 m_defaultAttributes
= style
;
3447 /// Test if this whole range has character attributes of the specified kind. If any
3448 /// of the attributes are different within the range, the test fails. You
3449 /// can use this to implement, for example, bold button updating. style must have
3450 /// flags indicating which attributes are of interest.
3451 bool wxRichTextParagraphLayoutBox
::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3454 int matchingCount
= 0;
3456 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3459 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3460 // wxASSERT (para != NULL);
3464 // Stop searching if we're beyond the range of interest
3465 if (para
->GetRange().GetStart() > range
.GetEnd())
3466 return foundCount
== matchingCount
&& foundCount
!= 0;
3468 if (!para
->GetRange().IsOutside(range
))
3470 wxRichTextObjectList
::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3474 wxRichTextObject
* child
= node2
->GetData();
3475 // Allow for empty string if no buffer
3476 wxRichTextRange childRange
= child
->GetRange();
3477 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3478 childRange
.SetEnd(childRange
.GetEnd()+1);
3480 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3483 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3485 if (wxTextAttrEqPartial(textAttr
, style
))
3489 node2
= node2
->GetNext();
3494 node
= node
->GetNext();
3497 return foundCount
== matchingCount
&& foundCount
!= 0;
3500 /// Test if this whole range has paragraph attributes of the specified kind. If any
3501 /// of the attributes are different within the range, the test fails. You
3502 /// can use this to implement, for example, centering button updating. style must have
3503 /// flags indicating which attributes are of interest.
3504 bool wxRichTextParagraphLayoutBox
::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3507 int matchingCount
= 0;
3509 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3512 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3513 // wxASSERT (para != NULL);
3517 // Stop searching if we're beyond the range of interest
3518 if (para
->GetRange().GetStart() > range
.GetEnd())
3519 return foundCount
== matchingCount
&& foundCount
!= 0;
3521 if (!para
->GetRange().IsOutside(range
))
3523 wxRichTextAttr textAttr
= GetAttributes();
3524 // Apply the paragraph style
3525 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3528 if (wxTextAttrEqPartial(textAttr
, style
))
3533 node
= node
->GetNext();
3535 return foundCount
== matchingCount
&& foundCount
!= 0;
3538 void wxRichTextParagraphLayoutBox
::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3540 wxRichTextBuffer
* buffer
= GetBuffer();
3541 if (buffer
&& buffer
->GetRichTextCtrl())
3542 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3546 /// Set character or paragraph properties
3547 bool wxRichTextParagraphLayoutBox
::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3549 wxRichTextBuffer
* buffer
= GetBuffer();
3551 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3552 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3553 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3554 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3555 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3557 // If we are associated with a control, make undoable; otherwise, apply immediately
3560 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3562 wxRichTextAction
* action
= NULL
;
3564 if (haveControl
&& withUndo
)
3566 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3567 action
->SetRange(range
);
3568 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3571 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3574 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3575 // wxASSERT (para != NULL);
3577 if (para
&& para
->GetChildCount() > 0)
3579 // Stop searching if we're beyond the range of interest
3580 if (para
->GetRange().GetStart() > range
.GetEnd())
3583 if (!para
->GetRange().IsOutside(range
))
3585 // We'll be using a copy of the paragraph to make style changes,
3586 // not updating the buffer directly.
3587 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3589 if (haveControl
&& withUndo
)
3591 newPara
= new wxRichTextParagraph(*para
);
3592 action
->GetNewParagraphs().AppendChild(newPara
);
3594 // Also store the old ones for Undo
3595 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3602 if (removeProperties
)
3604 // Removes the given style from the paragraph
3606 newPara
->GetProperties().RemoveProperties(properties
);
3608 else if (resetExistingProperties
)
3609 newPara
->GetProperties() = properties
;
3611 newPara
->GetProperties().MergeProperties(properties
);
3614 // When applying paragraph styles dynamically, don't change the text objects' attributes
3615 // since they will computed as needed. Only apply the character styling if it's _only_
3616 // character styling. This policy is subject to change and might be put under user control.
3618 // Hm. we might well be applying a mix of paragraph and character styles, in which
3619 // case we _do_ want to apply character styles regardless of what para styles are set.
3620 // But if we're applying a paragraph style, which has some character attributes, but
3621 // we only want the paragraphs to hold this character style, then we _don't_ want to
3622 // apply the character style. So we need to be able to choose.
3624 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3626 wxRichTextRange
childRange(range
);
3627 childRange
.LimitTo(newPara
->GetRange());
3629 // Find the starting position and if necessary split it so
3630 // we can start applying different properties.
3631 // TODO: check that the properties actually change or are different
3632 // from properties outside of range
3633 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3634 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3636 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3637 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3639 firstObject
= newPara
->SplitAt(range
.GetStart());
3641 // Increment by 1 because we're apply the style one _after_ the split point
3642 long splitPoint
= childRange
.GetEnd();
3643 if (splitPoint
!= newPara
->GetRange().GetEnd())
3647 if (splitPoint
== newPara
->GetRange().GetEnd())
3648 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3650 // lastObject is set as a side-effect of splitting. It's
3651 // returned as the object before the new object.
3652 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3654 wxASSERT(firstObject
!= NULL
);
3655 wxASSERT(lastObject
!= NULL
);
3657 if (!firstObject
|| !lastObject
)
3660 wxRichTextObjectList
::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3661 wxRichTextObjectList
::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3663 wxASSERT(firstNode
);
3666 wxRichTextObjectList
::compatibility_iterator node2
= firstNode
;
3670 wxRichTextObject
* child
= node2
->GetData();
3672 if (removeProperties
)
3674 // Removes the given properties from the paragraph
3675 child
->GetProperties().RemoveProperties(properties
);
3677 else if (resetExistingProperties
)
3678 child
->GetProperties() = properties
;
3681 child
->GetProperties().MergeProperties(properties
);
3684 if (node2
== lastNode
)
3687 node2
= node2
->GetNext();
3693 node
= node
->GetNext();
3696 // Do action, or delay it until end of batch.
3697 if (haveControl
&& withUndo
)
3698 buffer
->SubmitAction(action
);
3703 void wxRichTextParagraphLayoutBox
::Reset()
3707 wxRichTextBuffer
* buffer
= GetBuffer();
3708 if (buffer
&& buffer
->GetRichTextCtrl())
3710 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3711 event
.SetEventObject(buffer
->GetRichTextCtrl());
3712 event
.SetContainer(this);
3714 buffer
->SendEvent(event
, true);
3717 AddParagraph(wxEmptyString
);
3719 PrepareContent(*this);
3721 InvalidateHierarchy(wxRICHTEXT_ALL
);
3724 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3725 void wxRichTextParagraphLayoutBox
::Invalidate(const wxRichTextRange
& invalidRange
)
3727 wxRichTextCompositeObject
::Invalidate(invalidRange
);
3729 DoInvalidate(invalidRange
);
3732 // Do the (in)validation for this object only
3733 void wxRichTextParagraphLayoutBox
::DoInvalidate(const wxRichTextRange
& invalidRange
)
3735 if (invalidRange
== wxRICHTEXT_ALL
)
3737 m_invalidRange
= wxRICHTEXT_ALL
;
3739 // Already invalidating everything
3740 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3745 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3746 m_invalidRange
.SetStart(invalidRange
.GetStart());
3747 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3748 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3752 // Do the (in)validation both up and down the hierarchy
3753 void wxRichTextParagraphLayoutBox
::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3755 Invalidate(invalidRange
);
3757 if (invalidRange
!= wxRICHTEXT_NONE
)
3759 // Now go up the hierarchy
3760 wxRichTextObject
* thisObj
= this;
3761 wxRichTextObject
* p
= GetParent();
3764 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3766 l
->DoInvalidate(thisObj
->GetRange());
3774 /// Get invalid range, rounding to entire paragraphs if argument is true.
3775 wxRichTextRange wxRichTextParagraphLayoutBox
::GetInvalidRange(bool wholeParagraphs
) const
3777 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3778 return m_invalidRange
;
3780 wxRichTextRange range
= m_invalidRange
;
3782 if (wholeParagraphs
)
3784 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3786 range
.SetStart(para1
->GetRange().GetStart());
3787 // floating layout make all child should be relayout
3788 range
.SetEnd(GetOwnRange().GetEnd());
3793 /// Apply the style sheet to the buffer, for example if the styles have changed.
3794 bool wxRichTextParagraphLayoutBox
::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3796 wxASSERT(styleSheet
!= NULL
);
3802 wxRichTextAttr
attr(GetBasicStyle());
3803 if (GetBasicStyle().HasParagraphStyleName())
3805 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3808 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3809 SetBasicStyle(attr
);
3814 if (GetBasicStyle().HasCharacterStyleName())
3816 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3819 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3820 SetBasicStyle(attr
);
3825 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3828 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3829 // wxASSERT (para != NULL);
3833 // Combine paragraph and list styles. If there is a list style in the original attributes,
3834 // the current indentation overrides anything else and is used to find the item indentation.
3835 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837 // exception as above).
3838 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839 // So when changing a list style interactively, could retrieve level based on current style, then
3840 // set appropriate indent and apply new style.
3844 if (para
->GetAttributes().HasOutlineLevel())
3845 outline
= para
->GetAttributes().GetOutlineLevel();
3846 if (para
->GetAttributes().HasBulletNumber())
3847 num
= para
->GetAttributes().GetBulletNumber();
3849 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3851 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3853 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3854 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3855 if (paraDef
&& !listDef
)
3857 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3860 else if (listDef
&& !paraDef
)
3862 // Set overall style defined for the list style definition
3863 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3865 // Apply the style for this level
3866 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3869 else if (listDef
&& paraDef
)
3871 // Combines overall list style, style for level, and paragraph style
3872 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3876 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3878 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3880 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3882 // Overall list definition style
3883 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3885 // Style for this level
3886 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3890 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3892 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3895 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3901 para
->GetAttributes().SetOutlineLevel(outline
);
3903 para
->GetAttributes().SetBulletNumber(num
);
3906 node
= node
->GetNext();
3908 return foundCount
!= 0;
3912 bool wxRichTextParagraphLayoutBox
::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3914 wxRichTextBuffer
* buffer
= GetBuffer();
3915 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3917 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3918 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3920 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3922 // Current number, if numbering
3925 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3927 // If we are associated with a control, make undoable; otherwise, apply immediately
3930 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3932 wxRichTextAction
* action
= NULL
;
3934 if (haveControl
&& withUndo
)
3936 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3937 action
->SetRange(range
);
3938 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3941 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3944 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3945 // wxASSERT (para != NULL);
3947 if (para
&& para
->GetChildCount() > 0)
3949 // Stop searching if we're beyond the range of interest
3950 if (para
->GetRange().GetStart() > range
.GetEnd())
3953 if (!para
->GetRange().IsOutside(range
))
3955 // We'll be using a copy of the paragraph to make style changes,
3956 // not updating the buffer directly.
3957 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3959 if (haveControl
&& withUndo
)
3961 newPara
= new wxRichTextParagraph(*para
);
3962 action
->GetNewParagraphs().AppendChild(newPara
);
3964 // Also store the old ones for Undo
3965 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3972 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3973 int thisLevel
= specifyLevel ? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3975 // How is numbering going to work?
3976 // If we are renumbering, or numbering for the first time, we need to keep
3977 // track of the number for each level. But we might be simply applying a different
3979 // In Word, applying a style to several paragraphs, even if at different levels,
3980 // reverts the level back to the same one. So we could do the same here.
3981 // Renumbering will need to be done when we promote/demote a paragraph.
3983 // Apply the overall list style, and item style for this level
3984 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3985 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3987 // Now we need to do numbering
3990 newPara
->GetAttributes().SetBulletNumber(n
);
3995 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3997 // if def is NULL, remove list style, applying any associated paragraph style
3998 // to restore the attributes
4000 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4001 newPara
->GetAttributes().SetLeftIndent(0, 0);
4002 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4004 // Eliminate the main list-related attributes
4005 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
);
4007 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4009 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4012 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4019 node
= node
->GetNext();
4022 // Do action, or delay it until end of batch.
4023 if (haveControl
&& withUndo
)
4024 buffer
->SubmitAction(action
);
4029 bool wxRichTextParagraphLayoutBox
::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4031 wxRichTextBuffer
* buffer
= GetBuffer();
4032 if (buffer
&& buffer
->GetStyleSheet())
4034 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4036 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4041 /// Clear list for given range
4042 bool wxRichTextParagraphLayoutBox
::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4044 return SetListStyle(range
, NULL
, flags
);
4047 /// Number/renumber any list elements in the given range
4048 bool wxRichTextParagraphLayoutBox
::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4050 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4053 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4054 bool wxRichTextParagraphLayoutBox
::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4055 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4057 wxRichTextBuffer
* buffer
= GetBuffer();
4058 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4060 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4061 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4063 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4066 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4068 // Max number of levels
4069 const int maxLevels
= 10;
4071 // The level we're looking at now
4072 int currentLevel
= -1;
4074 // The item number for each level
4075 int levels
[maxLevels
];
4078 // Reset all numbering
4079 for (i
= 0; i
< maxLevels
; i
++)
4081 if (startFrom
!= -1)
4082 levels
[i
] = startFrom
-1;
4083 else if (renumber
) // start again
4086 levels
[i
] = -1; // start from the number we found, if any
4090 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4093 // If we are associated with a control, make undoable; otherwise, apply immediately
4096 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4098 wxRichTextAction
* action
= NULL
;
4100 if (haveControl
&& withUndo
)
4102 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4103 action
->SetRange(range
);
4104 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4107 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
4110 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4111 // wxASSERT (para != NULL);
4113 if (para
&& para
->GetChildCount() > 0)
4115 // Stop searching if we're beyond the range of interest
4116 if (para
->GetRange().GetStart() > range
.GetEnd())
4119 if (!para
->GetRange().IsOutside(range
))
4121 // We'll be using a copy of the paragraph to make style changes,
4122 // not updating the buffer directly.
4123 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4125 if (haveControl
&& withUndo
)
4127 newPara
= new wxRichTextParagraph(*para
);
4128 action
->GetNewParagraphs().AppendChild(newPara
);
4130 // Also store the old ones for Undo
4131 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4136 wxRichTextListStyleDefinition
* defToUse
= def
;
4139 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4140 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4145 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4146 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4148 // If we've specified a level to apply to all, change the level.
4149 if (specifiedLevel
!= -1)
4150 thisLevel
= specifiedLevel
;
4152 // Do promotion if specified
4153 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4155 thisLevel
= thisLevel
- promoteBy
;
4162 // Apply the overall list style, and item style for this level
4163 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4164 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4166 // OK, we've (re)applied the style, now let's get the numbering right.
4168 if (currentLevel
== -1)
4169 currentLevel
= thisLevel
;
4171 // Same level as before, do nothing except increment level's number afterwards
4172 if (currentLevel
== thisLevel
)
4175 // A deeper level: start renumbering all levels after current level
4176 else if (thisLevel
> currentLevel
)
4178 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4182 currentLevel
= thisLevel
;
4184 else if (thisLevel
< currentLevel
)
4186 currentLevel
= thisLevel
;
4189 // Use the current numbering if -1 and we have a bullet number already
4190 if (levels
[currentLevel
] == -1)
4192 if (newPara
->GetAttributes().HasBulletNumber())
4193 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4195 levels
[currentLevel
] = 1;
4199 levels
[currentLevel
] ++;
4202 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4204 // Create the bullet text if an outline list
4205 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4208 for (i
= 0; i
<= currentLevel
; i
++)
4210 if (!text
.IsEmpty())
4212 text
+= wxString
::Format(wxT("%d"), levels
[i
]);
4214 newPara
->GetAttributes().SetBulletText(text
);
4220 node
= node
->GetNext();
4223 // Do action, or delay it until end of batch.
4224 if (haveControl
&& withUndo
)
4225 buffer
->SubmitAction(action
);
4230 bool wxRichTextParagraphLayoutBox
::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4232 wxRichTextBuffer
* buffer
= GetBuffer();
4233 if (buffer
->GetStyleSheet())
4235 wxRichTextListStyleDefinition
* def
= NULL
;
4236 if (!defName
.IsEmpty())
4237 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4238 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4243 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4244 bool wxRichTextParagraphLayoutBox
::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4247 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4248 // to NumberList with a flag indicating promotion is required within one of the ranges.
4249 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4250 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4251 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4252 // list position will start from 1.
4253 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4254 // We can end the renumbering at this point.
4256 // For now, only renumber within the promotion range.
4258 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4261 bool wxRichTextParagraphLayoutBox
::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4263 wxRichTextBuffer
* buffer
= GetBuffer();
4264 if (buffer
->GetStyleSheet())
4266 wxRichTextListStyleDefinition
* def
= NULL
;
4267 if (!defName
.IsEmpty())
4268 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4269 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4274 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4275 /// position of the paragraph that it had to start looking from.
4276 bool wxRichTextParagraphLayoutBox
::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4278 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4281 wxRichTextBuffer
* buffer
= GetBuffer();
4282 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4283 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4285 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4288 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4289 // int thisLevel = def->FindLevelForIndent(thisIndent);
4291 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4293 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4294 if (previousParagraph
->GetAttributes().HasBulletName())
4295 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4296 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4297 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4299 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4300 attr
.SetBulletNumber(nextNumber
);
4304 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4305 if (!text
.IsEmpty())
4307 int pos
= text
.Find(wxT('.'), true);
4308 if (pos
!= wxNOT_FOUND
)
4310 text
= text
.Mid(0, text
.Length() - pos
- 1);
4313 text
= wxEmptyString
;
4314 if (!text
.IsEmpty())
4316 text
+= wxString
::Format(wxT("%d"), nextNumber
);
4317 attr
.SetBulletText(text
);
4331 * wxRichTextParagraph
4332 * This object represents a single paragraph (or in a straight text editor, a line).
4335 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4337 wxArrayInt wxRichTextParagraph
::sm_defaultTabs
;
4339 wxRichTextParagraph
::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4340 wxRichTextCompositeObject(parent
)
4343 SetAttributes(*style
);
4346 wxRichTextParagraph
::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4347 wxRichTextCompositeObject(parent
)
4350 SetAttributes(*paraStyle
);
4352 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4355 wxRichTextParagraph
::~wxRichTextParagraph()
4361 bool wxRichTextParagraph
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4366 // Currently we don't merge these attributes with the parent, but we
4367 // should consider whether we should (e.g. if we set a border colour
4368 // for all paragraphs). But generally box attributes are likely to be
4369 // different for different objects.
4370 wxRect paraRect
= GetRect();
4371 wxRichTextAttr attr
= GetCombinedAttributes();
4372 context
.ApplyVirtualAttributes(attr
, this);
4374 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4376 // Draw the bullet, if any
4377 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4379 if (attr
.GetLeftSubIndent() != 0)
4381 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4382 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4384 wxRichTextAttr
bulletAttr(attr
);
4386 // Combine with the font of the first piece of content, if one is specified
4387 if (GetChildren().GetCount() > 0)
4389 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4390 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4392 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4396 // Get line height from first line, if any
4397 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ?
(wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4400 int lineHeight
wxDUMMY_INITIALIZE(0);
4403 lineHeight
= line
->GetSize().y
;
4404 linePos
= line
->GetPosition() + GetPosition();
4409 if (bulletAttr
.HasFont() && GetBuffer())
4410 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4412 font
= (*wxNORMAL_FONT
);
4414 wxCheckSetFont(dc
, font
);
4416 lineHeight
= dc
.GetCharHeight();
4417 linePos
= GetPosition();
4418 linePos
.y
+= spaceBeforePara
;
4421 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4423 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4425 if (wxRichTextBuffer
::GetRenderer())
4426 wxRichTextBuffer
::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4428 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4430 if (wxRichTextBuffer
::GetRenderer())
4431 wxRichTextBuffer
::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4435 wxString bulletText
= GetBulletText();
4437 if (!bulletText
.empty() && wxRichTextBuffer
::GetRenderer())
4438 wxRichTextBuffer
::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4443 // Draw the range for each line, one object at a time.
4445 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
4448 wxRichTextLine
* line
= node
->GetData();
4449 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4451 // Lines are specified relative to the paragraph
4453 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4455 // Don't draw if off the screen
4456 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4458 wxPoint objectPosition
= linePosition
;
4459 int maxDescent
= line
->GetDescent();
4461 // Loop through objects until we get to the one within range
4462 wxRichTextObjectList
::compatibility_iterator node2
= m_children
.GetFirst();
4467 wxRichTextObject
* child
= node2
->GetData();
4469 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4471 // Draw this part of the line at the correct position
4472 wxRichTextRange
objectRange(child
->GetRange());
4473 objectRange
.LimitTo(lineRange
);
4476 if (child
->IsTopLevel())
4478 objectSize
= child
->GetCachedSize();
4479 objectRange
= child
->GetOwnRange();
4483 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4484 if (i
< (int) line
->GetObjectSizes().GetCount())
4486 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4492 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4496 // Use the child object's width, but the whole line's height
4497 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4498 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4500 objectPosition
.x
+= objectSize
.x
;
4503 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4504 // Can break out of inner loop now since we've passed this line's range
4507 node2
= node2
->GetNext();
4511 node
= node
->GetNext();
4517 // Get the range width using partial extents calculated for the whole paragraph.
4518 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4520 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4522 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4525 int leftMostPos
= 0;
4526 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4527 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4529 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4531 int w
= rightMostPos
- leftMostPos
;
4536 /// Lay the item out
4537 bool wxRichTextParagraph
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4539 // Deal with floating objects firstly before the normal layout
4540 wxRichTextBuffer
* buffer
= GetBuffer();
4542 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4543 wxASSERT(collector
);
4544 LayoutFloat(dc
, context
, rect
, style
, collector
);
4546 wxRichTextAttr attr
= GetCombinedAttributes();
4547 context
.ApplyVirtualAttributes(attr
, this);
4551 // Increase the size of the paragraph due to spacing
4552 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4553 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4554 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4555 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4556 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4558 int lineSpacing
= 0;
4560 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4561 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4563 wxCheckSetFont(dc
, attr
.GetFont());
4564 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4567 // Start position for each line relative to the paragraph
4568 int startPositionFirstLine
= leftIndent
;
4569 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4571 // If we have a bullet in this paragraph, the start position for the first line's text
4572 // is actually leftIndent + leftSubIndent.
4573 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4574 startPositionFirstLine
= startPositionSubsequentLines
;
4576 long lastEndPos
= GetRange().GetStart()-1;
4577 long lastCompletedEndPos
= lastEndPos
;
4579 int currentWidth
= 0;
4580 SetPosition(rect
.GetPosition());
4582 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4585 int maxHeight
= currentPosition
.y
;
4590 int lineDescent
= 0;
4592 wxRichTextObjectList
::compatibility_iterator node
;
4594 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4596 wxArrayInt partialExtents
;
4599 int paraDescent
= 0;
4601 // This calculates the partial text extents
4602 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4604 node
= m_children
.GetFirst();
4607 wxRichTextObject
* child
= node
->GetData();
4609 //child->SetCachedSize(wxDefaultSize);
4610 child
->Layout(dc
, context
, rect
, style
);
4612 node
= node
->GetNext();
4619 // We may need to go back to a previous child, in which case create the new line,
4620 // find the child corresponding to the start position of the string, and
4623 wxRect availableRect
;
4625 node
= m_children
.GetFirst();
4628 wxRichTextObject
* child
= node
->GetData();
4630 // If floating, ignore. We already laid out floats.
4631 // Also ignore if empty object, except if we haven't got any
4633 if (child
->IsFloating() || !child
->IsShown() ||
4634 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4637 node
= node
->GetNext();
4641 // If this is e.g. a composite text box, it will need to be laid out itself.
4642 // But if just a text fragment or image, for example, this will
4643 // do nothing. NB: won't we need to set the position after layout?
4644 // since for example if position is dependent on vertical line size, we
4645 // can't tell the position until the size is determined. So possibly introduce
4646 // another layout phase.
4648 // We may only be looking at part of a child, if we searched back for wrapping
4649 // and found a suitable point some way into the child. So get the size for the fragment
4652 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4653 long lastPosToUse
= child
->GetRange().GetEnd();
4654 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4656 if (lineBreakInThisObject
)
4657 lastPosToUse
= nextBreakPos
;
4660 int childDescent
= 0;
4662 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4663 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4664 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4666 if (child
->IsTopLevel())
4668 wxSize oldSize
= child
->GetCachedSize();
4670 child
->Invalidate(wxRICHTEXT_ALL
);
4671 child
->SetPosition(wxPoint(0, 0));
4673 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4674 // lays out the object again using the minimum size
4675 // The position will be determined by its location in its line,
4676 // and not by the child's actual position.
4677 child
->LayoutToBestSize(dc
, context
, buffer
,
4678 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4680 if (oldSize
!= child
->GetCachedSize())
4682 partialExtents
.Clear();
4684 // Recalculate the partial text extents since the child object changed size
4685 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4689 // Problem: we need to layout composites here for which we need the available width,
4690 // but we can't get the available width without using the float collector which
4691 // needs to know the object height.
4693 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4695 childSize
= child
->GetCachedSize();
4696 childDescent
= child
->GetDescent();
4700 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4701 // Get height only, then the width using the partial extents
4702 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4703 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4705 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4710 int loopIterations
= 0;
4712 // If there are nested objects that need to lay themselves out, we have to do this in a
4713 // loop because the height of the object may well depend on the available width.
4714 // And because of floating object positioning, the available width depends on the
4715 // height of the object and whether it will clash with the floating objects.
4716 // So, we see whether the available width changes due to the presence of floating images.
4717 // If it does, then we'll use the new restricted width to find the object height again.
4718 // If this causes another restriction in the available width, we'll try again, until
4719 // either we lose patience or the available width settles down.
4724 wxRect oldAvailableRect
= availableRect
;
4726 // Available width depends on the floating objects and the line height.
4727 // Note: the floating objects may be placed vertically along the two side of
4728 // buffer, so we may have different available line widths with different
4729 // [startY, endY]. So, we can't determine how wide the available
4730 // space is until we know the exact line height.
4731 if (childDescent
== 0)
4733 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4734 lineDescent
= maxDescent
;
4735 lineAscent
= maxAscent
;
4739 lineDescent
= wxMax(childDescent
, maxDescent
);
4740 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4742 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4744 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4746 // Adjust availableRect to the space that is available when taking floating objects into account.
4748 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4750 int newX
= floatAvailableRect
.x
+ startOffset
;
4751 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4752 availableRect
.x
= newX
;
4753 availableRect
.width
= newW
;
4756 if (floatAvailableRect
.width
< availableRect
.width
)
4757 availableRect
.width
= floatAvailableRect
.width
;
4759 currentPosition
.x
= availableRect
.x
- rect
.x
;
4761 if (child
->IsTopLevel() && loopIterations
<= 20)
4763 if (availableRect
!= oldAvailableRect
)
4765 wxSize oldSize
= child
->GetCachedSize();
4767 //child->SetCachedSize(wxDefaultSize);
4768 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4769 // lays out the object again using the minimum size
4770 child
->Invalidate(wxRICHTEXT_ALL
);
4771 child
->LayoutToBestSize(dc
, context
, buffer
,
4772 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4773 childSize
= child
->GetCachedSize();
4774 childDescent
= child
->GetDescent();
4775 //child->SetPosition(availableRect.GetPosition());
4777 if (oldSize
!= child
->GetCachedSize())
4779 partialExtents
.Clear();
4781 // Recalculate the partial text extents since the child object changed size
4782 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4785 // Go around the loop finding the available rect for the given floating objects
4795 if (child
->IsTopLevel())
4797 // We can move it to the correct position at this point
4798 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4802 // 1) There was a line break BEFORE the natural break
4803 // 2) There was a line break AFTER the natural break
4804 // 3) It's the last line
4805 // 4) The child still fits (carry on) - 'else' clause
4807 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4809 (childSize
.x
+ currentWidth
> availableRect
.width
)
4811 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4815 long wrapPosition
= 0;
4816 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4817 wrapPosition
= child
->GetRange().GetEnd();
4820 // Find a place to wrap. This may walk back to previous children,
4821 // for example if a word spans several objects.
4822 // Note: one object must contains only one wxTextAtrr, so the line height will not
4823 // change inside one object. Thus, we can pass the remain line width to the
4824 // FindWrapPosition function.
4825 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4827 // If the function failed, just cut it off at the end of this child.
4828 wrapPosition
= child
->GetRange().GetEnd();
4831 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4832 if (wrapPosition
<= lastCompletedEndPos
)
4833 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4835 // Line end position shouldn't be the same as the end, or greater.
4836 if (wrapPosition
>= GetRange().GetEnd())
4837 wrapPosition
= GetRange().GetEnd()-1;
4839 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4841 // Let's find the actual size of the current line now
4843 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4847 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4848 if (!child
->IsEmpty())
4850 // Get height only, then the width using the partial extents
4851 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4852 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4856 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4858 currentWidth
= actualSize
.x
;
4860 // The descent for the whole line at this point, is the correct max descent
4861 maxDescent
= childDescent
;
4863 maxAscent
= actualSize
.y
-childDescent
;
4865 // lineHeight is given by the height for the whole line, since it will
4866 // take into account ascend/descend.
4867 lineHeight
= actualSize
.y
;
4869 if (lineHeight
== 0 && buffer
)
4871 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4872 wxCheckSetFont(dc
, font
);
4873 lineHeight
= dc
.GetCharHeight();
4876 if (maxDescent
== 0)
4879 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4883 wxRichTextLine
* line
= AllocateLine(lineCount
);
4885 // Set relative range so we won't have to change line ranges when paragraphs are moved
4886 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4887 line
->SetPosition(currentPosition
);
4888 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4889 line
->SetDescent(maxDescent
);
4891 maxHeight
= currentPosition
.y
+ lineHeight
;
4893 // Now move down a line. TODO: add margins, spacing
4894 currentPosition
.y
+= lineHeight
;
4895 currentPosition
.y
+= lineSpacing
;
4898 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4903 // TODO: account for zero-length objects
4904 // wxASSERT(wrapPosition > lastCompletedEndPos);
4906 lastEndPos
= wrapPosition
;
4907 lastCompletedEndPos
= lastEndPos
;
4911 if (wrapPosition
< GetRange().GetEnd()-1)
4913 // May need to set the node back to a previous one, due to searching back in wrapping
4914 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4915 if (childAfterWrapPosition
)
4916 node
= m_children
.Find(childAfterWrapPosition
);
4918 node
= node
->GetNext();
4921 node
= node
->GetNext();
4923 // Apply paragraph styles such as alignment to the wrapped line
4924 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4928 // We still fit, so don't add a line, and keep going
4929 currentWidth
+= childSize
.x
;
4931 if (childDescent
== 0)
4933 // An object with a zero descend value wants to take up the whole
4934 // height regardless of baseline
4935 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4939 maxDescent
= wxMax(childDescent
, maxDescent
);
4940 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4943 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4945 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4946 lastEndPos
= child
->GetRange().GetEnd();
4948 node
= node
->GetNext();
4952 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4954 // Remove remaining unused line objects, if any
4955 ClearUnusedLines(lineCount
);
4957 // We need to add back the margins etc.
4959 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4960 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4961 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4962 SetCachedSize(marginRect
.GetSize());
4965 // The maximum size is the length of the paragraph stretched out into a line.
4966 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4967 // this size. TODO: take into account line breaks.
4969 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4970 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4971 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4972 SetMaxSize(marginRect
.GetSize());
4975 // Find the greatest minimum size. Currently we only look at non-text objects,
4976 // which isn't ideal but it would be slow to find the maximum word width to
4977 // use as the minimum.
4980 node
= m_children
.GetFirst();
4983 wxRichTextObject
* child
= node
->GetData();
4985 // If floating, ignore. We already laid out floats.
4986 // Also ignore if empty object, except if we haven't got any
4988 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4990 if (child
->GetCachedSize().x
> minWidth
)
4991 minWidth
= child
->GetMinSize().x
;
4993 node
= node
->GetNext();
4996 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4997 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4998 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4999 SetMinSize(marginRect
.GetSize());
5003 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5004 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5005 // Use the text extents to calculate the size of each fragment in each line
5006 wxRichTextLineList
::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5009 wxRichTextLine
* line
= lineNode
->GetData();
5010 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5012 // Loop through objects until we get to the one within range
5013 wxRichTextObjectList
::compatibility_iterator node2
= m_children
.GetFirst();
5017 wxRichTextObject
* child
= node2
->GetData();
5019 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5021 wxRichTextRange rangeToUse
= lineRange
;
5022 rangeToUse
.LimitTo(child
->GetRange());
5024 // Find the size of the child from the text extents, and store in an array
5025 // for drawing later
5027 if (rangeToUse
.GetStart() > GetRange().GetStart())
5028 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5029 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5030 int sz
= right
- left
;
5031 line
->GetObjectSizes().Add(sz
);
5033 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5034 // Can break out of inner loop now since we've passed this line's range
5037 node2
= node2
->GetNext();
5040 lineNode
= lineNode
->GetNext();
5048 /// Apply paragraph styles, such as centering, to wrapped lines
5049 /// TODO: take into account box attributes, possibly
5050 void wxRichTextParagraph
::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5052 if (!attr
.HasAlignment())
5055 wxPoint pos
= line
->GetPosition();
5056 wxSize size
= line
->GetSize();
5058 // centering, right-justification
5059 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5061 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5062 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5063 line
->SetPosition(pos
);
5065 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5067 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5068 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5069 line
->SetPosition(pos
);
5073 /// Insert text at the given position
5074 bool wxRichTextParagraph
::InsertText(long pos
, const wxString
& text
)
5076 wxRichTextObject
* childToUse
= NULL
;
5077 wxRichTextObjectList
::compatibility_iterator nodeToUse
= wxRichTextObjectList
::compatibility_iterator();
5079 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5082 wxRichTextObject
* child
= node
->GetData();
5083 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5090 node
= node
->GetNext();
5095 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5098 int posInString
= pos
- textObject
->GetRange().GetStart();
5100 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5101 text
+ textObject
->GetText().Mid(posInString
);
5102 textObject
->SetText(newText
);
5104 int textLength
= text
.length();
5106 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5107 textObject
->GetRange().GetEnd() + textLength
));
5109 // Increment the end range of subsequent fragments in this paragraph.
5110 // We'll set the paragraph range itself at a higher level.
5112 wxRichTextObjectList
::compatibility_iterator node
= nodeToUse
->GetNext();
5115 wxRichTextObject
* child
= node
->GetData();
5116 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5117 textObject
->GetRange().GetEnd() + textLength
));
5119 node
= node
->GetNext();
5126 // TODO: if not a text object, insert at closest position, e.g. in front of it
5132 // Don't pass parent initially to suppress auto-setting of parent range.
5133 // We'll do that at a higher level.
5134 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5136 AppendChild(textObject
);
5143 void wxRichTextParagraph
::Copy(const wxRichTextParagraph
& obj
)
5145 wxRichTextCompositeObject
::Copy(obj
);
5148 /// Clear the cached lines
5149 void wxRichTextParagraph
::ClearLines()
5151 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5154 /// Get/set the object size for the given range. Returns false if the range
5155 /// is invalid for this object.
5156 bool wxRichTextParagraph
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5158 if (!range
.IsWithin(GetRange()))
5161 if (flags
& wxRICHTEXT_UNFORMATTED
)
5163 // Just use unformatted data, assume no line breaks
5166 wxArrayInt childExtents
;
5175 int maxLineHeight
= 0;
5177 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5181 wxRichTextObject
* child
= node
->GetData();
5182 if (!child
->GetRange().IsOutside(range
))
5184 // Floating objects have a zero size within the paragraph.
5185 if (child
->IsFloating())
5190 if (partialExtents
->GetCount() > 0)
5191 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5195 partialExtents
->Add(0 /* zero size */ + lastSize
);
5202 wxRichTextRange rangeToUse
= range
;
5203 rangeToUse
.LimitTo(child
->GetRange());
5204 int childDescent
= 0;
5206 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
5207 // but it's only going to be used after caching has taken place.
5208 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5210 childDescent
= child
->GetDescent();
5211 childSize
= child
->GetCachedSize();
5213 if (childDescent
== 0)
5215 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5219 maxDescent
= wxMax(maxDescent
, childDescent
);
5220 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5223 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5225 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5226 sz
.x
+= childSize
.x
;
5227 descent
= maxDescent
;
5229 else if (child
->IsTopLevel())
5231 childDescent
= child
->GetDescent();
5232 childSize
= child
->GetCachedSize();
5234 if (childDescent
== 0)
5236 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5240 maxDescent
= wxMax(maxDescent
, childDescent
);
5241 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5244 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5246 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5247 sz
.x
+= childSize
.x
;
5248 descent
= maxDescent
;
5250 // FIXME: this won't change the original values.
5251 // Should we be calling GetRangeSize above instead of using cached values?
5253 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5255 child
->SetCachedSize(childSize
);
5256 child
->SetDescent(childDescent
);
5263 if (partialExtents
->GetCount() > 0)
5264 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5268 partialExtents
->Add(childSize
.x
+ lastSize
);
5271 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5273 if (childDescent
== 0)
5275 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5279 maxDescent
= wxMax(maxDescent
, childDescent
);
5280 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5283 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5285 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5286 sz
.x
+= childSize
.x
;
5287 descent
= maxDescent
;
5289 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5291 child
->SetCachedSize(childSize
);
5292 child
->SetDescent(childDescent
);
5298 if (partialExtents
->GetCount() > 0)
5299 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5304 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5306 partialExtents
->Add(childExtents
[i
] + lastSize
);
5316 node
= node
->GetNext();
5322 // Use formatted data, with line breaks
5325 // We're going to loop through each line, and then for each line,
5326 // call GetRangeSize for the fragment that comprises that line.
5327 // Only we have to do that multiple times within the line, because
5328 // the line may be broken into pieces. For now ignore line break commands
5329 // (so we can assume that getting the unformatted size for a fragment
5330 // within a line is the actual size)
5332 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
5335 wxRichTextLine
* line
= node
->GetData();
5336 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5337 if (!lineRange
.IsOutside(range
))
5341 int maxLineHeight
= 0;
5342 int maxLineWidth
= 0;
5344 wxRichTextObjectList
::compatibility_iterator node2
= m_children
.GetFirst();
5347 wxRichTextObject
* child
= node2
->GetData();
5349 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5351 wxRichTextRange rangeToUse
= lineRange
;
5352 rangeToUse
.LimitTo(child
->GetRange());
5353 if (child
->IsTopLevel())
5354 rangeToUse
= child
->GetOwnRange();
5357 int childDescent
= 0;
5358 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5360 if (childDescent
== 0)
5362 // Assume that if descent is zero, this child can occupy the full line height
5363 // and does not need space for the line's maximum descent. So we influence
5364 // the overall max line height only.
5365 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5369 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5370 maxDescent
= wxMax(maxAscent
, childDescent
);
5372 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5373 maxLineWidth
+= childSize
.x
;
5375 descent
= wxMax(descent
, childDescent
);
5378 node2
= node2
->GetNext();
5381 descent
= wxMax(descent
, maxDescent
);
5383 // Increase size by a line (TODO: paragraph spacing)
5384 sz
.y
+= maxLineHeight
;
5385 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5387 node
= node
->GetNext();
5394 /// Finds the absolute position and row height for the given character position
5395 bool wxRichTextParagraph
::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5399 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5401 *height
= line
->GetSize().y
;
5403 *height
= dc
.GetCharHeight();
5405 // -1 means 'the start of the buffer'.
5408 pt
= pt
+ line
->GetPosition();
5413 // The final position in a paragraph is taken to mean the position
5414 // at the start of the next paragraph.
5415 if (index
== GetRange().GetEnd())
5417 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5418 wxASSERT( parent
!= NULL
);
5420 // Find the height at the next paragraph, if any
5421 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5424 *height
= line
->GetSize().y
;
5425 pt
= line
->GetAbsolutePosition();
5429 *height
= dc
.GetCharHeight();
5430 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5431 pt
= wxPoint(indent
, GetCachedSize().y
);
5437 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5440 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
5443 wxRichTextLine
* line
= node
->GetData();
5444 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5445 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5447 // If this is the last point in the line, and we're forcing the
5448 // returned value to be the start of the next line, do the required
5450 if (index
== lineRange
.GetEnd() && forceLineStart
)
5452 if (node
->GetNext())
5454 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5455 *height
= nextLine
->GetSize().y
;
5456 pt
= nextLine
->GetAbsolutePosition();
5461 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5463 wxRichTextRange
r(lineRange
.GetStart(), index
);
5467 // We find the size of the line up to this point,
5468 // then we can add this size to the line start position and
5469 // paragraph start position to find the actual position.
5471 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5473 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5474 *height
= line
->GetSize().y
;
5481 node
= node
->GetNext();
5487 /// Hit-testing: returns a flag indicating hit test details, plus
5488 /// information about position
5489 int wxRichTextParagraph
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5492 return wxRICHTEXT_HITTEST_NONE
;
5494 // If we're in the top-level container, then we can return
5495 // a suitable hit test code even if the point is outside the container area,
5496 // so that we can position the caret sensibly even if we don't
5497 // click on valid content. If we're not at the top-level, and the point
5498 // is not within this paragraph object, then we don't want to stop more
5499 // precise hit-testing from working prematurely, so return immediately.
5500 // NEW STRATEGY: use the parent boundary to test whether we're in the
5501 // right region, not the paragraph, since the paragraph may be positioned
5502 // some way in from where the user clicks.
5505 wxRichTextObject
* tempObj
, *tempContextObj
;
5506 if (GetParent() && GetParent()->wxRichTextObject
::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5507 return wxRICHTEXT_HITTEST_NONE
;
5510 wxRichTextObjectList
::compatibility_iterator objNode
= m_children
.GetFirst();
5513 wxRichTextObject
* child
= objNode
->GetData();
5514 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5517 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5518 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5523 objNode
= objNode
->GetNext();
5526 wxPoint paraPos
= GetPosition();
5528 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
5531 wxRichTextLine
* line
= node
->GetData();
5532 wxPoint linePos
= paraPos
+ line
->GetPosition();
5533 wxSize lineSize
= line
->GetSize();
5534 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5536 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5538 if (pt
.x
< linePos
.x
)
5540 textPosition
= lineRange
.GetStart();
5541 *obj
= FindObjectAtPosition(textPosition
);
5542 *contextObj
= GetContainer();
5543 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5545 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5547 textPosition
= lineRange
.GetEnd();
5548 *obj
= FindObjectAtPosition(textPosition
);
5549 *contextObj
= GetContainer();
5550 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5554 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5555 wxArrayInt partialExtents
;
5560 // This calculates the partial text extents
5561 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5563 int lastX
= linePos
.x
;
5565 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5567 int nextX
= partialExtents
[i
] + linePos
.x
;
5569 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5571 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5573 *obj
= FindObjectAtPosition(textPosition
);
5574 *contextObj
= GetContainer();
5576 // So now we know it's between i-1 and i.
5577 // Let's see if we can be more precise about
5578 // which side of the position it's on.
5580 int midPoint
= (nextX
+ lastX
)/2;
5581 if (pt
.x
>= midPoint
)
5582 return wxRICHTEXT_HITTEST_AFTER
;
5584 return wxRICHTEXT_HITTEST_BEFORE
;
5591 int lastX
= linePos
.x
;
5592 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5597 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5599 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5601 int nextX
= childSize
.x
+ linePos
.x
;
5603 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5607 *obj
= FindObjectAtPosition(textPosition
);
5608 *contextObj
= GetContainer();
5610 // So now we know it's between i-1 and i.
5611 // Let's see if we can be more precise about
5612 // which side of the position it's on.
5614 int midPoint
= (nextX
+ lastX
)/2;
5615 if (pt
.x
>= midPoint
)
5616 return wxRICHTEXT_HITTEST_AFTER
;
5618 return wxRICHTEXT_HITTEST_BEFORE
;
5629 node
= node
->GetNext();
5632 return wxRICHTEXT_HITTEST_NONE
;
5635 /// Split an object at this position if necessary, and return
5636 /// the previous object, or NULL if inserting at beginning.
5637 wxRichTextObject
* wxRichTextParagraph
::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5639 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5642 wxRichTextObject
* child
= node
->GetData();
5644 if (pos
== child
->GetRange().GetStart())
5648 if (node
->GetPrevious())
5649 *previousObject
= node
->GetPrevious()->GetData();
5651 *previousObject
= NULL
;
5657 if (child
->GetRange().Contains(pos
))
5659 // This should create a new object, transferring part of
5660 // the content to the old object and the rest to the new object.
5661 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5663 // If we couldn't split this object, just insert in front of it.
5666 // Maybe this is an empty string, try the next one
5671 // Insert the new object after 'child'
5672 if (node
->GetNext())
5673 m_children
.Insert(node
->GetNext(), newObject
);
5675 m_children
.Append(newObject
);
5676 newObject
->SetParent(this);
5679 *previousObject
= child
;
5685 node
= node
->GetNext();
5688 *previousObject
= NULL
;
5692 /// Move content to a list from obj on
5693 void wxRichTextParagraph
::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5695 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(obj
);
5698 wxRichTextObject
* child
= node
->GetData();
5701 wxRichTextObjectList
::compatibility_iterator oldNode
= node
;
5703 node
= node
->GetNext();
5705 m_children
.DeleteNode(oldNode
);
5709 /// Add content back from list
5710 void wxRichTextParagraph
::MoveFromList(wxList
& list
)
5712 for (wxList
::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5714 AppendChild((wxRichTextObject
*) node
->GetData());
5719 void wxRichTextParagraph
::CalculateRange(long start
, long& end
)
5721 wxRichTextCompositeObject
::CalculateRange(start
, end
);
5723 // Add one for end of paragraph
5726 m_range
.SetRange(start
, end
);
5729 /// Find the object at the given position
5730 wxRichTextObject
* wxRichTextParagraph
::FindObjectAtPosition(long position
)
5732 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5735 wxRichTextObject
* obj
= node
->GetData();
5736 if (obj
->GetRange().Contains(position
) ||
5737 obj
->GetRange().GetStart() == position
||
5738 obj
->GetRange().GetEnd() == position
)
5741 node
= node
->GetNext();
5746 /// Get the plain text searching from the start or end of the range.
5747 /// The resulting string may be shorter than the range given.
5748 bool wxRichTextParagraph
::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5750 text
= wxEmptyString
;
5754 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5757 wxRichTextObject
* obj
= node
->GetData();
5758 if (!obj
->GetRange().IsOutside(range
))
5760 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5763 text
+= textObj
->GetTextForRange(range
);
5771 node
= node
->GetNext();
5776 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetLast();
5779 wxRichTextObject
* obj
= node
->GetData();
5780 if (!obj
->GetRange().IsOutside(range
))
5782 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5785 text
= textObj
->GetTextForRange(range
) + text
;
5789 text
= wxT(" ") + text
;
5793 node
= node
->GetPrevious();
5800 /// Find a suitable wrap position.
5801 bool wxRichTextParagraph
::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5803 if (range
.GetLength() <= 0)
5806 // Find the first position where the line exceeds the available space.
5808 long breakPosition
= range
.GetEnd();
5810 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5811 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5815 if (range
.GetStart() > GetRange().GetStart())
5816 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5821 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5823 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5825 if (widthFromStartOfThisRange
> availableSpace
)
5827 breakPosition
= i
-1;
5835 // Binary chop for speed
5836 long minPos
= range
.GetStart();
5837 long maxPos
= range
.GetEnd();
5840 if (minPos
== maxPos
)
5843 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5845 if (sz
.x
> availableSpace
)
5846 breakPosition
= minPos
- 1;
5849 else if ((maxPos
- minPos
) == 1)
5852 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5854 if (sz
.x
> availableSpace
)
5855 breakPosition
= minPos
- 1;
5858 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5859 if (sz
.x
> availableSpace
)
5860 breakPosition
= maxPos
-1;
5866 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5869 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5871 if (sz
.x
> availableSpace
)
5883 // Now we know the last position on the line.
5884 // Let's try to find a word break.
5887 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5889 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5890 if (newLinePos
!= wxNOT_FOUND
)
5892 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5896 int spacePos
= plainText
.Find(wxT(' '), true);
5897 int tabPos
= plainText
.Find(wxT('\t'), true);
5898 int pos
= wxMax(spacePos
, tabPos
);
5899 if (pos
!= wxNOT_FOUND
)
5901 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5902 breakPosition
= breakPosition
- positionsFromEndOfString
;
5907 wrapPosition
= breakPosition
;
5912 /// Get the bullet text for this paragraph.
5913 wxString wxRichTextParagraph
::GetBulletText()
5915 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5916 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5917 return wxEmptyString
;
5919 int number
= GetAttributes().GetBulletNumber();
5922 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5924 text
.Printf(wxT("%d"), number
);
5926 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5928 // TODO: Unicode, and also check if number > 26
5929 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5931 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5933 // TODO: Unicode, and also check if number > 26
5934 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5936 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5938 text
= wxRichTextDecimalToRoman(number
);
5940 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5942 text
= wxRichTextDecimalToRoman(number
);
5945 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5947 text
= GetAttributes().GetBulletText();
5950 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5952 // The outline style relies on the text being computed statically,
5953 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5954 // should be stored in the attributes; if not, just use the number for this
5955 // level, as previously computed.
5956 if (!GetAttributes().GetBulletText().IsEmpty())
5957 text
= GetAttributes().GetBulletText();
5960 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5962 text
= wxT("(") + text
+ wxT(")");
5964 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5966 text
= text
+ wxT(")");
5969 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5977 /// Allocate or reuse a line object
5978 wxRichTextLine
* wxRichTextParagraph
::AllocateLine(int pos
)
5980 if (pos
< (int) m_cachedLines
.GetCount())
5982 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5988 wxRichTextLine
* line
= new wxRichTextLine(this);
5989 m_cachedLines
.Append(line
);
5994 /// Clear remaining unused line objects, if any
5995 bool wxRichTextParagraph
::ClearUnusedLines(int lineCount
)
5997 int cachedLineCount
= m_cachedLines
.GetCount();
5998 if ((int) cachedLineCount
> lineCount
)
6000 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6002 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetLast();
6003 wxRichTextLine
* line
= node
->GetData();
6004 m_cachedLines
.Erase(node
);
6011 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6012 /// retrieve the actual style.
6013 wxRichTextAttr wxRichTextParagraph
::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6015 wxRichTextAttr attr
;
6016 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6019 attr
= buf
->GetBasicStyle();
6020 if (!includingBoxAttr
)
6022 attr
.GetTextBoxAttr().Reset();
6023 // The background colour will be painted by the container, and we don't
6024 // want to unnecessarily overwrite the background when we're drawing text
6025 // because this may erase the guideline (which appears just under the text
6026 // if there's no padding).
6027 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6029 wxRichTextApplyStyle(attr
, GetAttributes());
6032 attr
= GetAttributes();
6034 wxRichTextApplyStyle(attr
, contentStyle
);
6038 /// Get combined attributes of the base style and paragraph style.
6039 wxRichTextAttr wxRichTextParagraph
::GetCombinedAttributes(bool includingBoxAttr
) const
6041 wxRichTextAttr attr
;
6042 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6045 attr
= buf
->GetBasicStyle();
6046 if (!includingBoxAttr
)
6047 attr
.GetTextBoxAttr().Reset();
6048 wxRichTextApplyStyle(attr
, GetAttributes());
6051 attr
= GetAttributes();
6056 // Create default tabstop array
6057 void wxRichTextParagraph
::InitDefaultTabs()
6059 // create a default tab list at 10 mm each.
6060 for (int i
= 0; i
< 20; ++i
)
6062 sm_defaultTabs
.Add(i
*100);
6066 // Clear default tabstop array
6067 void wxRichTextParagraph
::ClearDefaultTabs()
6069 sm_defaultTabs
.Clear();
6072 void wxRichTextParagraph
::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6074 wxRichTextObjectList
::compatibility_iterator node
= GetChildren().GetFirst();
6077 wxRichTextObject
* anchored
= node
->GetData();
6078 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6082 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6085 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6087 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6088 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6090 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6094 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6096 /* Update the offset */
6097 int newOffsetY
= pos
- rect
.y
;
6098 if (newOffsetY
!= offsetY
)
6100 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6101 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6102 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6105 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6107 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6108 x
= rect
.x
+ rect
.width
- size
.x
;
6110 anchored
->SetPosition(wxPoint(x
, pos
));
6111 anchored
->SetCachedSize(size
);
6112 floatCollector
->CollectFloat(this, anchored
);
6115 node
= node
->GetNext();
6119 // Get the first position from pos that has a line break character.
6120 long wxRichTextParagraph
::GetFirstLineBreakPosition(long pos
)
6122 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
6125 wxRichTextObject
* obj
= node
->GetData();
6126 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6128 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6131 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6136 node
= node
->GetNext();
6143 * This object represents a line in a paragraph, and stores
6144 * offsets from the start of the paragraph representing the
6145 * start and end positions of the line.
6148 wxRichTextLine
::wxRichTextLine(wxRichTextParagraph
* parent
)
6154 void wxRichTextLine
::Init(wxRichTextParagraph
* parent
)
6157 m_range
.SetRange(-1, -1);
6158 m_pos
= wxPoint(0, 0);
6159 m_size
= wxSize(0, 0);
6161 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6162 m_objectSizes
.Clear();
6167 void wxRichTextLine
::Copy(const wxRichTextLine
& obj
)
6169 m_range
= obj
.m_range
;
6170 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6171 m_objectSizes
= obj
.m_objectSizes
;
6175 /// Get the absolute object position
6176 wxPoint wxRichTextLine
::GetAbsolutePosition() const
6178 return m_parent
->GetPosition() + m_pos
;
6181 /// Get the absolute range
6182 wxRichTextRange wxRichTextLine
::GetAbsoluteRange() const
6184 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6185 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6190 * wxRichTextPlainText
6191 * This object represents a single piece of text.
6194 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6196 wxRichTextPlainText
::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6197 wxRichTextObject(parent
)
6200 SetAttributes(*style
);
6205 #define USE_KERNING_FIX 1
6207 // If insufficient tabs are defined, this is the tab width used
6208 #define WIDTH_FOR_DEFAULT_TABS 50
6211 bool wxRichTextPlainText
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6213 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6214 wxASSERT (para
!= NULL
);
6216 wxRichTextAttr
textAttr(para ? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6217 context
.ApplyVirtualAttributes(textAttr
, this);
6219 // Let's make the assumption for now that for content in a paragraph, including
6220 // text, we never have a discontinuous selection. So we only deal with a
6222 wxRichTextRange selectionRange
;
6223 if (selection
.IsValid())
6225 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6226 if (selectionRanges
.GetCount() > 0)
6227 selectionRange
= selectionRanges
[0];
6229 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6232 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6234 int offset
= GetRange().GetStart();
6236 // Replace line break characters with spaces
6237 wxString str
= m_text
;
6238 wxString toRemove
= wxRichTextLineBreakChar
;
6239 str
.Replace(toRemove
, wxT(" "));
6240 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6243 long len
= range
.GetLength();
6244 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6246 // Test for the optimized situations where all is selected, or none
6249 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6250 wxCheckSetFont(dc
, textFont
);
6251 int charHeight
= dc
.GetCharHeight();
6254 if ( textFont
.IsOk() )
6256 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6258 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6259 textFont
.SetPointSize( static_cast<int>(size
) );
6262 wxCheckSetFont(dc
, textFont
);
6264 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6266 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6267 textFont
.SetPointSize( static_cast<int>(size
) );
6269 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6270 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6271 wxCheckSetFont(dc
, textFont
);
6276 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6282 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6285 // TODO: new selection code
6287 // (a) All selected.
6288 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6290 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6292 // (b) None selected.
6293 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6295 // Draw all unselected
6296 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6300 // (c) Part selected, part not
6301 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6303 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6305 // 1. Initial unselected chunk, if any, up until start of selection.
6306 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6308 int r1
= range
.GetStart();
6309 int s1
= selectionRange
.GetStart()-1;
6310 int fragmentLen
= s1
- r1
+ 1;
6311 if (fragmentLen
< 0)
6313 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6315 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6317 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6320 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6322 // Compensate for kerning difference
6323 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6324 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6326 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6327 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6328 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6329 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6331 int kerningDiff
= (w1
+ w3
) - w2
;
6332 x
= x
- kerningDiff
;
6337 // 2. Selected chunk, if any.
6338 if (selectionRange
.GetEnd() >= range
.GetStart())
6340 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6341 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6343 int fragmentLen
= s2
- s1
+ 1;
6344 if (fragmentLen
< 0)
6346 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6348 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6350 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6353 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6355 // Compensate for kerning difference
6356 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6357 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6359 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6360 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6361 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6362 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6364 int kerningDiff
= (w1
+ w3
) - w2
;
6365 x
= x
- kerningDiff
;
6370 // 3. Remaining unselected chunk, if any
6371 if (selectionRange
.GetEnd() < range
.GetEnd())
6373 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6374 int r2
= range
.GetEnd();
6376 int fragmentLen
= r2
- s2
+ 1;
6377 if (fragmentLen
< 0)
6379 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6381 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6383 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6390 bool wxRichTextPlainText
::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6392 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6394 wxArrayInt tabArray
;
6398 if (attr
.GetTabs().IsEmpty())
6399 tabArray
= wxRichTextParagraph
::GetDefaultTabs();
6401 tabArray
= attr
.GetTabs();
6402 tabCount
= tabArray
.GetCount();
6404 for (int i
= 0; i
< tabCount
; ++i
)
6406 int pos
= tabArray
[i
];
6407 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6414 int nextTabPos
= -1;
6420 wxColour
highlightColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6421 wxColour
highlightTextColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6423 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6424 wxCheckSetPen(dc
, wxPen(highlightColour
));
6425 dc
.SetTextForeground(highlightTextColour
);
6426 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6430 dc
.SetTextForeground(attr
.GetTextColour());
6432 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6434 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6435 dc
.SetTextBackground(attr
.GetBackgroundColour());
6438 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6441 wxCoord x_orig
= GetParent()->GetPosition().x
;
6444 // the string has a tab
6445 // break up the string at the Tab
6446 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6447 str
= str
.AfterFirst(wxT('\t'));
6448 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6450 bool not_found
= true;
6451 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6453 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6455 // Find the next tab position.
6456 // Even if we're at the end of the tab array, we must still draw the chunk.
6458 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6460 if (nextTabPos
<= tabPos
)
6462 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6463 nextTabPos
= tabPos
+ defaultTabWidth
;
6470 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6471 dc
.DrawRectangle(selRect
);
6473 dc
.DrawText(stringChunk
, x
, y
);
6475 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6477 wxPen oldPen
= dc
.GetPen();
6478 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6479 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6480 wxCheckSetPen(dc
, oldPen
);
6486 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6491 dc
.GetTextExtent(str
, & w
, & h
);
6494 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6495 dc
.DrawRectangle(selRect
);
6497 dc
.DrawText(str
, x
, y
);
6499 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6501 wxPen oldPen
= dc
.GetPen();
6502 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6503 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6504 wxCheckSetPen(dc
, oldPen
);
6513 /// Lay the item out
6514 bool wxRichTextPlainText
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6516 // Only lay out if we haven't already cached the size
6518 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6520 // Eventually we want to have a reasonable estimate of minimum size.
6521 m_minSize
= wxSize(0, 0);
6526 void wxRichTextPlainText
::Copy(const wxRichTextPlainText
& obj
)
6528 wxRichTextObject
::Copy(obj
);
6530 m_text
= obj
.m_text
;
6533 /// Get/set the object size for the given range. Returns false if the range
6534 /// is invalid for this object.
6535 bool wxRichTextPlainText
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6537 if (!range
.IsWithin(GetRange()))
6540 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6541 wxASSERT (para
!= NULL
);
6543 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6545 wxRichTextAttr
textAttr(para ? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6546 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6548 // Always assume unformatted text, since at this level we have no knowledge
6549 // of line breaks - and we don't need it, since we'll calculate size within
6550 // formatted text by doing it in chunks according to the line ranges
6552 bool bScript(false);
6553 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6556 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6557 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6559 wxFont textFont
= font
;
6560 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6561 textFont
.SetPointSize( static_cast<int>(size
) );
6562 wxCheckSetFont(dc
, textFont
);
6567 wxCheckSetFont(dc
, font
);
6571 bool haveDescent
= false;
6572 int startPos
= range
.GetStart() - GetRange().GetStart();
6573 long len
= range
.GetLength();
6575 wxString
str(m_text
);
6576 wxString toReplace
= wxRichTextLineBreakChar
;
6577 str
.Replace(toReplace
, wxT(" "));
6579 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6581 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6582 stringChunk
.MakeUpper();
6586 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6588 // the string has a tab
6589 wxArrayInt tabArray
;
6590 if (textAttr
.GetTabs().IsEmpty())
6591 tabArray
= wxRichTextParagraph
::GetDefaultTabs();
6593 tabArray
= textAttr
.GetTabs();
6595 int tabCount
= tabArray
.GetCount();
6597 for (int i
= 0; i
< tabCount
; ++i
)
6599 int pos
= tabArray
[i
];
6600 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6604 int nextTabPos
= -1;
6606 while (stringChunk
.Find(wxT('\t')) >= 0)
6608 int absoluteWidth
= 0;
6610 // the string has a tab
6611 // break up the string at the Tab
6612 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6613 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6618 if (partialExtents
->GetCount() > 0)
6619 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6623 // Add these partial extents
6625 dc
.GetPartialTextExtents(stringFragment
, p
);
6627 for (j
= 0; j
< p
.GetCount(); j
++)
6628 partialExtents
->Add(oldWidth
+ p
[j
]);
6630 if (partialExtents
->GetCount() > 0)
6631 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6633 absoluteWidth
= relativeX
;
6637 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6639 absoluteWidth
= width
+ relativeX
;
6643 bool notFound
= true;
6644 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6646 nextTabPos
= tabArray
.Item(i
);
6648 // Find the next tab position.
6649 // Even if we're at the end of the tab array, we must still process the chunk.
6651 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6653 if (nextTabPos
<= absoluteWidth
)
6655 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6656 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6660 width
= nextTabPos
- relativeX
;
6663 partialExtents
->Add(width
);
6669 if (!stringChunk
.IsEmpty())
6674 if (partialExtents
->GetCount() > 0)
6675 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6679 // Add these partial extents
6681 dc
.GetPartialTextExtents(stringChunk
, p
);
6683 for (j
= 0; j
< p
.GetCount(); j
++)
6684 partialExtents
->Add(oldWidth
+ p
[j
]);
6688 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6696 int charHeight
= dc
.GetCharHeight();
6697 if ((*partialExtents
).GetCount() > 0)
6698 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6701 size
= wxSize(w
, charHeight
);
6705 size
= wxSize(width
, dc
.GetCharHeight());
6709 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6717 /// Do a split, returning an object containing the second part, and setting
6718 /// the first part in 'this'.
6719 wxRichTextObject
* wxRichTextPlainText
::DoSplit(long pos
)
6721 long index
= pos
- GetRange().GetStart();
6723 if (index
< 0 || index
>= (int) m_text
.length())
6726 wxString firstPart
= m_text
.Mid(0, index
);
6727 wxString secondPart
= m_text
.Mid(index
);
6731 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6732 newObject
->SetAttributes(GetAttributes());
6733 newObject
->SetProperties(GetProperties());
6735 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6736 GetRange().SetEnd(pos
-1);
6742 void wxRichTextPlainText
::CalculateRange(long start
, long& end
)
6744 end
= start
+ m_text
.length() - 1;
6745 m_range
.SetRange(start
, end
);
6749 bool wxRichTextPlainText
::DeleteRange(const wxRichTextRange
& range
)
6751 wxRichTextRange r
= range
;
6753 r
.LimitTo(GetRange());
6755 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6761 long startIndex
= r
.GetStart() - GetRange().GetStart();
6762 long len
= r
.GetLength();
6764 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6768 /// Get text for the given range.
6769 wxString wxRichTextPlainText
::GetTextForRange(const wxRichTextRange
& range
) const
6771 wxRichTextRange r
= range
;
6773 r
.LimitTo(GetRange());
6775 long startIndex
= r
.GetStart() - GetRange().GetStart();
6776 long len
= r
.GetLength();
6778 return m_text
.Mid(startIndex
, len
);
6781 /// Returns true if this object can merge itself with the given one.
6782 bool wxRichTextPlainText
::CanMerge(wxRichTextObject
* object
) const
6784 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6785 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6788 /// Returns true if this object merged itself with the given one.
6789 /// The calling code will then delete the given object.
6790 bool wxRichTextPlainText
::Merge(wxRichTextObject
* object
)
6792 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6793 wxASSERT( textObject
!= NULL
);
6797 m_text
+= textObject
->GetText();
6798 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6805 /// Dump to output stream for debugging
6806 void wxRichTextPlainText
::Dump(wxTextOutputStream
& stream
)
6808 wxRichTextObject
::Dump(stream
);
6809 stream
<< m_text
<< wxT("\n");
6812 /// Get the first position from pos that has a line break character.
6813 long wxRichTextPlainText
::GetFirstLineBreakPosition(long pos
)
6816 int len
= m_text
.length();
6817 int startPos
= pos
- m_range
.GetStart();
6818 for (i
= startPos
; i
< len
; i
++)
6820 wxChar ch
= m_text
[i
];
6821 if (ch
== wxRichTextLineBreakChar
)
6823 return i
+ m_range
.GetStart();
6831 * This is a kind of box, used to represent the whole buffer
6834 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6836 wxList wxRichTextBuffer
::sm_handlers
;
6837 wxList wxRichTextBuffer
::sm_drawingHandlers
;
6838 wxRichTextRenderer
* wxRichTextBuffer
::sm_renderer
= NULL
;
6839 int wxRichTextBuffer
::sm_bulletRightMargin
= 20;
6840 float wxRichTextBuffer
::sm_bulletProportion
= (float) 0.3;
6843 void wxRichTextBuffer
::Init()
6845 m_commandProcessor
= new wxCommandProcessor
;
6846 m_styleSheet
= NULL
;
6848 m_batchedCommandDepth
= 0;
6849 m_batchedCommand
= NULL
;
6857 wxRichTextBuffer
::~wxRichTextBuffer()
6859 delete m_commandProcessor
;
6860 delete m_batchedCommand
;
6863 ClearEventHandlers();
6866 void wxRichTextBuffer
::ResetAndClearCommands()
6870 GetCommandProcessor()->ClearCommands();
6873 Invalidate(wxRICHTEXT_ALL
);
6876 void wxRichTextBuffer
::Copy(const wxRichTextBuffer
& obj
)
6878 wxRichTextParagraphLayoutBox
::Copy(obj
);
6880 m_styleSheet
= obj
.m_styleSheet
;
6881 m_modified
= obj
.m_modified
;
6882 m_batchedCommandDepth
= 0;
6883 if (m_batchedCommand
)
6884 delete m_batchedCommand
;
6885 m_batchedCommand
= NULL
;
6886 m_suppressUndo
= obj
.m_suppressUndo
;
6887 m_invalidRange
= obj
.m_invalidRange
;
6890 /// Push style sheet to top of stack
6891 bool wxRichTextBuffer
::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6894 styleSheet
->InsertSheet(m_styleSheet
);
6896 SetStyleSheet(styleSheet
);
6901 /// Pop style sheet from top of stack
6902 wxRichTextStyleSheet
* wxRichTextBuffer
::PopStyleSheet()
6906 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6907 m_styleSheet
= oldSheet
->GetNextSheet();
6916 /// Submit command to insert paragraphs
6917 bool wxRichTextBuffer
::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6919 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6922 /// Submit command to insert paragraphs
6923 bool wxRichTextParagraphLayoutBox
::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6925 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6927 action
->GetNewParagraphs() = paragraphs
;
6929 action
->SetPosition(pos
);
6931 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6932 if (!paragraphs
.GetPartialParagraph())
6933 range
.SetEnd(range
.GetEnd()+1);
6935 // Set the range we'll need to delete in Undo
6936 action
->SetRange(range
);
6938 buffer
->SubmitAction(action
);
6943 /// Submit command to insert the given text
6944 bool wxRichTextBuffer
::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6946 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6949 /// Submit command to insert the given text
6950 bool wxRichTextParagraphLayoutBox
::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6952 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6954 wxRichTextAttr
* p
= NULL
;
6955 wxRichTextAttr paraAttr
;
6956 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6958 // Get appropriate paragraph style
6959 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6960 if (!paraAttr
.IsDefault())
6964 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6966 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6968 if (!text
.empty() && text
.Last() != wxT('\n'))
6970 // Don't count the newline when undoing
6972 action
->GetNewParagraphs().SetPartialParagraph(true);
6974 else if (!text
.empty() && text
.Last() == wxT('\n'))
6977 action
->SetPosition(pos
);
6979 // Set the range we'll need to delete in Undo
6980 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6982 buffer
->SubmitAction(action
);
6987 /// Submit command to insert the given text
6988 bool wxRichTextBuffer
::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6990 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6993 /// Submit command to insert the given text
6994 bool wxRichTextParagraphLayoutBox
::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6996 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6998 wxRichTextAttr
* p
= NULL
;
6999 wxRichTextAttr paraAttr
;
7000 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7002 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7003 if (!paraAttr
.IsDefault())
7007 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7009 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7010 action
->GetNewParagraphs().AppendChild(newPara
);
7011 action
->GetNewParagraphs().UpdateRanges();
7012 action
->GetNewParagraphs().SetPartialParagraph(false);
7013 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7017 newPara
->SetAttributes(*p
);
7019 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7021 if (para
&& para
->GetRange().GetEnd() == pos
)
7024 // Now see if we need to number the paragraph.
7025 if (newPara
->GetAttributes().HasBulletNumber())
7027 wxRichTextAttr numberingAttr
;
7028 if (FindNextParagraphNumber(para
, numberingAttr
))
7029 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7033 action
->SetPosition(pos
);
7035 // Use the default character style
7036 // Use the default character style
7037 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7039 // Check whether the default style merely reflects the paragraph/basic style,
7040 // in which case don't apply it.
7041 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7042 wxRichTextAttr toApply
;
7045 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7046 wxRichTextAttr newAttr
;
7047 // This filters out attributes that are accounted for by the current
7048 // paragraph/basic style
7049 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7052 toApply
= defaultStyle
;
7054 if (!toApply
.IsDefault())
7055 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7058 // Set the range we'll need to delete in Undo
7059 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7061 buffer
->SubmitAction(action
);
7066 /// Submit command to insert the given image
7067 bool wxRichTextBuffer
::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7068 const wxRichTextAttr
& textAttr
)
7070 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7073 /// Submit command to insert the given image
7074 bool wxRichTextParagraphLayoutBox
::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7075 wxRichTextCtrl
* ctrl
, int flags
,
7076 const wxRichTextAttr
& textAttr
)
7078 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7080 wxRichTextAttr
* p
= NULL
;
7081 wxRichTextAttr paraAttr
;
7082 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7084 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7085 if (!paraAttr
.IsDefault())
7089 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7091 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7093 newPara
->SetAttributes(*p
);
7095 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7096 newPara
->AppendChild(imageObject
);
7097 imageObject
->SetAttributes(textAttr
);
7098 action
->GetNewParagraphs().AppendChild(newPara
);
7099 action
->GetNewParagraphs().UpdateRanges();
7101 action
->GetNewParagraphs().SetPartialParagraph(true);
7103 action
->SetPosition(pos
);
7105 // Set the range we'll need to delete in Undo
7106 action
->SetRange(wxRichTextRange(pos
, pos
));
7108 buffer
->SubmitAction(action
);
7113 // Insert an object with no change of it
7114 wxRichTextObject
* wxRichTextBuffer
::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7116 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7119 // Insert an object with no change of it
7120 wxRichTextObject
* wxRichTextParagraphLayoutBox
::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7122 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7124 wxRichTextAttr
* p
= NULL
;
7125 wxRichTextAttr paraAttr
;
7126 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7128 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7129 if (!paraAttr
.IsDefault())
7133 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7135 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7137 newPara
->SetAttributes(*p
);
7139 newPara
->AppendChild(object
);
7140 action
->GetNewParagraphs().AppendChild(newPara
);
7141 action
->GetNewParagraphs().UpdateRanges();
7143 action
->GetNewParagraphs().SetPartialParagraph(true);
7145 action
->SetPosition(pos
);
7147 // Set the range we'll need to delete in Undo
7148 action
->SetRange(wxRichTextRange(pos
, pos
));
7150 buffer
->SubmitAction(action
);
7152 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7156 /// Get the style that is appropriate for a new paragraph at this position.
7157 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7159 wxRichTextAttr wxRichTextParagraphLayoutBox
::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7161 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7164 wxRichTextAttr attr
;
7165 bool foundAttributes
= false;
7167 // Look for a matching paragraph style
7168 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7170 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7173 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7174 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7176 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7179 foundAttributes
= true;
7180 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7184 // If we didn't find the 'next style', use this style instead.
7185 if (!foundAttributes
)
7187 foundAttributes
= true;
7188 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7193 // Also apply list style if present
7194 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7196 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7199 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7200 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7202 // Apply the overall list style, and item style for this level
7203 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7204 wxRichTextApplyStyle(attr
, listStyle
);
7205 attr
.SetOutlineLevel(thisLevel
);
7206 if (para
->GetAttributes().HasBulletNumber())
7207 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7211 if (!foundAttributes
)
7213 attr
= para
->GetAttributes();
7214 int flags
= attr
.GetFlags();
7216 // Eliminate character styles
7217 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7218 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7219 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7220 attr
.SetFlags(flags
);
7226 return wxRichTextAttr();
7229 /// Submit command to delete this range
7230 bool wxRichTextBuffer
::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7232 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7235 /// Submit command to delete this range
7236 bool wxRichTextParagraphLayoutBox
::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7238 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7240 action
->SetPosition(ctrl
->GetCaretPosition());
7242 // Set the range to delete
7243 action
->SetRange(range
);
7245 // Copy the fragment that we'll need to restore in Undo
7246 CopyFragment(range
, action
->GetOldParagraphs());
7248 // See if we're deleting a paragraph marker, in which case we need to
7249 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7250 if (range
.GetStart() == range
.GetEnd())
7252 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7253 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7255 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7256 if (nextPara
&& nextPara
!= para
)
7258 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7259 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7264 buffer
->SubmitAction(action
);
7269 /// Collapse undo/redo commands
7270 bool wxRichTextBuffer
::BeginBatchUndo(const wxString
& cmdName
)
7272 if (m_batchedCommandDepth
== 0)
7274 wxASSERT(m_batchedCommand
== NULL
);
7275 if (m_batchedCommand
)
7277 GetCommandProcessor()->Store(m_batchedCommand
);
7279 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7282 m_batchedCommandDepth
++;
7287 /// Collapse undo/redo commands
7288 bool wxRichTextBuffer
::EndBatchUndo()
7290 m_batchedCommandDepth
--;
7292 wxASSERT(m_batchedCommandDepth
>= 0);
7293 wxASSERT(m_batchedCommand
!= NULL
);
7295 if (m_batchedCommandDepth
== 0)
7297 GetCommandProcessor()->Store(m_batchedCommand
);
7298 m_batchedCommand
= NULL
;
7304 /// Submit immediately, or delay according to whether collapsing is on
7305 bool wxRichTextBuffer
::SubmitAction(wxRichTextAction
* action
)
7307 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7308 PrepareContent(action
->GetNewParagraphs());
7310 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7312 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7313 cmd
->AddAction(action
);
7315 cmd
->GetActions().Clear();
7318 m_batchedCommand
->AddAction(action
);
7322 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7323 cmd
->AddAction(action
);
7325 // Only store it if we're not suppressing undo.
7326 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7332 /// Begin suppressing undo/redo commands.
7333 bool wxRichTextBuffer
::BeginSuppressUndo()
7340 /// End suppressing undo/redo commands.
7341 bool wxRichTextBuffer
::EndSuppressUndo()
7348 /// Begin using a style
7349 bool wxRichTextBuffer
::BeginStyle(const wxRichTextAttr
& style
)
7351 wxRichTextAttr
newStyle(GetDefaultStyle());
7353 // Save the old default style
7354 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7356 wxRichTextApplyStyle(newStyle
, style
);
7357 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7359 SetDefaultStyle(newStyle
);
7365 bool wxRichTextBuffer
::EndStyle()
7367 if (!m_attributeStack
.GetFirst())
7369 wxLogDebug(_("Too many EndStyle calls!"));
7373 wxList
::compatibility_iterator node
= m_attributeStack
.GetLast();
7374 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7375 m_attributeStack
.Erase(node
);
7377 SetDefaultStyle(*attr
);
7384 bool wxRichTextBuffer
::EndAllStyles()
7386 while (m_attributeStack
.GetCount() != 0)
7391 /// Clear the style stack
7392 void wxRichTextBuffer
::ClearStyleStack()
7394 for (wxList
::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7395 delete (wxRichTextAttr
*) node
->GetData();
7396 m_attributeStack
.Clear();
7399 /// Begin using bold
7400 bool wxRichTextBuffer
::BeginBold()
7402 wxRichTextAttr attr
;
7403 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7405 return BeginStyle(attr
);
7408 /// Begin using italic
7409 bool wxRichTextBuffer
::BeginItalic()
7411 wxRichTextAttr attr
;
7412 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7414 return BeginStyle(attr
);
7417 /// Begin using underline
7418 bool wxRichTextBuffer
::BeginUnderline()
7420 wxRichTextAttr attr
;
7421 attr
.SetFontUnderlined(true);
7423 return BeginStyle(attr
);
7426 /// Begin using point size
7427 bool wxRichTextBuffer
::BeginFontSize(int pointSize
)
7429 wxRichTextAttr attr
;
7430 attr
.SetFontSize(pointSize
);
7432 return BeginStyle(attr
);
7435 /// Begin using this font
7436 bool wxRichTextBuffer
::BeginFont(const wxFont
& font
)
7438 wxRichTextAttr attr
;
7441 return BeginStyle(attr
);
7444 /// Begin using this colour
7445 bool wxRichTextBuffer
::BeginTextColour(const wxColour
& colour
)
7447 wxRichTextAttr attr
;
7448 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7449 attr
.SetTextColour(colour
);
7451 return BeginStyle(attr
);
7454 /// Begin using alignment
7455 bool wxRichTextBuffer
::BeginAlignment(wxTextAttrAlignment alignment
)
7457 wxRichTextAttr attr
;
7458 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7459 attr
.SetAlignment(alignment
);
7461 return BeginStyle(attr
);
7464 /// Begin left indent
7465 bool wxRichTextBuffer
::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7467 wxRichTextAttr attr
;
7468 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7469 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7471 return BeginStyle(attr
);
7474 /// Begin right indent
7475 bool wxRichTextBuffer
::BeginRightIndent(int rightIndent
)
7477 wxRichTextAttr attr
;
7478 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7479 attr
.SetRightIndent(rightIndent
);
7481 return BeginStyle(attr
);
7484 /// Begin paragraph spacing
7485 bool wxRichTextBuffer
::BeginParagraphSpacing(int before
, int after
)
7489 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7491 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7493 wxRichTextAttr attr
;
7494 attr
.SetFlags(flags
);
7495 attr
.SetParagraphSpacingBefore(before
);
7496 attr
.SetParagraphSpacingAfter(after
);
7498 return BeginStyle(attr
);
7501 /// Begin line spacing
7502 bool wxRichTextBuffer
::BeginLineSpacing(int lineSpacing
)
7504 wxRichTextAttr attr
;
7505 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7506 attr
.SetLineSpacing(lineSpacing
);
7508 return BeginStyle(attr
);
7511 /// Begin numbered bullet
7512 bool wxRichTextBuffer
::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7514 wxRichTextAttr attr
;
7515 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7516 attr
.SetBulletStyle(bulletStyle
);
7517 attr
.SetBulletNumber(bulletNumber
);
7518 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7520 return BeginStyle(attr
);
7523 /// Begin symbol bullet
7524 bool wxRichTextBuffer
::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7526 wxRichTextAttr attr
;
7527 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7528 attr
.SetBulletStyle(bulletStyle
);
7529 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7530 attr
.SetBulletText(symbol
);
7532 return BeginStyle(attr
);
7535 /// Begin standard bullet
7536 bool wxRichTextBuffer
::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7538 wxRichTextAttr attr
;
7539 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7540 attr
.SetBulletStyle(bulletStyle
);
7541 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7542 attr
.SetBulletName(bulletName
);
7544 return BeginStyle(attr
);
7547 /// Begin named character style
7548 bool wxRichTextBuffer
::BeginCharacterStyle(const wxString
& characterStyle
)
7550 if (GetStyleSheet())
7552 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7555 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7556 return BeginStyle(attr
);
7562 /// Begin named paragraph style
7563 bool wxRichTextBuffer
::BeginParagraphStyle(const wxString
& paragraphStyle
)
7565 if (GetStyleSheet())
7567 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7570 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7571 return BeginStyle(attr
);
7577 /// Begin named list style
7578 bool wxRichTextBuffer
::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7580 if (GetStyleSheet())
7582 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7585 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7587 attr
.SetBulletNumber(number
);
7589 return BeginStyle(attr
);
7596 bool wxRichTextBuffer
::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7598 wxRichTextAttr attr
;
7600 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7602 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7605 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7610 return BeginStyle(attr
);
7613 /// Adds a handler to the end
7614 void wxRichTextBuffer
::AddHandler(wxRichTextFileHandler
*handler
)
7616 sm_handlers
.Append(handler
);
7619 /// Inserts a handler at the front
7620 void wxRichTextBuffer
::InsertHandler(wxRichTextFileHandler
*handler
)
7622 sm_handlers
.Insert( handler
);
7625 /// Removes a handler
7626 bool wxRichTextBuffer
::RemoveHandler(const wxString
& name
)
7628 wxRichTextFileHandler
*handler
= FindHandler(name
);
7631 sm_handlers
.DeleteObject(handler
);
7639 /// Finds a handler by filename or, if supplied, type
7640 wxRichTextFileHandler
*wxRichTextBuffer
::FindHandlerFilenameOrType(const wxString
& filename
,
7641 wxRichTextFileType imageType
)
7643 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7644 return FindHandler(imageType
);
7645 else if (!filename
.IsEmpty())
7647 wxString path
, file
, ext
;
7648 wxFileName
::SplitPath(filename
, & path
, & file
, & ext
);
7649 return FindHandler(ext
, imageType
);
7656 /// Finds a handler by name
7657 wxRichTextFileHandler
* wxRichTextBuffer
::FindHandler(const wxString
& name
)
7659 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
7662 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7663 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7665 node
= node
->GetNext();
7670 /// Finds a handler by extension and type
7671 wxRichTextFileHandler
* wxRichTextBuffer
::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7673 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
7676 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7677 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7678 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7680 node
= node
->GetNext();
7685 /// Finds a handler by type
7686 wxRichTextFileHandler
* wxRichTextBuffer
::FindHandler(wxRichTextFileType type
)
7688 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
7691 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7692 if (handler
->GetType() == type
) return handler
;
7693 node
= node
->GetNext();
7698 void wxRichTextBuffer
::InitStandardHandlers()
7700 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7701 AddHandler(new wxRichTextPlainTextHandler
);
7704 void wxRichTextBuffer
::CleanUpHandlers()
7706 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
7709 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7710 wxList
::compatibility_iterator next
= node
->GetNext();
7715 sm_handlers
.Clear();
7718 wxString wxRichTextBuffer
::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7725 wxList
::compatibility_iterator node
= GetHandlers().GetFirst();
7729 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7730 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7735 wildcard
+= wxT(";");
7736 wildcard
+= wxT("*.") + handler
->GetExtension();
7741 wildcard
+= wxT("|");
7742 wildcard
+= handler
->GetName();
7743 wildcard
+= wxT(" ");
7744 wildcard
+= _("files");
7745 wildcard
+= wxT(" (*.");
7746 wildcard
+= handler
->GetExtension();
7747 wildcard
+= wxT(")|*.");
7748 wildcard
+= handler
->GetExtension();
7750 types
->Add(handler
->GetType());
7755 node
= node
->GetNext();
7759 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7764 bool wxRichTextBuffer
::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7766 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7769 SetDefaultStyle(wxRichTextAttr());
7770 handler
->SetFlags(GetHandlerFlags());
7771 bool success
= handler
->LoadFile(this, filename
);
7772 Invalidate(wxRICHTEXT_ALL
);
7780 bool wxRichTextBuffer
::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7782 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7785 handler
->SetFlags(GetHandlerFlags());
7786 return handler
->SaveFile(this, filename
);
7792 /// Load from a stream
7793 bool wxRichTextBuffer
::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7795 wxRichTextFileHandler
* handler
= FindHandler(type
);
7798 SetDefaultStyle(wxRichTextAttr());
7799 handler
->SetFlags(GetHandlerFlags());
7800 bool success
= handler
->LoadFile(this, stream
);
7801 Invalidate(wxRICHTEXT_ALL
);
7808 /// Save to a stream
7809 bool wxRichTextBuffer
::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7811 wxRichTextFileHandler
* handler
= FindHandler(type
);
7814 handler
->SetFlags(GetHandlerFlags());
7815 return handler
->SaveFile(this, stream
);
7821 /// Copy the range to the clipboard
7822 bool wxRichTextBuffer
::CopyToClipboard(const wxRichTextRange
& range
)
7824 bool success
= false;
7825 wxRichTextParagraphLayoutBox
* container
= this;
7826 if (GetRichTextCtrl())
7827 container
= GetRichTextCtrl()->GetFocusObject();
7829 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7831 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7833 wxTheClipboard
->Clear();
7835 // Add composite object
7837 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7840 wxString text
= container
->GetTextForRange(range
);
7843 text
= wxTextFile
::Translate(text
, wxTextFileType_Dos
);
7846 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7849 // Add rich text buffer data object. This needs the XML handler to be present.
7851 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7853 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7854 container
->CopyFragment(range
, *richTextBuf
);
7856 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7859 if (wxTheClipboard
->SetData(compositeObject
))
7862 wxTheClipboard
->Close();
7871 /// Paste the clipboard content to the buffer
7872 bool wxRichTextBuffer
::PasteFromClipboard(long position
)
7874 bool success
= false;
7875 wxRichTextParagraphLayoutBox
* container
= this;
7876 if (GetRichTextCtrl())
7877 container
= GetRichTextCtrl()->GetFocusObject();
7879 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7880 if (CanPasteFromClipboard())
7882 if (wxTheClipboard
->Open())
7884 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject
::GetRichTextBufferFormatId())))
7886 wxRichTextBufferDataObject data
;
7887 wxTheClipboard
->GetData(data
);
7888 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7891 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7892 if (GetRichTextCtrl())
7893 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7894 delete richTextBuffer
;
7897 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7899 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7903 wxTextDataObject data
;
7904 wxTheClipboard
->GetData(data
);
7905 wxString
text(data
.GetText());
7908 text2
.Alloc(text
.Length()+1);
7910 for (i
= 0; i
< text
.Length(); i
++)
7912 wxChar ch
= text
[i
];
7913 if (ch
!= wxT('\r'))
7917 wxString text2
= text
;
7919 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7921 if (GetRichTextCtrl())
7922 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7926 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7928 wxBitmapDataObject data
;
7929 wxTheClipboard
->GetData(data
);
7930 wxBitmap
bitmap(data
.GetBitmap());
7931 wxImage
image(bitmap
.ConvertToImage());
7933 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7935 action
->GetNewParagraphs().AddImage(image
);
7937 if (action
->GetNewParagraphs().GetChildCount() == 1)
7938 action
->GetNewParagraphs().SetPartialParagraph(true);
7940 action
->SetPosition(position
+1);
7942 // Set the range we'll need to delete in Undo
7943 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7945 SubmitAction(action
);
7949 wxTheClipboard
->Close();
7953 wxUnusedVar(position
);
7958 /// Can we paste from the clipboard?
7959 bool wxRichTextBuffer
::CanPasteFromClipboard() const
7961 bool canPaste
= false;
7962 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7963 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7965 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7967 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7969 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject
::GetRichTextBufferFormatId())) ||
7970 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7974 wxTheClipboard
->Close();
7980 /// Dumps contents of buffer for debugging purposes
7981 void wxRichTextBuffer
::Dump()
7985 wxStringOutputStream
stream(& text
);
7986 wxTextOutputStream
textStream(stream
);
7993 /// Add an event handler
7994 bool wxRichTextBuffer
::AddEventHandler(wxEvtHandler
* handler
)
7996 m_eventHandlers
.Append(handler
);
8000 /// Remove an event handler
8001 bool wxRichTextBuffer
::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8003 wxList
::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8006 m_eventHandlers
.Erase(node
);
8016 /// Clear event handlers
8017 void wxRichTextBuffer
::ClearEventHandlers()
8019 m_eventHandlers
.Clear();
8022 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8023 /// otherwise will stop at the first successful one.
8024 bool wxRichTextBuffer
::SendEvent(wxEvent
& event
, bool sendToAll
)
8026 bool success
= false;
8027 for (wxList
::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8029 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8030 if (handler
->ProcessEvent(event
))
8040 /// Set style sheet and notify of the change
8041 bool wxRichTextBuffer
::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8043 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8045 wxWindowID winid
= wxID_ANY
;
8046 if (GetRichTextCtrl())
8047 winid
= GetRichTextCtrl()->GetId();
8049 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8050 event
.SetEventObject(GetRichTextCtrl());
8051 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8052 event
.SetOldStyleSheet(oldSheet
);
8053 event
.SetNewStyleSheet(sheet
);
8056 if (SendEvent(event
) && !event
.IsAllowed())
8058 if (sheet
!= oldSheet
)
8064 if (oldSheet
&& oldSheet
!= sheet
)
8067 SetStyleSheet(sheet
);
8069 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8070 event
.SetOldStyleSheet(NULL
);
8073 return SendEvent(event
);
8076 /// Set renderer, deleting old one
8077 void wxRichTextBuffer
::SetRenderer(wxRichTextRenderer
* renderer
)
8081 sm_renderer
= renderer
;
8084 /// Hit-testing: returns a flag indicating hit test details, plus
8085 /// information about position
8086 int wxRichTextBuffer
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8088 int ret
= wxRichTextParagraphLayoutBox
::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8089 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8095 textPosition
= m_ownRange
.GetEnd()-1;
8098 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8102 bool wxRichTextStdRenderer
::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8104 if (bulletAttr
.GetTextColour().IsOk())
8106 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8107 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8111 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8112 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8116 if (bulletAttr
.HasFont())
8118 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8121 font
= (*wxNORMAL_FONT
);
8123 wxCheckSetFont(dc
, font
);
8125 int charHeight
= dc
.GetCharHeight();
8127 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer
::GetBulletProportion());
8128 int bulletHeight
= bulletWidth
;
8132 // Calculate the top position of the character (as opposed to the whole line height)
8133 int y
= rect
.y
+ (rect
.height
- charHeight
);
8135 // Calculate where the bullet should be positioned
8136 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8138 // The margin between a bullet and text.
8139 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer
::GetBulletRightMargin());
8141 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8142 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8143 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8144 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8146 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8148 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8150 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8153 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8154 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8155 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8156 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8158 dc
.DrawPolygon(4, pts
);
8160 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8163 pts
[0].x
= x
; pts
[0].y
= y
;
8164 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8165 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8167 dc
.DrawPolygon(3, pts
);
8169 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8171 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8172 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8174 else // "standard/circle", and catch-all
8176 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8182 bool wxRichTextStdRenderer
::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8187 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8189 wxRichTextAttr fontAttr
;
8190 fontAttr
.SetFontSize(attr
.GetFontSize());
8191 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8192 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8193 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8194 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8195 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8197 else if (attr
.HasFont())
8198 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8200 font
= (*wxNORMAL_FONT
);
8202 wxCheckSetFont(dc
, font
);
8204 if (attr
.GetTextColour().IsOk())
8205 dc
.SetTextForeground(attr
.GetTextColour());
8207 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8209 int charHeight
= dc
.GetCharHeight();
8211 dc
.GetTextExtent(text
, & tw
, & th
);
8215 // Calculate the top position of the character (as opposed to the whole line height)
8216 int y
= rect
.y
+ (rect
.height
- charHeight
);
8218 // The margin between a bullet and text.
8219 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer
::GetBulletRightMargin());
8221 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8222 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8223 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8224 x
= x
+ (rect
.width
)/2 - tw
/2;
8226 dc
.DrawText(text
, x
, y
);
8234 bool wxRichTextStdRenderer
::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8236 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8237 // with the buffer. The store will allow retrieval from memory, disk or other means.
8241 /// Enumerate the standard bullet names currently supported
8242 bool wxRichTextStdRenderer
::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8244 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8245 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8246 bulletNames
.Add(wxTRANSLATE("standard/square"));
8247 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8248 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8257 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8259 wxRichTextBox
::wxRichTextBox(wxRichTextObject
* parent
):
8260 wxRichTextParagraphLayoutBox(parent
)
8265 bool wxRichTextBox
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8270 // TODO: if the active object in the control, draw an indication.
8271 // We need to add the concept of active object, and not just focus object,
8272 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8273 // Ultimately we would like to be able to interactively resize an active object
8274 // using drag handles.
8275 return wxRichTextParagraphLayoutBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8279 void wxRichTextBox
::Copy(const wxRichTextBox
& obj
)
8281 wxRichTextParagraphLayoutBox
::Copy(obj
);
8284 // Edit properties via a GUI
8285 bool wxRichTextBox
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8287 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8288 boxDlg
.SetAttributes(GetAttributes());
8290 if (boxDlg
.ShowModal() == wxID_OK
)
8292 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8293 // indeterminate in the object.
8294 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8301 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8303 wxRichTextCell
::wxRichTextCell(wxRichTextObject
* parent
):
8304 wxRichTextBox(parent
)
8309 bool wxRichTextCell
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8311 return wxRichTextBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8315 void wxRichTextCell
::Copy(const wxRichTextCell
& obj
)
8317 wxRichTextBox
::Copy(obj
);
8320 // Edit properties via a GUI
8321 bool wxRichTextCell
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8323 // We need to gather common attributes for all selected cells.
8325 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8326 bool multipleCells
= false;
8327 wxRichTextAttr attr
;
8329 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8330 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8332 wxRichTextAttr clashingAttr
, absentAttr
;
8333 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8335 int selectedCellCount
= 0;
8336 for (i
= 0; i
< sel
.GetCount(); i
++)
8338 const wxRichTextRange
& range
= sel
[i
];
8339 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8342 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8344 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8346 selectedCellCount
++;
8349 multipleCells
= selectedCellCount
> 1;
8353 attr
= GetAttributes();
8358 caption
= _("Multiple Cell Properties");
8360 caption
= _("Cell Properties");
8362 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8363 cellDlg
.SetAttributes(attr
);
8365 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8368 // We don't want position and floating controls for a cell.
8369 sizePage
->ShowPositionControls(false);
8370 sizePage
->ShowFloatingControls(false);
8373 if (cellDlg
.ShowModal() == wxID_OK
)
8377 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8378 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8379 // since it may represent clashing attributes across multiple objects.
8380 table
->SetCellStyle(sel
, attr
);
8383 // For a single object, indeterminate attributes set by the user should be reflected in the
8384 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8385 // the style directly instead of applying (which ignores indeterminate attributes,
8386 // leaving them as they were).
8387 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8394 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8396 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8398 wxRichTextTable
::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8404 // Draws the object.
8405 bool wxRichTextTable
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8407 return wxRichTextBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8410 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8411 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8413 // Lays the object out. rect is the space available for layout. Often it will
8414 // be the specified overall space for this object, if trying to constrain
8415 // layout to a particular size, or it could be the total space available in the
8416 // parent. rect is the overall size, so we must subtract margins and padding.
8417 // to get the actual available space.
8418 bool wxRichTextTable
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8420 SetPosition(rect
.GetPosition());
8422 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8423 // minimum size if within alloted size, then divide up remaining size
8424 // between rows/cols.
8427 wxRichTextBuffer
* buffer
= GetBuffer();
8428 if (buffer
) scale
= buffer
->GetScale();
8430 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8431 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8433 wxRichTextAttr
attr(GetAttributes());
8434 context
.ApplyVirtualAttributes(attr
, this);
8436 // If we have no fixed table size, and assuming we're not pushed for
8437 // space, then we don't have to try to stretch the table to fit the contents.
8438 bool stretchToFitTableWidth
= false;
8440 int tableWidth
= rect
.width
;
8441 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8443 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8445 // Fixed table width, so we do want to stretch columns out if necessary.
8446 stretchToFitTableWidth
= true;
8448 // Shouldn't be able to exceed the size passed to this function
8449 tableWidth
= wxMin(rect
.width
, tableWidth
);
8452 // Get internal padding
8453 int paddingLeft
= 0, paddingTop
= 0;
8454 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8455 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8456 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8457 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8459 // Assume that left and top padding are also used for inter-cell padding.
8460 int paddingX
= paddingLeft
;
8461 int paddingY
= paddingTop
;
8463 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8464 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8466 // Internal table width - the area for content
8467 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8469 int rowCount
= m_cells
.GetCount();
8470 if (m_colCount
== 0 || rowCount
== 0)
8472 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8473 SetCachedSize(overallRect
.GetSize());
8475 // Zero content size
8476 SetMinSize(overallRect
.GetSize());
8477 SetMaxSize(GetMinSize());
8481 // The final calculated widths
8482 wxArrayInt colWidths
;
8483 colWidths
.Add(0, m_colCount
);
8485 wxArrayInt absoluteColWidths
;
8486 absoluteColWidths
.Add(0, m_colCount
);
8487 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8488 wxArrayInt percentageColWidths
;
8489 percentageColWidths
.Add(0, m_colCount
);
8490 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8491 // These are only relevant when the first column contains spanning information.
8492 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8493 wxArrayInt maxColWidths
;
8494 maxColWidths
.Add(0, m_colCount
);
8495 wxArrayInt minColWidths
;
8496 minColWidths
.Add(0, m_colCount
);
8498 wxSize
tableSize(tableWidth
, 0);
8502 for (i
= 0; i
< m_colCount
; i
++)
8504 absoluteColWidths
[i
] = 0;
8505 // absoluteColWidthsSpanning[i] = 0;
8506 percentageColWidths
[i
] = -1;
8507 // percentageColWidthsSpanning[i] = -1;
8509 maxColWidths
[i
] = 0;
8510 minColWidths
[i
] = 0;
8511 // columnSpans[i] = 1;
8514 // (0) Determine which cells are visible according to spans
8516 // __________________
8521 // |------------------|
8522 // |__________________| 4
8524 // To calculate cell visibility:
8525 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8526 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8527 // that cell, hide the cell.
8529 // We can also use this array to match the size of spanning cells to the grid. Or just do
8530 // this when we iterate through all cells.
8532 // 0.1: add spanning cells to an array
8533 wxRichTextRectArray rectArray
;
8534 for (j
= 0; j
< m_rowCount
; j
++)
8536 for (i
= 0; i
< m_colCount
; i
++)
8538 wxRichTextBox
* cell
= GetCell(j
, i
);
8539 int colSpan
= 1, rowSpan
= 1;
8540 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8541 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8542 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8543 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8544 if (colSpan
> 1 || rowSpan
> 1)
8546 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8550 // 0.2: find which cells are subsumed by a spanning cell
8551 for (j
= 0; j
< m_rowCount
; j
++)
8553 for (i
= 0; i
< m_colCount
; i
++)
8555 wxRichTextBox
* cell
= GetCell(j
, i
);
8556 if (rectArray
.GetCount() == 0)
8562 int colSpan
= 1, rowSpan
= 1;
8563 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8564 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8565 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8566 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8567 if (colSpan
> 1 || rowSpan
> 1)
8569 // Assume all spanning cells are shown
8575 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8577 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8589 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8590 // overlap with a spanned cell starting at a previous column position.
8591 // This means we need to keep an array of rects so we can check. However
8592 // it does also mean that some spans simply may not be taken into account
8593 // where there are different spans happening on different rows. In these cases,
8594 // they will simply be as wide as their constituent columns.
8596 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8597 // the absolute or percentage width of each column.
8599 for (j
= 0; j
< m_rowCount
; j
++)
8601 // First get the overall margins so we can calculate percentage widths based on
8602 // the available content space for all cells on the row
8604 int overallRowContentMargin
= 0;
8605 int visibleCellCount
= 0;
8607 for (i
= 0; i
< m_colCount
; i
++)
8609 wxRichTextBox
* cell
= GetCell(j
, i
);
8610 if (cell
->IsShown())
8612 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8613 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8615 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8616 visibleCellCount
++;
8620 // Add in inter-cell padding
8621 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8623 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8624 wxSize
rowTableSize(rowContentWidth
, 0);
8625 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8627 for (i
= 0; i
< m_colCount
; i
++)
8629 wxRichTextBox
* cell
= GetCell(j
, i
);
8630 if (cell
->IsShown())
8633 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8634 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8636 // Lay out cell to find min/max widths
8637 cell
->Invalidate(wxRICHTEXT_ALL
);
8638 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
8642 int absoluteCellWidth
= -1;
8643 int percentageCellWidth
= -1;
8645 // I think we need to calculate percentages from the internal table size,
8646 // minus the padding between cells which we'll need to calculate from the
8647 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8648 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8649 // so if we want to conform to that we'll need to add in the overall cell margins.
8650 // However, this will make it difficult to specify percentages that add up to
8651 // 100% and still fit within the table width.
8652 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8653 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8654 // If we're using internal content size for the width, we would calculate the
8655 // the overall cell width for n cells as:
8656 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8657 // + thisOverallCellMargin
8658 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8659 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8661 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8663 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8664 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8666 percentageCellWidth
= w
;
8670 absoluteCellWidth
= w
;
8672 // Override absolute width with minimum width if necessary
8673 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8674 absoluteCellWidth
= cell
->GetMinSize().x
;
8677 if (absoluteCellWidth
!= -1)
8679 if (absoluteCellWidth
> absoluteColWidths
[i
])
8680 absoluteColWidths
[i
] = absoluteCellWidth
;
8683 if (percentageCellWidth
!= -1)
8685 if (percentageCellWidth
> percentageColWidths
[i
])
8686 percentageColWidths
[i
] = percentageCellWidth
;
8689 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8690 minColWidths
[i
] = cell
->GetMinSize().x
;
8691 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8692 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8698 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8699 // TODO: simply merge this into (1).
8700 for (i
= 0; i
< m_colCount
; i
++)
8702 if (absoluteColWidths
[i
] > 0)
8704 colWidths
[i
] = absoluteColWidths
[i
];
8706 else if (percentageColWidths
[i
] > 0)
8708 colWidths
[i
] = percentageColWidths
[i
];
8710 // This is rubbish - we calculated the absolute widths from percentages, so
8711 // we can't do it again here.
8712 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8716 // (3) Process absolute or proportional widths of spanning columns,
8717 // now that we know what our fixed column widths are going to be.
8718 // Spanned cells will try to adjust columns so the span will fit.
8719 // Even existing fixed column widths can be expanded if necessary.
8720 // Actually, currently fixed columns widths aren't adjusted; instead,
8721 // the algorithm favours earlier rows and adjusts unspecified column widths
8722 // the first time only. After that, we can't know whether the column has been
8723 // specified explicitly or not. (We could make a note if necessary.)
8724 for (j
= 0; j
< m_rowCount
; j
++)
8726 // First get the overall margins so we can calculate percentage widths based on
8727 // the available content space for all cells on the row
8729 int overallRowContentMargin
= 0;
8730 int visibleCellCount
= 0;
8732 for (i
= 0; i
< m_colCount
; i
++)
8734 wxRichTextBox
* cell
= GetCell(j
, i
);
8735 if (cell
->IsShown())
8737 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8738 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8740 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8741 visibleCellCount
++;
8745 // Add in inter-cell padding
8746 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8748 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8749 wxSize
rowTableSize(rowContentWidth
, 0);
8750 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8752 for (i
= 0; i
< m_colCount
; i
++)
8754 wxRichTextBox
* cell
= GetCell(j
, i
);
8755 if (cell
->IsShown())
8758 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8759 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8763 int spans
= wxMin(colSpan
, m_colCount
- i
);
8767 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8769 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8770 // Override absolute width with minimum width if necessary
8771 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8772 cellWidth
= cell
->GetMinSize().x
;
8776 // Do we want to do this? It's the only chance we get to
8777 // use the cell's min/max sizes, so we need to work out
8778 // how we're going to balance the unspecified spanning cell
8779 // width with the possibility more-constrained constituent cell widths.
8780 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8781 // don't want to constraint all the spanned columns to fit into this cell.
8782 // OK, let's say that if any of the constituent columns don't fit,
8783 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8784 // cells to the columns later.
8785 cellWidth
= cell
->GetMinSize().x
;
8786 if (cell
->GetMaxSize().x
> cellWidth
)
8787 cellWidth
= cell
->GetMaxSize().x
;
8790 // Subtract the padding between cells
8791 int spanningWidth
= cellWidth
;
8792 spanningWidth
-= paddingX
* (spans
-1);
8794 if (spanningWidth
> 0)
8796 // Now share the spanning width between columns within that span
8797 // TODO: take into account min widths of columns within the span
8798 int spanningWidthLeft
= spanningWidth
;
8799 int stretchColCount
= 0;
8800 for (k
= i
; k
< (i
+spans
); k
++)
8802 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8803 spanningWidthLeft
-= colWidths
[k
];
8807 // Now divide what's left between the remaining columns
8809 if (stretchColCount
> 0)
8810 colShare
= spanningWidthLeft
/ stretchColCount
;
8811 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8813 // If fixed-width columns are currently too big, then we'll later
8814 // stretch the spanned cell to fit.
8816 if (spanningWidthLeft
> 0)
8818 for (k
= i
; k
< (i
+spans
); k
++)
8820 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8822 int newWidth
= colShare
;
8823 if (k
== (i
+spans
-1))
8824 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8825 colWidths
[k
] = newWidth
;
8836 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8837 // TODO: take into account min widths of columns within the span
8838 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8839 int widthLeft
= tableWidthMinusPadding
;
8840 int stretchColCount
= 0;
8841 for (i
= 0; i
< m_colCount
; i
++)
8843 // TODO: we need to take into account min widths.
8844 // Subtract min width from width left, then
8845 // add the colShare to the min width
8846 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8847 widthLeft
-= colWidths
[i
];
8850 if (minColWidths
[i
] > 0)
8851 widthLeft
-= minColWidths
[i
];
8857 // Now divide what's left between the remaining columns
8859 if (stretchColCount
> 0)
8860 colShare
= widthLeft
/ stretchColCount
;
8861 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8863 // Check we don't have enough space, in which case shrink all columns, overriding
8864 // any absolute/proportional widths
8865 // TODO: actually we would like to divide up the shrinkage according to size.
8866 // How do we calculate the proportions that will achieve this?
8867 // Could first choose an arbitrary value for stretching cells, and then calculate
8868 // factors to multiply each width by.
8869 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8870 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8872 colShare
= tableWidthMinusPadding
/ m_colCount
;
8873 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8874 for (i
= 0; i
< m_colCount
; i
++)
8877 minColWidths
[i
] = 0;
8881 // We have to adjust the columns if either we need to shrink the
8882 // table to fit the parent/table width, or we explicitly set the
8883 // table width and need to stretch out the table.
8884 if (widthLeft
< 0 || stretchToFitTableWidth
)
8886 for (i
= 0; i
< m_colCount
; i
++)
8888 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8890 if (minColWidths
[i
] > 0)
8891 colWidths
[i
] = minColWidths
[i
] + colShare
;
8893 colWidths
[i
] = colShare
;
8894 if (i
== (m_colCount
-1))
8895 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8900 // TODO: if spanned cells have no specified or max width, make them the
8901 // as big as the columns they span. Do this for all spanned cells in all
8902 // rows, of course. Size any spanned cells left over at the end - even if they
8903 // have width > 0, make sure they're limited to the appropriate column edge.
8907 Sort out confusion between content width
8908 and overall width later. For now, assume we specify overall width.
8910 So, now we've laid out the table to fit into the given space
8911 and have used specified widths and minimum widths.
8913 Now we need to consider how we will try to take maximum width into account.
8917 // (??) TODO: take max width into account
8919 // (6) Lay out all cells again with the current values
8922 int y
= availableSpace
.y
;
8923 for (j
= 0; j
< m_rowCount
; j
++)
8925 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8926 int maxCellHeight
= 0;
8927 int maxSpecifiedCellHeight
= 0;
8929 wxArrayInt actualWidths
;
8930 actualWidths
.Add(0, m_colCount
);
8932 wxTextAttrDimensionConverter
converter(dc
, scale
);
8933 for (i
= 0; i
< m_colCount
; i
++)
8935 wxRichTextCell
* cell
= GetCell(j
, i
);
8936 if (cell
->IsShown())
8938 // Get max specified cell height
8939 // Don't handle percentages for height
8940 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8942 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8943 if (h
> maxSpecifiedCellHeight
)
8944 maxSpecifiedCellHeight
= h
;
8947 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8950 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8951 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8953 wxRect availableCellSpace
;
8955 // TODO: take into acount spans
8958 // Calculate the size of this spanning cell from its constituent columns
8960 int spans
= wxMin(colSpan
, m_colCount
- i
);
8961 for (k
= i
; k
< spans
; k
++)
8967 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8970 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8972 // Store actual width so we can force cell to be the appropriate width on the final loop
8973 actualWidths
[i
] = availableCellSpace
.GetWidth();
8976 cell
->Invalidate(wxRICHTEXT_ALL
);
8977 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8979 // TODO: use GetCachedSize().x to compute 'natural' size
8981 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8982 if (cell
->GetCachedSize().y
> maxCellHeight
)
8983 maxCellHeight
= cell
->GetCachedSize().y
;
8988 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8990 for (i
= 0; i
< m_colCount
; i
++)
8992 wxRichTextCell
* cell
= GetCell(j
, i
);
8993 if (cell
->IsShown())
8995 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8996 // Lay out cell with new height
8997 cell
->Invalidate(wxRICHTEXT_ALL
);
8998 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9000 // Make sure the cell size really is the appropriate size,
9001 // not the calculated box size
9002 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9004 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9009 if (j
< (m_rowCount
-1))
9013 // We need to add back the margins etc.
9015 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9016 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9017 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9018 SetCachedSize(marginRect
.GetSize());
9021 // TODO: calculate max size
9023 SetMaxSize(GetCachedSize());
9026 // TODO: calculate min size
9028 SetMinSize(GetCachedSize());
9031 // TODO: currently we use either a fixed table width or the parent's size.
9032 // We also want to be able to calculate the table width from its content,
9033 // whether using fixed column widths or cell content min/max width.
9034 // Probably need a boolean flag to say whether we need to stretch cells
9035 // to fit the table width, or to simply use min/max cell widths. The
9036 // trouble with this is that if cell widths are not specified, they
9037 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9038 // Anyway, ignoring that problem, we probably need to factor layout into a function
9039 // that can can calculate the maximum unconstrained layout in case table size is
9040 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9041 // constrain Layout(), or the previously-calculated max size to constraint layout.
9046 // Finds the absolute position and row height for the given character position
9047 bool wxRichTextTable
::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9049 wxRichTextCell
* child
= GetCell(index
+1);
9052 // Find the position at the start of the child cell, since the table doesn't
9053 // have any caret position of its own.
9054 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9060 // Get the cell at the given character position (in the range of the table).
9061 wxRichTextCell
* wxRichTextTable
::GetCell(long pos
) const
9063 int row
= 0, col
= 0;
9064 if (GetCellRowColumnPosition(pos
, row
, col
))
9066 return GetCell(row
, col
);
9072 // Get the row/column for a given character position
9073 bool wxRichTextTable
::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9075 if (m_colCount
== 0 || m_rowCount
== 0)
9078 row
= (int) (pos
/ m_colCount
);
9079 col
= pos
- (row
* m_colCount
);
9081 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9083 if (row
< m_rowCount
&& col
< m_colCount
)
9089 // Calculate range, taking row/cell ordering into account instead of relying
9090 // on list ordering.
9091 void wxRichTextTable
::CalculateRange(long start
, long& end
)
9093 long current
= start
;
9094 long lastEnd
= current
;
9103 for (i
= 0; i
< m_rowCount
; i
++)
9105 for (j
= 0; j
< m_colCount
; j
++)
9107 wxRichTextCell
* child
= GetCell(i
, j
);
9112 child
->CalculateRange(current
, childEnd
);
9115 current
= childEnd
+ 1;
9120 // A top-level object always has a range of size 1,
9121 // because its children don't count at this level.
9123 m_range
.SetRange(start
, start
);
9125 // An object with no children has zero length
9126 if (m_children
.GetCount() == 0)
9128 m_ownRange
.SetRange(0, lastEnd
);
9131 // Gets the range size.
9132 bool wxRichTextTable
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9134 return wxRichTextBox
::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9137 // Deletes content in the given range.
9138 bool wxRichTextTable
::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9140 // TODO: implement deletion of cells
9144 // Gets any text in this object for the given range.
9145 wxString wxRichTextTable
::GetTextForRange(const wxRichTextRange
& range
) const
9147 return wxRichTextBox
::GetTextForRange(range
);
9150 // Copies this object.
9151 void wxRichTextTable
::Copy(const wxRichTextTable
& obj
)
9153 wxRichTextBox
::Copy(obj
);
9157 m_rowCount
= obj
.m_rowCount
;
9158 m_colCount
= obj
.m_colCount
;
9160 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9163 for (i
= 0; i
< m_rowCount
; i
++)
9165 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9166 for (j
= 0; j
< m_colCount
; j
++)
9168 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9176 void wxRichTextTable
::ClearTable()
9182 bool wxRichTextTable
::CreateTable(int rows
, int cols
)
9189 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9192 for (i
= 0; i
< rows
; i
++)
9194 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9195 for (j
= 0; j
< cols
; j
++)
9197 wxRichTextCell
* cell
= new wxRichTextCell
;
9199 cell
->AddParagraph(wxEmptyString
);
9208 wxRichTextCell
* wxRichTextTable
::GetCell(int row
, int col
) const
9210 wxASSERT(row
< m_rowCount
);
9211 wxASSERT(col
< m_colCount
);
9213 if (row
< m_rowCount
&& col
< m_colCount
)
9215 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9216 wxRichTextObject
* obj
= colArray
[col
];
9217 return wxDynamicCast(obj
, wxRichTextCell
);
9223 // Returns a selection object specifying the selections between start and end character positions.
9224 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9225 wxRichTextSelection wxRichTextTable
::GetSelection(long start
, long end
) const
9227 wxRichTextSelection selection
;
9228 selection
.SetContainer((wxRichTextTable
*) this);
9237 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9239 if (end
>= (m_colCount
* m_rowCount
))
9242 // We need to find the rectangle of cells that is described by the rectangle
9243 // with start, end as the diagonal. Make sure we don't add cells that are
9244 // not currenty visible because they are overlapped by spanning cells.
9246 --------------------------
9247 | 0 | 1 | 2 | 3 | 4 |
9248 --------------------------
9249 | 5 | 6 | 7 | 8 | 9 |
9250 --------------------------
9251 | 10 | 11 | 12 | 13 | 14 |
9252 --------------------------
9253 | 15 | 16 | 17 | 18 | 19 |
9254 --------------------------
9256 Let's say we select 6 -> 18.
9258 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9259 which is left and which is right.
9261 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9263 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9269 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9270 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9272 int topRow
= int(start
/m_colCount
);
9273 int bottomRow
= int(end
/m_colCount
);
9275 if (leftCol
> rightCol
)
9282 if (topRow
> bottomRow
)
9284 int tmp
= bottomRow
;
9290 for (i
= topRow
; i
<= bottomRow
; i
++)
9292 for (j
= leftCol
; j
<= rightCol
; j
++)
9294 wxRichTextCell
* cell
= GetCell(i
, j
);
9295 if (cell
&& cell
->IsShown())
9296 selection
.Add(cell
->GetRange());
9303 // Sets the attributes for the cells specified by the selection.
9304 bool wxRichTextTable
::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9306 if (selection
.GetContainer() != this)
9309 wxRichTextBuffer
* buffer
= GetBuffer();
9310 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9311 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9314 buffer
->BeginBatchUndo(_("Set Cell Style"));
9316 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
9319 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9320 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9321 SetStyle(cell
, style
, flags
);
9322 node
= node
->GetNext();
9325 // Do action, or delay it until end of batch.
9327 buffer
->EndBatchUndo();
9332 bool wxRichTextTable
::DeleteRows(int startRow
, int noRows
)
9334 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9335 if ((startRow
+ noRows
) >= m_rowCount
)
9339 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9341 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9342 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9344 wxRichTextObject
* cell
= colArray
[j
];
9345 RemoveChild(cell
, true);
9348 // Keep deleting at the same position, since we move all
9350 m_cells
.RemoveAt(startRow
);
9353 m_rowCount
= m_rowCount
- noRows
;
9358 bool wxRichTextTable
::DeleteColumns(int startCol
, int noCols
)
9360 wxASSERT((startCol
+ noCols
) < m_colCount
);
9361 if ((startCol
+ noCols
) >= m_colCount
)
9364 bool deleteRows
= (noCols
== m_colCount
);
9367 for (i
= 0; i
< m_rowCount
; i
++)
9369 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows ?
0 : i
];
9370 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9372 wxRichTextObject
* cell
= colArray
[j
];
9373 RemoveChild(cell
, true);
9377 m_cells
.RemoveAt(0);
9382 m_colCount
= m_colCount
- noCols
;
9387 bool wxRichTextTable
::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9389 wxASSERT(startRow
<= m_rowCount
);
9390 if (startRow
> m_rowCount
)
9394 for (i
= 0; i
< noRows
; i
++)
9397 if (startRow
== m_rowCount
)
9399 m_cells
.Add(wxRichTextObjectPtrArray());
9400 idx
= m_cells
.GetCount() - 1;
9404 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9408 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9409 for (j
= 0; j
< m_colCount
; j
++)
9411 wxRichTextCell
* cell
= new wxRichTextCell
;
9412 cell
->GetAttributes() = attr
;
9419 m_rowCount
= m_rowCount
+ noRows
;
9423 bool wxRichTextTable
::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9425 wxASSERT(startCol
<= m_colCount
);
9426 if (startCol
> m_colCount
)
9430 for (i
= 0; i
< m_rowCount
; i
++)
9432 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9433 for (j
= 0; j
< noCols
; j
++)
9435 wxRichTextCell
* cell
= new wxRichTextCell
;
9436 cell
->GetAttributes() = attr
;
9440 if (startCol
== m_colCount
)
9443 colArray
.Insert(cell
, startCol
+j
);
9447 m_colCount
= m_colCount
+ noCols
;
9452 // Edit properties via a GUI
9453 bool wxRichTextTable
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9455 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9456 boxDlg
.SetAttributes(GetAttributes());
9458 if (boxDlg
.ShowModal() == wxID_OK
)
9460 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9468 * Module to initialise and clean up handlers
9471 class wxRichTextModule
: public wxModule
9473 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9475 wxRichTextModule() {}
9478 wxRichTextBuffer
::SetRenderer(new wxRichTextStdRenderer
);
9479 wxRichTextBuffer
::InitStandardHandlers();
9480 wxRichTextParagraph
::InitDefaultTabs();
9485 wxRichTextBuffer
::CleanUpHandlers();
9486 wxRichTextBuffer
::CleanUpDrawingHandlers();
9487 wxRichTextDecimalToRoman(-1);
9488 wxRichTextParagraph
::ClearDefaultTabs();
9489 wxRichTextCtrl
::ClearAvailableFontNames();
9490 wxRichTextBuffer
::SetRenderer(NULL
);
9494 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9497 // If the richtext lib is dynamically loaded after the app has already started
9498 // (such as from wxPython) then the built-in module system will not init this
9499 // module. Provide this function to do it manually.
9500 void wxRichTextModuleInit()
9502 wxModule
* module
= new wxRichTextModule
;
9504 wxModule
::RegisterModule(module
);
9509 * Commands for undo/redo
9513 wxRichTextCommand
::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9514 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9516 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9519 wxRichTextCommand
::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9523 wxRichTextCommand
::~wxRichTextCommand()
9528 void wxRichTextCommand
::AddAction(wxRichTextAction
* action
)
9530 if (!m_actions
.Member(action
))
9531 m_actions
.Append(action
);
9534 bool wxRichTextCommand
::Do()
9536 for (wxList
::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9538 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9545 bool wxRichTextCommand
::Undo()
9547 for (wxList
::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9549 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9556 void wxRichTextCommand
::ClearActions()
9558 WX_CLEAR_LIST(wxList
, m_actions
);
9566 wxRichTextAction
::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9567 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9568 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9572 m_containerAddress
.Create(buffer
, container
);
9573 m_ignoreThis
= ignoreFirstTime
;
9578 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9579 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9581 cmd
->AddAction(this);
9584 wxRichTextAction
::~wxRichTextAction()
9590 // Returns the container that this action refers to, using the container address and top-level buffer.
9591 wxRichTextParagraphLayoutBox
* wxRichTextAction
::GetContainer() const
9593 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9598 void wxRichTextAction
::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9600 // Store a list of line start character and y positions so we can figure out which area
9601 // we need to refresh
9603 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9604 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9605 wxASSERT(container
!= NULL
);
9609 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9610 // If we had several actions, which only invalidate and leave layout until the
9611 // paint handler is called, then this might not be true. So we may need to switch
9612 // optimisation on only when we're simply adding text and not simultaneously
9613 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9614 // first, but of course this means we'll be doing it twice.
9615 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9617 wxSize clientSize
= m_ctrl
->GetClientSize();
9618 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9619 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9621 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9622 wxRichTextObjectList
::compatibility_iterator node
= container
->GetChildren().Find(para
);
9625 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9626 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
9629 wxRichTextLine
* line
= node2
->GetData();
9630 wxPoint pt
= line
->GetAbsolutePosition();
9631 wxRichTextRange range
= line
->GetAbsoluteRange();
9635 node2
= wxRichTextLineList
::compatibility_iterator();
9636 node
= wxRichTextObjectList
::compatibility_iterator();
9638 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9640 optimizationLineCharPositions
.Add(range
.GetStart());
9641 optimizationLineYPositions
.Add(pt
.y
);
9645 node2
= node2
->GetNext();
9649 node
= node
->GetNext();
9655 bool wxRichTextAction
::Do()
9657 m_buffer
->Modify(true);
9659 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9660 wxASSERT(container
!= NULL
);
9666 case wxRICHTEXT_INSERT
:
9668 // Store a list of line start character and y positions so we can figure out which area
9669 // we need to refresh
9670 wxArrayInt optimizationLineCharPositions
;
9671 wxArrayInt optimizationLineYPositions
;
9673 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9674 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9677 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9678 container
->UpdateRanges();
9680 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9681 // Layout() would stop prematurely at the top level.
9682 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9684 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9686 // Character position to caret position
9687 newCaretPosition
--;
9689 // Don't take into account the last newline
9690 if (m_newParagraphs
.GetPartialParagraph())
9691 newCaretPosition
--;
9693 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9695 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9696 if (p
->GetRange().GetLength() == 1)
9697 newCaretPosition
--;
9700 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9702 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9704 wxRichTextEvent
cmdEvent(
9705 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9706 m_ctrl ? m_ctrl
->GetId() : -1);
9707 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9708 cmdEvent
.SetRange(GetRange());
9709 cmdEvent
.SetPosition(GetRange().GetStart());
9710 cmdEvent
.SetContainer(container
);
9712 m_buffer
->SendEvent(cmdEvent
);
9716 case wxRICHTEXT_DELETE
:
9718 wxArrayInt optimizationLineCharPositions
;
9719 wxArrayInt optimizationLineYPositions
;
9721 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9722 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9725 container
->DeleteRange(GetRange());
9726 container
->UpdateRanges();
9727 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9728 // Layout() would stop prematurely at the top level.
9729 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9731 long caretPos
= GetRange().GetStart()-1;
9732 if (caretPos
>= container
->GetOwnRange().GetEnd())
9735 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9737 wxRichTextEvent
cmdEvent(
9738 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9739 m_ctrl ? m_ctrl
->GetId() : -1);
9740 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9741 cmdEvent
.SetRange(GetRange());
9742 cmdEvent
.SetPosition(GetRange().GetStart());
9743 cmdEvent
.SetContainer(container
);
9745 m_buffer
->SendEvent(cmdEvent
);
9749 case wxRICHTEXT_CHANGE_STYLE
:
9750 case wxRICHTEXT_CHANGE_PROPERTIES
:
9752 ApplyParagraphs(GetNewParagraphs());
9754 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9755 // Layout() would stop prematurely at the top level.
9756 container
->InvalidateHierarchy(GetRange());
9758 UpdateAppearance(GetPosition());
9760 wxRichTextEvent
cmdEvent(
9761 m_cmdId
== wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9762 m_ctrl ? m_ctrl
->GetId() : -1);
9763 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9764 cmdEvent
.SetRange(GetRange());
9765 cmdEvent
.SetPosition(GetRange().GetStart());
9766 cmdEvent
.SetContainer(container
);
9768 m_buffer
->SendEvent(cmdEvent
);
9772 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9774 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9777 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9778 obj
->GetAttributes() = m_attributes
;
9779 m_attributes
= oldAttr
;
9782 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9783 // Layout() would stop prematurely at the top level.
9784 container
->InvalidateHierarchy(GetRange());
9786 UpdateAppearance(GetPosition());
9788 wxRichTextEvent
cmdEvent(
9789 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9790 m_ctrl ? m_ctrl
->GetId() : -1);
9791 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9792 cmdEvent
.SetRange(GetRange());
9793 cmdEvent
.SetPosition(GetRange().GetStart());
9794 cmdEvent
.SetContainer(container
);
9796 m_buffer
->SendEvent(cmdEvent
);
9800 case wxRICHTEXT_CHANGE_OBJECT
:
9802 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9803 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9804 if (obj
&& m_object
)
9806 wxRichTextObjectList
::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9809 wxRichTextObject
* obj
= node
->GetData();
9810 node
->SetData(m_object
);
9815 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9816 // Layout() would stop prematurely at the top level.
9817 container
->InvalidateHierarchy(GetRange());
9819 UpdateAppearance(GetPosition());
9821 // TODO: send new kind of modification event
9832 bool wxRichTextAction
::Undo()
9834 m_buffer
->Modify(true);
9836 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9837 wxASSERT(container
!= NULL
);
9843 case wxRICHTEXT_INSERT
:
9845 wxArrayInt optimizationLineCharPositions
;
9846 wxArrayInt optimizationLineYPositions
;
9848 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9849 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9852 container
->DeleteRange(GetRange());
9853 container
->UpdateRanges();
9854 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9855 // Layout() would stop prematurely at the top level.
9856 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9858 long newCaretPosition
= GetPosition() - 1;
9860 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9862 wxRichTextEvent
cmdEvent(
9863 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9864 m_ctrl ? m_ctrl
->GetId() : -1);
9865 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9866 cmdEvent
.SetRange(GetRange());
9867 cmdEvent
.SetPosition(GetRange().GetStart());
9868 cmdEvent
.SetContainer(container
);
9870 m_buffer
->SendEvent(cmdEvent
);
9874 case wxRICHTEXT_DELETE
:
9876 wxArrayInt optimizationLineCharPositions
;
9877 wxArrayInt optimizationLineYPositions
;
9879 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9880 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9883 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9884 container
->UpdateRanges();
9885 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9886 // Layout() would stop prematurely at the top level.
9887 container
->InvalidateHierarchy(GetRange());
9889 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9891 wxRichTextEvent
cmdEvent(
9892 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9893 m_ctrl ? m_ctrl
->GetId() : -1);
9894 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9895 cmdEvent
.SetRange(GetRange());
9896 cmdEvent
.SetPosition(GetRange().GetStart());
9897 cmdEvent
.SetContainer(container
);
9899 m_buffer
->SendEvent(cmdEvent
);
9903 case wxRICHTEXT_CHANGE_STYLE
:
9904 case wxRICHTEXT_CHANGE_PROPERTIES
:
9906 ApplyParagraphs(GetOldParagraphs());
9907 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9908 // Layout() would stop prematurely at the top level.
9909 container
->InvalidateHierarchy(GetRange());
9911 UpdateAppearance(GetPosition());
9913 wxRichTextEvent
cmdEvent(
9914 m_cmdId
== wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9915 m_ctrl ? m_ctrl
->GetId() : -1);
9916 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9917 cmdEvent
.SetRange(GetRange());
9918 cmdEvent
.SetPosition(GetRange().GetStart());
9919 cmdEvent
.SetContainer(container
);
9921 m_buffer
->SendEvent(cmdEvent
);
9925 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9926 case wxRICHTEXT_CHANGE_OBJECT
:
9937 /// Update the control appearance
9938 void wxRichTextAction
::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9940 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9941 wxASSERT(container
!= NULL
);
9947 m_ctrl
->SetFocusObject(container
);
9948 m_ctrl
->SetCaretPosition(caretPosition
);
9950 if (!m_ctrl
->IsFrozen())
9952 wxRect containerRect
= container
->GetRect();
9954 m_ctrl
->LayoutContent();
9956 // Refresh everything if there were floating objects or the container changed size
9957 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9958 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9960 m_ctrl
->Refresh(false);
9964 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9965 // Find refresh rectangle if we are in a position to optimise refresh
9966 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9970 wxSize clientSize
= m_ctrl
->GetClientSize();
9971 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9973 // Start/end positions
9975 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9977 bool foundEnd
= false;
9979 // position offset - how many characters were inserted
9980 int positionOffset
= GetRange().GetLength();
9982 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9983 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9984 positionOffset
= - positionOffset
;
9986 // find the first line which is being drawn at the same position as it was
9987 // before. Since we're talking about a simple insertion, we can assume
9988 // that the rest of the window does not need to be redrawn.
9990 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9991 // Since we support floating layout, we should redraw the whole para instead of just
9992 // the first line touching the invalid range.
9995 firstY
= para
->GetPosition().y
;
9998 wxRichTextObjectList
::compatibility_iterator node
= container
->GetChildren().Find(para
);
10001 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10002 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
10005 wxRichTextLine
* line
= node2
->GetData();
10006 wxPoint pt
= line
->GetAbsolutePosition();
10007 wxRichTextRange range
= line
->GetAbsoluteRange();
10009 // we want to find the first line that is in the same position
10010 // as before. This will mean we're at the end of the changed text.
10012 if (pt
.y
> lastY
) // going past the end of the window, no more info
10014 node2
= wxRichTextLineList
::compatibility_iterator();
10015 node
= wxRichTextObjectList
::compatibility_iterator();
10017 // Detect last line in the buffer
10018 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10020 // If deleting text, make sure we refresh below as well as above
10021 if (positionOffset
>= 0)
10024 lastY
= pt
.y
+ line
->GetSize().y
;
10027 node2
= wxRichTextLineList
::compatibility_iterator();
10028 node
= wxRichTextObjectList
::compatibility_iterator();
10034 // search for this line being at the same position as before
10035 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10037 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10038 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10040 // Stop, we're now the same as we were
10045 node2
= wxRichTextLineList
::compatibility_iterator();
10046 node
= wxRichTextObjectList
::compatibility_iterator();
10054 node2
= node2
->GetNext();
10058 node
= node
->GetNext();
10061 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10063 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10065 // Convert to device coordinates
10066 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
10067 m_ctrl
->RefreshRect(rect
);
10071 m_ctrl
->Refresh(false);
10073 m_ctrl
->PositionCaret();
10075 // This causes styles to persist when doing programmatic
10076 // content creation except when Freeze/Thaw is used, so
10077 // disable this and check for the consequences.
10078 // m_ctrl->SetDefaultStyleToCursorStyle();
10080 if (sendUpdateEvent
)
10081 wxTextCtrl
::SendTextUpdatedEvent(m_ctrl
);
10086 /// Replace the buffer paragraphs with the new ones.
10087 void wxRichTextAction
::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10089 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10090 wxASSERT(container
!= NULL
);
10094 wxRichTextObjectList
::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10097 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10098 wxASSERT (para
!= NULL
);
10100 // We'll replace the existing paragraph by finding the paragraph at this position,
10101 // delete its node data, and setting a copy as the new node data.
10102 // TODO: make more efficient by simply swapping old and new paragraph objects.
10104 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10107 wxRichTextObjectList
::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10108 if (bufferParaNode
)
10110 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10111 newPara
->SetParent(container
);
10113 bufferParaNode
->SetData(newPara
);
10115 delete existingPara
;
10119 node
= node
->GetNext();
10126 * This stores beginning and end positions for a range of data.
10129 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10131 /// Limit this range to be within 'range'
10132 bool wxRichTextRange
::LimitTo(const wxRichTextRange
& range
)
10134 if (m_start
< range
.m_start
)
10135 m_start
= range
.m_start
;
10137 if (m_end
> range
.m_end
)
10138 m_end
= range
.m_end
;
10144 * wxRichTextImage implementation
10145 * This object represents an image.
10148 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10150 wxRichTextImage
::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10151 wxRichTextObject(parent
)
10154 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10156 SetAttributes(*charStyle
);
10159 wxRichTextImage
::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10160 wxRichTextObject(parent
)
10163 m_imageBlock
= imageBlock
;
10165 SetAttributes(*charStyle
);
10168 void wxRichTextImage
::Init()
10170 m_originalImageSize
= wxSize(-1, -1);
10173 /// Create a cached image at the required size
10174 bool wxRichTextImage
::LoadImageCache(wxDC
& dc
, bool resetCache
)
10176 if (!m_imageBlock
.IsOk())
10179 // If we have an original image size, use that to compute the cached bitmap size
10180 // instead of loading the image each time. This way we can avoid loading
10181 // the image so long as the new cached bitmap size hasn't changed.
10184 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10186 m_imageCache
= wxNullBitmap
;
10188 m_imageBlock
.Load(image
);
10192 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10195 int width
= m_originalImageSize
.GetWidth();
10196 int height
= m_originalImageSize
.GetHeight();
10198 int parentWidth
= 0;
10199 int parentHeight
= 0;
10202 int maxHeight
= -1;
10204 wxRichTextBuffer
* buffer
= GetBuffer();
10208 if (buffer
->GetRichTextCtrl())
10210 // Subtract borders
10211 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10213 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10214 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10215 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10217 sz
= contentRect
.GetSize();
10219 // Start with a maximum width of the control size, even if not specified by the content,
10220 // to minimize the amount of picture overlapping the right-hand side
10224 sz
= buffer
->GetCachedSize();
10225 parentWidth
= sz
.GetWidth();
10226 parentHeight
= sz
.GetHeight();
10229 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10231 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10232 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10233 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10234 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10235 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10236 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10239 // Limit to max width
10241 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10245 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10246 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10247 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10248 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10249 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10250 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10252 // If we already have a smaller max width due to the constraints of the control size,
10253 // don't use the larger max width.
10254 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10258 if (maxWidth
> 0 && width
> maxWidth
)
10261 // Preserve the aspect ratio
10262 if (width
!= m_originalImageSize
.GetWidth())
10263 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10265 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10267 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10268 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10269 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10270 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10271 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10272 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10274 // Preserve the aspect ratio
10275 if (height
!= m_originalImageSize
.GetHeight())
10276 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10279 // Limit to max height
10281 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10283 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10284 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10285 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10286 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10287 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10288 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10291 if (maxHeight
> 0 && height
> maxHeight
)
10293 height
= maxHeight
;
10295 // Preserve the aspect ratio
10296 if (height
!= m_originalImageSize
.GetHeight())
10297 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10300 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10302 // Do nothing, we didn't need to change the image cache
10308 m_imageBlock
.Load(image
);
10313 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10314 m_imageCache
= wxBitmap(image
);
10317 // If the original width and height is small, e.g. 400 or below,
10318 // scale up and then down to improve image quality. This can make
10319 // a big difference, with not much performance hit.
10320 int upscaleThreshold
= 400;
10322 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10324 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10325 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10328 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10329 m_imageCache
= wxBitmap(img
);
10333 return m_imageCache
.IsOk();
10337 bool wxRichTextImage
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10342 // Don't need cached size AFAIK
10343 // wxSize size = GetCachedSize();
10344 if (!LoadImageCache(dc
))
10347 wxRichTextAttr
attr(GetAttributes());
10348 context
.ApplyVirtualAttributes(attr
, this);
10350 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10352 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10353 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10354 marginRect
= rect
; // outer rectangle, will calculate contentRect
10355 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10357 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10359 if (selection
.WithinSelection(GetRange().GetStart(), this))
10361 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10362 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10363 dc
.SetLogicalFunction(wxINVERT
);
10364 dc
.DrawRectangle(contentRect
);
10365 dc
.SetLogicalFunction(wxCOPY
);
10371 /// Lay the item out
10372 bool wxRichTextImage
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10374 if (!LoadImageCache(dc
))
10377 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10378 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10379 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10381 wxRichTextAttr
attr(GetAttributes());
10382 context
.ApplyVirtualAttributes(attr
, this);
10384 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10386 wxSize overallSize
= marginRect
.GetSize();
10388 SetCachedSize(overallSize
);
10389 SetMaxSize(overallSize
);
10390 SetMinSize(overallSize
);
10391 SetPosition(rect
.GetPosition());
10396 /// Get/set the object size for the given range. Returns false if the range
10397 /// is invalid for this object.
10398 bool wxRichTextImage
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10400 if (!range
.IsWithin(GetRange()))
10403 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10405 size
.x
= 0; size
.y
= 0;
10406 if (partialExtents
)
10407 partialExtents
->Add(0);
10411 wxRichTextAttr
attr(GetAttributes());
10412 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10414 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10415 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10416 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10417 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10419 wxSize overallSize
= marginRect
.GetSize();
10421 if (partialExtents
)
10422 partialExtents
->Add(overallSize
.x
);
10424 size
= overallSize
;
10429 // Get the 'natural' size for an object. For an image, it would be the
10431 wxTextAttrSize wxRichTextImage
::GetNaturalSize() const
10433 wxTextAttrSize size
;
10434 if (GetImageCache().IsOk())
10436 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10437 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10444 void wxRichTextImage
::Copy(const wxRichTextImage
& obj
)
10446 wxRichTextObject
::Copy(obj
);
10448 m_imageBlock
= obj
.m_imageBlock
;
10449 m_originalImageSize
= obj
.m_originalImageSize
;
10452 /// Edit properties via a GUI
10453 bool wxRichTextImage
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10455 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10456 imageDlg
.SetAttributes(GetAttributes());
10458 if (imageDlg
.ShowModal() == wxID_OK
)
10460 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10461 // indeterminate in the object.
10462 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10474 /// Compare two attribute objects
10475 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10477 return (attr1
== attr2
);
10480 // Partial equality test taking flags into account
10481 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10483 return attr1
.EqPartial(attr2
);
10487 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10489 if (tabs1
.GetCount() != tabs2
.GetCount())
10493 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10495 if (tabs1
[i
] != tabs2
[i
])
10501 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10503 return destStyle
.Apply(style
, compareWith
);
10506 // Remove attributes
10507 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10509 return destStyle
.RemoveStyle(style
);
10512 /// Combine two bitlists, specifying the bits of interest with separate flags.
10513 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10515 return wxRichTextAttr
::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10518 /// Compare two bitlists
10519 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10521 return wxRichTextAttr
::BitlistsEqPartial(valueA
, valueB
, flags
);
10524 /// Split into paragraph and character styles
10525 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10527 return wxRichTextAttr
::SplitParaCharStyles(style
, parStyle
, charStyle
);
10530 /// Convert a decimal to Roman numerals
10531 wxString
wxRichTextDecimalToRoman(long n
)
10533 static wxArrayInt decimalNumbers
;
10534 static wxArrayString romanNumbers
;
10539 decimalNumbers
.Clear();
10540 romanNumbers
.Clear();
10541 return wxEmptyString
;
10544 if (decimalNumbers
.GetCount() == 0)
10546 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10548 wxRichTextAddDecRom(1000, wxT("M"));
10549 wxRichTextAddDecRom(900, wxT("CM"));
10550 wxRichTextAddDecRom(500, wxT("D"));
10551 wxRichTextAddDecRom(400, wxT("CD"));
10552 wxRichTextAddDecRom(100, wxT("C"));
10553 wxRichTextAddDecRom(90, wxT("XC"));
10554 wxRichTextAddDecRom(50, wxT("L"));
10555 wxRichTextAddDecRom(40, wxT("XL"));
10556 wxRichTextAddDecRom(10, wxT("X"));
10557 wxRichTextAddDecRom(9, wxT("IX"));
10558 wxRichTextAddDecRom(5, wxT("V"));
10559 wxRichTextAddDecRom(4, wxT("IV"));
10560 wxRichTextAddDecRom(1, wxT("I"));
10566 while (n
> 0 && i
< 13)
10568 if (n
>= decimalNumbers
[i
])
10570 n
-= decimalNumbers
[i
];
10571 roman
+= romanNumbers
[i
];
10578 if (roman
.IsEmpty())
10584 * wxRichTextFileHandler
10585 * Base class for file handlers
10588 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10590 #if wxUSE_FFILE && wxUSE_STREAMS
10591 bool wxRichTextFileHandler
::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10593 wxFFileInputStream
stream(filename
);
10595 return LoadFile(buffer
, stream
);
10600 bool wxRichTextFileHandler
::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10602 wxFFileOutputStream
stream(filename
);
10604 return SaveFile(buffer
, stream
);
10608 #endif // wxUSE_FFILE && wxUSE_STREAMS
10610 /// Can we handle this filename (if using files)? By default, checks the extension.
10611 bool wxRichTextFileHandler
::CanHandle(const wxString
& filename
) const
10613 wxString path
, file
, ext
;
10614 wxFileName
::SplitPath(filename
, & path
, & file
, & ext
);
10616 return (ext
.Lower() == GetExtension());
10620 * wxRichTextTextHandler
10621 * Plain text handler
10624 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10627 bool wxRichTextPlainTextHandler
::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10629 if (!stream
.IsOk())
10635 while (!stream
.Eof())
10637 int ch
= stream
.GetC();
10641 if (ch
== 10 && lastCh
!= 13)
10644 if (ch
> 0 && ch
!= 10)
10651 buffer
->ResetAndClearCommands();
10653 buffer
->AddParagraphs(str
);
10654 buffer
->UpdateRanges();
10659 bool wxRichTextPlainTextHandler
::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10661 if (!stream
.IsOk())
10664 wxString text
= buffer
->GetText();
10666 wxString newLine
= wxRichTextLineBreakChar
;
10667 text
.Replace(newLine
, wxT("\n"));
10669 wxCharBuffer buf
= text
.ToAscii();
10671 stream
.Write((const char*) buf
, text
.length());
10674 #endif // wxUSE_STREAMS
10677 * Stores information about an image, in binary in-memory form
10680 wxRichTextImageBlock
::wxRichTextImageBlock()
10685 wxRichTextImageBlock
::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10691 wxRichTextImageBlock
::~wxRichTextImageBlock()
10696 void wxRichTextImageBlock
::Init()
10700 m_imageType
= wxBITMAP_TYPE_INVALID
;
10703 void wxRichTextImageBlock
::Clear()
10707 m_imageType
= wxBITMAP_TYPE_INVALID
;
10711 // Load the original image into a memory block.
10712 // If the image is not a JPEG, we must convert it into a JPEG
10713 // to conserve space.
10714 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10715 // load the image a 2nd time.
10717 bool wxRichTextImageBlock
::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10718 wxImage
& image
, bool convertToJPEG
)
10720 m_imageType
= imageType
;
10722 wxString
filenameToRead(filename
);
10723 bool removeFile
= false;
10725 if (imageType
== wxBITMAP_TYPE_INVALID
)
10726 return false; // Could not determine image type
10728 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10730 wxString tempFile
=
10731 wxFileName
::CreateTempFileName(_("image"));
10733 wxASSERT(!tempFile
.IsEmpty());
10735 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10736 filenameToRead
= tempFile
;
10739 m_imageType
= wxBITMAP_TYPE_JPEG
;
10742 if (!file
.Open(filenameToRead
))
10745 m_dataSize
= (size_t) file
.Length();
10750 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10753 wxRemoveFile(filenameToRead
);
10755 return (m_data
!= NULL
);
10758 // Make an image block from the wxImage in the given
10760 bool wxRichTextImageBlock
::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10762 image
.SetOption(wxT("quality"), quality
);
10764 if (imageType
== wxBITMAP_TYPE_INVALID
)
10765 return false; // Could not determine image type
10767 return DoMakeImageBlock(image
, imageType
);
10770 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10771 bool wxRichTextImageBlock
::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10773 if (imageType
== wxBITMAP_TYPE_INVALID
)
10774 return false; // Could not determine image type
10776 return DoMakeImageBlock(image
, imageType
);
10779 // Makes the image block
10780 bool wxRichTextImageBlock
::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10782 wxMemoryOutputStream memStream
;
10783 if (!image
.SaveFile(memStream
, imageType
))
10788 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10796 m_imageType
= imageType
;
10797 m_dataSize
= memStream
.GetSize();
10799 memStream
.CopyTo(m_data
, m_dataSize
);
10801 return (m_data
!= NULL
);
10805 bool wxRichTextImageBlock
::Write(const wxString
& filename
)
10807 return WriteBlock(filename
, m_data
, m_dataSize
);
10810 void wxRichTextImageBlock
::Copy(const wxRichTextImageBlock
& block
)
10812 m_imageType
= block
.m_imageType
;
10814 m_dataSize
= block
.m_dataSize
;
10815 if (m_dataSize
== 0)
10818 m_data
= new unsigned char[m_dataSize
];
10820 for (i
= 0; i
< m_dataSize
; i
++)
10821 m_data
[i
] = block
.m_data
[i
];
10825 void wxRichTextImageBlock
::operator=(const wxRichTextImageBlock
& block
)
10830 // Load a wxImage from the block
10831 bool wxRichTextImageBlock
::Load(wxImage
& image
)
10836 // Read in the image.
10838 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10839 bool success
= image
.LoadFile(mstream
, GetImageType());
10841 wxString tempFile
= wxFileName
::CreateTempFileName(_("image"));
10842 wxASSERT(!tempFile
.IsEmpty());
10844 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10848 success
= image
.LoadFile(tempFile
, GetImageType());
10849 wxRemoveFile(tempFile
);
10855 // Write data in hex to a stream
10856 bool wxRichTextImageBlock
::WriteHex(wxOutputStream
& stream
)
10858 if (m_dataSize
== 0)
10861 int bufSize
= 100000;
10862 if (int(2*m_dataSize
) < bufSize
)
10863 bufSize
= 2*m_dataSize
;
10864 char* buf
= new char[bufSize
+1];
10866 int left
= m_dataSize
;
10871 if (left
*2 > bufSize
)
10873 n
= bufSize
; left
-= (bufSize
/2);
10877 n
= left
*2; left
= 0;
10881 for (i
= 0; i
< (n
/2); i
++)
10883 wxDecToHex(m_data
[j
], b
, b
+1);
10888 stream
.Write((const char*) buf
, n
);
10894 // Read data in hex from a stream
10895 bool wxRichTextImageBlock
::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10897 int dataSize
= length
/2;
10902 // create a null terminated temporary string:
10906 m_data
= new unsigned char[dataSize
];
10908 for (i
= 0; i
< dataSize
; i
++)
10910 str
[0] = (char)stream
.GetC();
10911 str
[1] = (char)stream
.GetC();
10913 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10916 m_dataSize
= dataSize
;
10917 m_imageType
= imageType
;
10922 // Allocate and read from stream as a block of memory
10923 unsigned char* wxRichTextImageBlock
::ReadBlock(wxInputStream
& stream
, size_t size
)
10925 unsigned char* block
= new unsigned char[size
];
10929 stream
.Read(block
, size
);
10934 unsigned char* wxRichTextImageBlock
::ReadBlock(const wxString
& filename
, size_t size
)
10936 wxFileInputStream
stream(filename
);
10937 if (!stream
.IsOk())
10940 return ReadBlock(stream
, size
);
10943 // Write memory block to stream
10944 bool wxRichTextImageBlock
::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10946 stream
.Write((void*) block
, size
);
10947 return stream
.IsOk();
10951 // Write memory block to file
10952 bool wxRichTextImageBlock
::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10954 wxFileOutputStream
outStream(filename
);
10955 if (!outStream
.IsOk())
10958 return WriteBlock(outStream
, block
, size
);
10961 // Gets the extension for the block's type
10962 wxString wxRichTextImageBlock
::GetExtension() const
10964 wxImageHandler
* handler
= wxImage
::FindHandler(GetImageType());
10966 return handler
->GetExtension();
10968 return wxEmptyString
;
10974 * The data object for a wxRichTextBuffer
10977 const wxChar
*wxRichTextBufferDataObject
::ms_richTextBufferFormatId
= wxT("wxShape");
10979 wxRichTextBufferDataObject
::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10981 m_richTextBuffer
= richTextBuffer
;
10983 // this string should uniquely identify our format, but is otherwise
10985 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10987 SetFormat(m_formatRichTextBuffer
);
10990 wxRichTextBufferDataObject
::~wxRichTextBufferDataObject()
10992 delete m_richTextBuffer
;
10995 // after a call to this function, the richTextBuffer is owned by the caller and it
10996 // is responsible for deleting it!
10997 wxRichTextBuffer
* wxRichTextBufferDataObject
::GetRichTextBuffer()
10999 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11000 m_richTextBuffer
= NULL
;
11002 return richTextBuffer
;
11005 wxDataFormat wxRichTextBufferDataObject
::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11007 return m_formatRichTextBuffer
;
11010 size_t wxRichTextBufferDataObject
::GetDataSize() const
11012 if (!m_richTextBuffer
)
11018 wxStringOutputStream
stream(& bufXML
);
11019 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11021 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11027 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11028 return strlen(buffer
) + 1;
11030 return bufXML
.Length()+1;
11034 bool wxRichTextBufferDataObject
::GetDataHere(void *pBuf
) const
11036 if (!pBuf
|| !m_richTextBuffer
)
11042 wxStringOutputStream
stream(& bufXML
);
11043 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11045 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11051 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11052 size_t len
= strlen(buffer
);
11053 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11054 ((char*) pBuf
)[len
] = 0;
11056 size_t len
= bufXML
.Length();
11057 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11058 ((char*) pBuf
)[len
] = 0;
11064 bool wxRichTextBufferDataObject
::SetData(size_t WXUNUSED(len
), const void *buf
)
11066 wxDELETE(m_richTextBuffer
);
11068 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11070 m_richTextBuffer
= new wxRichTextBuffer
;
11072 wxStringInputStream
stream(bufXML
);
11073 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11075 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11077 wxDELETE(m_richTextBuffer
);
11089 * wxRichTextFontTable
11090 * Manages quick access to a pool of fonts for rendering rich text
11093 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11095 class wxRichTextFontTableData
: public wxObjectRefData
11098 wxRichTextFontTableData() {}
11100 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
11102 wxRichTextFontTableHashMap m_hashMap
;
11105 wxFont wxRichTextFontTableData
::FindFont(const wxRichTextAttr
& fontSpec
)
11107 wxString
facename(fontSpec
.GetFontFaceName());
11108 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()));
11109 wxRichTextFontTableHashMap
::iterator entry
= m_hashMap
.find(spec
);
11111 if ( entry
== m_hashMap
.end() )
11113 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11114 m_hashMap
[spec
] = font
;
11119 return entry
->second
;
11123 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11125 wxRichTextFontTable
::wxRichTextFontTable()
11127 m_refData
= new wxRichTextFontTableData
;
11130 wxRichTextFontTable
::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11136 wxRichTextFontTable
::~wxRichTextFontTable()
11141 bool wxRichTextFontTable
::operator == (const wxRichTextFontTable
& table
) const
11143 return (m_refData
== table
.m_refData
);
11146 void wxRichTextFontTable
::operator= (const wxRichTextFontTable
& table
)
11151 wxFont wxRichTextFontTable
::FindFont(const wxRichTextAttr
& fontSpec
)
11153 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11155 return data
->FindFont(fontSpec
);
11160 void wxRichTextFontTable
::Clear()
11162 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11164 data
->m_hashMap
.clear();
11170 void wxTextBoxAttr
::Reset()
11173 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11174 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11175 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11176 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11177 m_boxStyleName
= wxEmptyString
;
11181 m_position
.Reset();
11192 bool wxTextBoxAttr
::operator== (const wxTextBoxAttr
& attr
) const
11195 m_flags
== attr
.m_flags
&&
11196 m_floatMode
== attr
.m_floatMode
&&
11197 m_clearMode
== attr
.m_clearMode
&&
11198 m_collapseMode
== attr
.m_collapseMode
&&
11199 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11201 m_margins
== attr
.m_margins
&&
11202 m_padding
== attr
.m_padding
&&
11203 m_position
== attr
.m_position
&&
11205 m_size
== attr
.m_size
&&
11206 m_minSize
== attr
.m_minSize
&&
11207 m_maxSize
== attr
.m_maxSize
&&
11209 m_border
== attr
.m_border
&&
11210 m_outline
== attr
.m_outline
&&
11212 m_boxStyleName
== attr
.m_boxStyleName
11216 // Partial equality test
11217 bool wxTextBoxAttr
::EqPartial(const wxTextBoxAttr
& attr
) const
11219 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11222 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11225 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11228 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11231 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11236 if (!m_position
.EqPartial(attr
.m_position
))
11241 if (!m_size
.EqPartial(attr
.m_size
))
11243 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11245 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11250 if (!m_margins
.EqPartial(attr
.m_margins
))
11255 if (!m_padding
.EqPartial(attr
.m_padding
))
11260 if (!GetBorder().EqPartial(attr
.GetBorder()))
11265 if (!GetOutline().EqPartial(attr
.GetOutline()))
11271 // Merges the given attributes. If compareWith
11272 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11273 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11274 bool wxTextBoxAttr
::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11276 if (attr
.HasFloatMode())
11278 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11279 SetFloatMode(attr
.GetFloatMode());
11282 if (attr
.HasClearMode())
11284 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11285 SetClearMode(attr
.GetClearMode());
11288 if (attr
.HasCollapseBorders())
11290 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11291 SetCollapseBorders(attr
.GetCollapseBorders());
11294 if (attr
.HasVerticalAlignment())
11296 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11297 SetVerticalAlignment(attr
.GetVerticalAlignment());
11300 if (attr
.HasBoxStyleName())
11302 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11303 SetBoxStyleName(attr
.GetBoxStyleName());
11306 m_margins
.Apply(attr
.m_margins
, compareWith ?
(& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11307 m_padding
.Apply(attr
.m_padding
, compareWith ?
(& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11308 m_position
.Apply(attr
.m_position
, compareWith ?
(& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11310 m_size
.Apply(attr
.m_size
, compareWith ?
(& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11311 m_minSize
.Apply(attr
.m_minSize
, compareWith ?
(& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11312 m_maxSize
.Apply(attr
.m_maxSize
, compareWith ?
(& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11314 m_border
.Apply(attr
.m_border
, compareWith ?
(& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11315 m_outline
.Apply(attr
.m_outline
, compareWith ?
(& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11320 // Remove specified attributes from this object
11321 bool wxTextBoxAttr
::RemoveStyle(const wxTextBoxAttr
& attr
)
11323 if (attr
.HasFloatMode())
11324 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11326 if (attr
.HasClearMode())
11327 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11329 if (attr
.HasCollapseBorders())
11330 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11332 if (attr
.HasVerticalAlignment())
11333 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11335 if (attr
.HasBoxStyleName())
11337 SetBoxStyleName(wxEmptyString
);
11338 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11341 m_margins
.RemoveStyle(attr
.m_margins
);
11342 m_padding
.RemoveStyle(attr
.m_padding
);
11343 m_position
.RemoveStyle(attr
.m_position
);
11345 m_size
.RemoveStyle(attr
.m_size
);
11346 m_minSize
.RemoveStyle(attr
.m_minSize
);
11347 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11349 m_border
.RemoveStyle(attr
.m_border
);
11350 m_outline
.RemoveStyle(attr
.m_outline
);
11355 // Collects the attributes that are common to a range of content, building up a note of
11356 // which attributes are absent in some objects and which clash in some objects.
11357 void wxTextBoxAttr
::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11359 if (attr
.HasFloatMode())
11361 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11363 if (HasFloatMode())
11365 if (GetFloatMode() != attr
.GetFloatMode())
11367 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11368 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11372 SetFloatMode(attr
.GetFloatMode());
11376 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11378 if (attr
.HasClearMode())
11380 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11382 if (HasClearMode())
11384 if (GetClearMode() != attr
.GetClearMode())
11386 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11387 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11391 SetClearMode(attr
.GetClearMode());
11395 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11397 if (attr
.HasCollapseBorders())
11399 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11401 if (HasCollapseBorders())
11403 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11405 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11406 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11410 SetCollapseBorders(attr
.GetCollapseBorders());
11414 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11416 if (attr
.HasVerticalAlignment())
11418 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11420 if (HasVerticalAlignment())
11422 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11424 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11425 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11429 SetVerticalAlignment(attr
.GetVerticalAlignment());
11433 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11435 if (attr
.HasBoxStyleName())
11437 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11439 if (HasBoxStyleName())
11441 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11443 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11444 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11448 SetBoxStyleName(attr
.GetBoxStyleName());
11452 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11454 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11455 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11456 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11458 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11459 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11460 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11462 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11463 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11466 bool wxTextBoxAttr
::IsDefault() const
11468 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11469 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11470 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11475 void wxRichTextAttr
::Copy(const wxRichTextAttr
& attr
)
11477 wxTextAttr
::Copy(attr
);
11479 m_textBoxAttr
= attr
.m_textBoxAttr
;
11482 bool wxRichTextAttr
::operator==(const wxRichTextAttr
& attr
) const
11484 if (!(wxTextAttr
::operator==(attr
)))
11487 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11490 // Partial equality test taking comparison object into account
11491 bool wxRichTextAttr
::EqPartial(const wxRichTextAttr
& attr
) const
11493 if (!(wxTextAttr
::EqPartial(attr
)))
11496 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11499 // Merges the given attributes. If compareWith
11500 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11501 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11502 bool wxRichTextAttr
::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11504 wxTextAttr
::Apply(style
, compareWith
);
11506 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith ?
(& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11509 // Remove specified attributes from this object
11510 bool wxRichTextAttr
::RemoveStyle(const wxRichTextAttr
& attr
)
11512 wxTextAttr
::RemoveStyle(*this, attr
);
11514 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11517 // Collects the attributes that are common to a range of content, building up a note of
11518 // which attributes are absent in some objects and which clash in some objects.
11519 void wxRichTextAttr
::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11521 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11523 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11526 // Partial equality test
11527 bool wxTextAttrBorder
::EqPartial(const wxTextAttrBorder
& border
) const
11529 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11532 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11535 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11541 // Apply border to 'this', but not if the same as compareWith
11542 bool wxTextAttrBorder
::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11544 if (border
.HasStyle())
11546 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11547 SetStyle(border
.GetStyle());
11549 if (border
.HasColour())
11551 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11552 SetColour(border
.GetColourLong());
11554 if (border
.HasWidth())
11556 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11557 SetWidth(border
.GetWidth());
11563 // Remove specified attributes from this object
11564 bool wxTextAttrBorder
::RemoveStyle(const wxTextAttrBorder
& attr
)
11566 if (attr
.HasStyle() && HasStyle())
11567 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11568 if (attr
.HasColour() && HasColour())
11569 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11570 if (attr
.HasWidth() && HasWidth())
11571 m_borderWidth
.Reset();
11576 // Collects the attributes that are common to a range of content, building up a note of
11577 // which attributes are absent in some objects and which clash in some objects.
11578 void wxTextAttrBorder
::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11580 if (attr
.HasStyle())
11582 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11586 if (GetStyle() != attr
.GetStyle())
11588 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11589 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11593 SetStyle(attr
.GetStyle());
11597 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11599 if (attr
.HasColour())
11601 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11605 if (GetColour() != attr
.GetColour())
11607 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11608 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11612 SetColour(attr
.GetColourLong());
11616 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11618 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11621 // Partial equality test
11622 bool wxTextAttrBorders
::EqPartial(const wxTextAttrBorders
& borders
) const
11624 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11625 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11628 // Apply border to 'this', but not if the same as compareWith
11629 bool wxTextAttrBorders
::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11631 m_left
.Apply(borders
.m_left
, compareWith ?
(& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11632 m_right
.Apply(borders
.m_right
, compareWith ?
(& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11633 m_top
.Apply(borders
.m_top
, compareWith ?
(& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11634 m_bottom
.Apply(borders
.m_bottom
, compareWith ?
(& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11638 // Remove specified attributes from this object
11639 bool wxTextAttrBorders
::RemoveStyle(const wxTextAttrBorders
& attr
)
11641 m_left
.RemoveStyle(attr
.m_left
);
11642 m_right
.RemoveStyle(attr
.m_right
);
11643 m_top
.RemoveStyle(attr
.m_top
);
11644 m_bottom
.RemoveStyle(attr
.m_bottom
);
11648 // Collects the attributes that are common to a range of content, building up a note of
11649 // which attributes are absent in some objects and which clash in some objects.
11650 void wxTextAttrBorders
::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11652 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11653 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11654 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11655 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11658 // Set style of all borders
11659 void wxTextAttrBorders
::SetStyle(int style
)
11661 m_left
.SetStyle(style
);
11662 m_right
.SetStyle(style
);
11663 m_top
.SetStyle(style
);
11664 m_bottom
.SetStyle(style
);
11667 // Set colour of all borders
11668 void wxTextAttrBorders
::SetColour(unsigned long colour
)
11670 m_left
.SetColour(colour
);
11671 m_right
.SetColour(colour
);
11672 m_top
.SetColour(colour
);
11673 m_bottom
.SetColour(colour
);
11676 void wxTextAttrBorders
::SetColour(const wxColour
& colour
)
11678 m_left
.SetColour(colour
);
11679 m_right
.SetColour(colour
);
11680 m_top
.SetColour(colour
);
11681 m_bottom
.SetColour(colour
);
11684 // Set width of all borders
11685 void wxTextAttrBorders
::SetWidth(const wxTextAttrDimension
& width
)
11687 m_left
.SetWidth(width
);
11688 m_right
.SetWidth(width
);
11689 m_top
.SetWidth(width
);
11690 m_bottom
.SetWidth(width
);
11693 // Partial equality test
11694 bool wxTextAttrDimension
::EqPartial(const wxTextAttrDimension
& dim
) const
11696 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11702 bool wxTextAttrDimension
::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11706 if (!(compareWith
&& dim
== (*compareWith
)))
11713 // Collects the attributes that are common to a range of content, building up a note of
11714 // which attributes are absent in some objects and which clash in some objects.
11715 void wxTextAttrDimension
::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11717 if (attr
.IsValid())
11719 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11723 if (!((*this) == attr
))
11725 clashingAttr
.SetValid(true);
11734 absentAttr
.SetValid(true);
11737 wxTextAttrDimensionConverter
::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11739 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11742 wxTextAttrDimensionConverter
::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11744 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11747 int wxTextAttrDimensionConverter
::ConvertTenthsMMToPixels(int units
) const
11749 return wxRichTextObject
::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11752 int wxTextAttrDimensionConverter
::ConvertPixelsToTenthsMM(int pixels
) const
11754 return wxRichTextObject
::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11757 int wxTextAttrDimensionConverter
::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11759 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11760 return ConvertTenthsMMToPixels(dim
.GetValue());
11761 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11762 return dim
.GetValue();
11763 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11765 wxASSERT(m_parentSize
!= wxDefaultSize
);
11766 if (direction
== wxHORIZONTAL
)
11767 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11769 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11778 int wxTextAttrDimensionConverter
::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11780 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11781 return dim
.GetValue();
11782 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11783 return ConvertPixelsToTenthsMM(dim
.GetValue());
11791 // Partial equality test
11792 bool wxTextAttrDimensions
::EqPartial(const wxTextAttrDimensions
& dims
) const
11794 if (!m_left
.EqPartial(dims
.m_left
))
11797 if (!m_right
.EqPartial(dims
.m_right
))
11800 if (!m_top
.EqPartial(dims
.m_top
))
11803 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11809 // Apply border to 'this', but not if the same as compareWith
11810 bool wxTextAttrDimensions
::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11812 m_left
.Apply(dims
.m_left
, compareWith ?
(& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11813 m_right
.Apply(dims
.m_right
, compareWith ?
(& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11814 m_top
.Apply(dims
.m_top
, compareWith ?
(& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11815 m_bottom
.Apply(dims
.m_bottom
, compareWith ?
(& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11820 // Remove specified attributes from this object
11821 bool wxTextAttrDimensions
::RemoveStyle(const wxTextAttrDimensions
& attr
)
11823 if (attr
.m_left
.IsValid())
11825 if (attr
.m_right
.IsValid())
11827 if (attr
.m_top
.IsValid())
11829 if (attr
.m_bottom
.IsValid())
11835 // Collects the attributes that are common to a range of content, building up a note of
11836 // which attributes are absent in some objects and which clash in some objects.
11837 void wxTextAttrDimensions
::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11839 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11840 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11841 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11842 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11845 // Partial equality test
11846 bool wxTextAttrSize
::EqPartial(const wxTextAttrSize
& size
) const
11848 if (!m_width
.EqPartial(size
.m_width
))
11851 if (!m_height
.EqPartial(size
.m_height
))
11857 // Apply border to 'this', but not if the same as compareWith
11858 bool wxTextAttrSize
::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11860 m_width
.Apply(size
.m_width
, compareWith ?
(& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11861 m_height
.Apply(size
.m_height
, compareWith ?
(& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11866 // Remove specified attributes from this object
11867 bool wxTextAttrSize
::RemoveStyle(const wxTextAttrSize
& attr
)
11869 if (attr
.m_width
.IsValid())
11871 if (attr
.m_height
.IsValid())
11877 // Collects the attributes that are common to a range of content, building up a note of
11878 // which attributes are absent in some objects and which clash in some objects.
11879 void wxTextAttrSize
::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11881 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11882 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11885 // Collects the attributes that are common to a range of content, building up a note of
11886 // which attributes are absent in some objects and which clash in some objects.
11887 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11889 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11890 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11892 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11894 if (attr
.HasFont())
11896 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11898 if (currentStyle
.HasFontSize())
11900 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11902 // Clash of attr - mark as such
11903 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11904 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11908 currentStyle
.SetFontSize(attr
.GetFontSize());
11911 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11913 if (currentStyle
.HasFontItalic())
11915 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11917 // Clash of attr - mark as such
11918 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11919 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11923 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11926 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11928 if (currentStyle
.HasFontFamily())
11930 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11932 // Clash of attr - mark as such
11933 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11934 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11938 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11941 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11943 if (currentStyle
.HasFontWeight())
11945 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11947 // Clash of attr - mark as such
11948 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11949 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11953 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11956 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11958 if (currentStyle
.HasFontFaceName())
11960 wxString
faceName1(currentStyle
.GetFontFaceName());
11961 wxString
faceName2(attr
.GetFontFaceName());
11963 if (faceName1
!= faceName2
)
11965 // Clash of attr - mark as such
11966 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11967 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11971 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11974 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11976 if (currentStyle
.HasFontUnderlined())
11978 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11980 // Clash of attr - mark as such
11981 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11982 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11986 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11990 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11992 if (currentStyle
.HasTextColour())
11994 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11996 // Clash of attr - mark as such
11997 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11998 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12002 currentStyle
.SetTextColour(attr
.GetTextColour());
12005 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12007 if (currentStyle
.HasBackgroundColour())
12009 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12011 // Clash of attr - mark as such
12012 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12013 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12017 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12020 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12022 if (currentStyle
.HasAlignment())
12024 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12026 // Clash of attr - mark as such
12027 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12028 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12032 currentStyle
.SetAlignment(attr
.GetAlignment());
12035 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12037 if (currentStyle
.HasTabs())
12039 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12041 // Clash of attr - mark as such
12042 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12043 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12047 currentStyle
.SetTabs(attr
.GetTabs());
12050 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12052 if (currentStyle
.HasLeftIndent())
12054 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12056 // Clash of attr - mark as such
12057 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12058 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12062 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12065 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12067 if (currentStyle
.HasRightIndent())
12069 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12071 // Clash of attr - mark as such
12072 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12073 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12077 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12080 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12082 if (currentStyle
.HasParagraphSpacingAfter())
12084 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12086 // Clash of attr - mark as such
12087 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12088 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12092 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12095 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12097 if (currentStyle
.HasParagraphSpacingBefore())
12099 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12101 // Clash of attr - mark as such
12102 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12103 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12107 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12110 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12112 if (currentStyle
.HasLineSpacing())
12114 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12116 // Clash of attr - mark as such
12117 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12118 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12122 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12125 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12127 if (currentStyle
.HasCharacterStyleName())
12129 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12131 // Clash of attr - mark as such
12132 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12133 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12137 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12140 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12142 if (currentStyle
.HasParagraphStyleName())
12144 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12146 // Clash of attr - mark as such
12147 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12148 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12152 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12155 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12157 if (currentStyle
.HasListStyleName())
12159 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12161 // Clash of attr - mark as such
12162 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12163 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12167 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12170 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12172 if (currentStyle
.HasBulletStyle())
12174 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12176 // Clash of attr - mark as such
12177 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12178 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12182 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12185 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12187 if (currentStyle
.HasBulletNumber())
12189 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12191 // Clash of attr - mark as such
12192 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12193 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12197 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12200 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12202 if (currentStyle
.HasBulletText())
12204 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12206 // Clash of attr - mark as such
12207 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12208 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12213 currentStyle
.SetBulletText(attr
.GetBulletText());
12214 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12218 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12220 if (currentStyle
.HasBulletName())
12222 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12224 // Clash of attr - mark as such
12225 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12226 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12231 currentStyle
.SetBulletName(attr
.GetBulletName());
12235 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12237 if (currentStyle
.HasURL())
12239 if (currentStyle
.GetURL() != attr
.GetURL())
12241 // Clash of attr - mark as such
12242 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12243 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12248 currentStyle
.SetURL(attr
.GetURL());
12252 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12254 if (currentStyle
.HasTextEffects())
12256 // We need to find the bits in the new attr that are different:
12257 // just look at those bits that are specified by the new attr.
12259 // We need to remove the bits and flags that are not common between current attr
12260 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12261 // previous styles.
12263 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12264 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12266 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12268 // Find the text effects that were different, using XOR
12269 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12271 // Clash of attr - mark as such
12272 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12273 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12278 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12279 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12282 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12283 // that we've looked at so far
12284 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12285 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12287 if (currentStyle
.GetTextEffectFlags() == 0)
12288 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12291 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12293 if (currentStyle
.HasOutlineLevel())
12295 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12297 // Clash of attr - mark as such
12298 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12299 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12303 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12307 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12309 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12311 bool wxRichTextProperties
::operator==(const wxRichTextProperties
& props
) const
12313 if (m_properties
.GetCount() != props
.GetCount())
12317 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12319 const wxVariant
& var1
= m_properties
[i
];
12320 int idx
= props
.Find(var1
.GetName());
12323 const wxVariant
& var2
= props
.m_properties
[idx
];
12324 if (!(var1
== var2
))
12331 wxArrayString wxRichTextProperties
::GetPropertyNames() const
12335 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12337 arr
.Add(m_properties
[i
].GetName());
12342 int wxRichTextProperties
::Find(const wxString
& name
) const
12345 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12347 if (m_properties
[i
].GetName() == name
)
12353 bool wxRichTextProperties
::Remove(const wxString
& name
)
12355 int idx
= Find(name
);
12358 m_properties
.RemoveAt(idx
);
12365 wxVariant
* wxRichTextProperties
::FindOrCreateProperty(const wxString
& name
)
12367 int idx
= Find(name
);
12368 if (idx
== wxNOT_FOUND
)
12369 SetProperty(name
, wxString());
12371 if (idx
!= wxNOT_FOUND
)
12373 return & (*this)[idx
];
12379 const wxVariant
& wxRichTextProperties
::GetProperty(const wxString
& name
) const
12381 static const wxVariant nullVariant
;
12382 int idx
= Find(name
);
12384 return m_properties
[idx
];
12386 return nullVariant
;
12389 wxString wxRichTextProperties
::GetPropertyString(const wxString
& name
) const
12391 return GetProperty(name
).GetString();
12394 long wxRichTextProperties
::GetPropertyLong(const wxString
& name
) const
12396 return GetProperty(name
).GetLong();
12399 bool wxRichTextProperties
::GetPropertyBool(const wxString
& name
) const
12401 return GetProperty(name
).GetBool();
12404 double wxRichTextProperties
::GetPropertyDouble(const wxString
& name
) const
12406 return GetProperty(name
).GetDouble();
12409 void wxRichTextProperties
::SetProperty(const wxVariant
& variant
)
12411 wxASSERT(!variant
.GetName().IsEmpty());
12413 int idx
= Find(variant
.GetName());
12416 m_properties
.Add(variant
);
12418 m_properties
[idx
] = variant
;
12421 void wxRichTextProperties
::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12423 int idx
= Find(name
);
12424 wxVariant
var(variant
);
12428 m_properties
.Add(var
);
12430 m_properties
[idx
] = var
;
12433 void wxRichTextProperties
::SetProperty(const wxString
& name
, const wxString
& value
)
12435 SetProperty(name
, wxVariant(value
, name
));
12438 void wxRichTextProperties
::SetProperty(const wxString
& name
, long value
)
12440 SetProperty(name
, wxVariant(value
, name
));
12443 void wxRichTextProperties
::SetProperty(const wxString
& name
, double value
)
12445 SetProperty(name
, wxVariant(value
, name
));
12448 void wxRichTextProperties
::SetProperty(const wxString
& name
, bool value
)
12450 SetProperty(name
, wxVariant(value
, name
));
12453 void wxRichTextProperties
::RemoveProperties(const wxRichTextProperties
& properties
)
12456 for (i
= 0; i
< properties
.GetCount(); i
++)
12458 wxString name
= properties
.GetProperties()[i
].GetName();
12459 if (HasProperty(name
))
12464 void wxRichTextProperties
::MergeProperties(const wxRichTextProperties
& properties
)
12467 for (i
= 0; i
< properties
.GetCount(); i
++)
12469 SetProperty(properties
.GetProperties()[i
]);
12473 wxRichTextObject
* wxRichTextObjectAddress
::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12475 if (m_address
.GetCount() == 0)
12476 return topLevelContainer
;
12478 wxRichTextCompositeObject
* p
= topLevelContainer
;
12480 while (p
&& i
< m_address
.GetCount())
12482 int pos
= m_address
[i
];
12483 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12484 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12487 wxRichTextObject
* p1
= p
->GetChild(pos
);
12488 if (i
== (m_address
.GetCount()-1))
12491 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12497 bool wxRichTextObjectAddress
::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12501 if (topLevelContainer
== obj
)
12504 wxRichTextObject
* o
= obj
;
12507 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12511 int pos
= p
->GetChildren().IndexOf(o
);
12515 m_address
.Insert(pos
, 0);
12517 if (p
== topLevelContainer
)
12526 bool wxRichTextSelection
::operator==(const wxRichTextSelection
& sel
) const
12528 if (m_container
!= sel
.m_container
)
12530 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12533 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12534 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12539 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12540 // or none at the level of the object's container.
12541 wxRichTextRangeArray wxRichTextSelection
::GetSelectionForObject(wxRichTextObject
* obj
) const
12545 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12547 if (container
== m_container
)
12550 container
= obj
->GetContainer();
12553 if (container
->GetParent())
12555 // If we found that our object's container is within the range of
12556 // a selection higher up, then assume the whole original object
12557 // is also selected.
12558 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12559 if (parentContainer
== m_container
)
12561 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12563 wxRichTextRangeArray ranges
;
12564 ranges
.Add(obj
->GetRange());
12569 container
= parentContainer
;
12578 return wxRichTextRangeArray();
12581 // Is the given position within the selection?
12582 bool wxRichTextSelection
::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12588 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12589 return WithinSelection(pos
, selectionRanges
);
12593 // Is the given position within the selection range?
12594 bool wxRichTextSelection
::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12597 for (i
= 0; i
< ranges
.GetCount(); i
++)
12599 const wxRichTextRange
& range
= ranges
[i
];
12600 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12606 // Is the given range completely within the selection range?
12607 bool wxRichTextSelection
::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12610 for (i
= 0; i
< ranges
.GetCount(); i
++)
12612 const wxRichTextRange
& eachRange
= ranges
[i
];
12613 if (range
.IsWithin(eachRange
))
12619 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
12620 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
12622 bool wxRichTextDrawingContext
::HasVirtualAttributes(wxRichTextObject
* obj
) const
12624 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12627 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12628 if (handler
->HasVirtualAttributes(obj
))
12631 node
= node
->GetNext();
12636 wxRichTextAttr wxRichTextDrawingContext
::GetVirtualAttributes(wxRichTextObject
* obj
) const
12638 wxRichTextAttr attr
;
12639 // We apply all handlers, so we can may combine several different attributes
12640 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12643 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12644 if (handler
->HasVirtualAttributes(obj
))
12646 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
12648 wxUnusedVar(success
);
12651 node
= node
->GetNext();
12656 bool wxRichTextDrawingContext
::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
12658 if (HasVirtualAttributes(obj
))
12660 wxRichTextAttr
a(GetVirtualAttributes(obj
));
12668 /// Adds a handler to the end
12669 void wxRichTextBuffer
::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
12671 sm_drawingHandlers
.Append(handler
);
12674 /// Inserts a handler at the front
12675 void wxRichTextBuffer
::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
12677 sm_drawingHandlers
.Insert( handler
);
12680 /// Removes a handler
12681 bool wxRichTextBuffer
::RemoveDrawingHandler(const wxString
& name
)
12683 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
12686 sm_drawingHandlers
.DeleteObject(handler
);
12694 wxRichTextDrawingHandler
* wxRichTextBuffer
::FindDrawingHandler(const wxString
& name
)
12696 wxList
::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12699 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12700 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
12702 node
= node
->GetNext();
12707 void wxRichTextBuffer
::CleanUpDrawingHandlers()
12709 wxList
::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12712 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12713 wxList
::compatibility_iterator next
= node
->GetNext();
12718 sm_drawingHandlers
.Clear();