1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
20 #include "wx/richtext/richtextbuffer.h"
26 #include "wx/dataobj.h"
27 #include "wx/module.h"
30 #include "wx/settings.h"
31 #include "wx/filename.h"
32 #include "wx/clipbrd.h"
33 #include "wx/wfstream.h"
34 #include "wx/mstream.h"
35 #include "wx/sstream.h"
36 #include "wx/textfile.h"
37 #include "wx/hashmap.h"
38 #include "wx/dynarray.h"
40 #include "wx/richtext/richtextctrl.h"
41 #include "wx/richtext/richtextstyles.h"
42 #include "wx/richtext/richtextimagedlg.h"
43 #include "wx/richtext/richtextsizepage.h"
44 #include "wx/richtext/richtextxml.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
)
470 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
472 const wxPen
& pen1
= dc
.GetPen();
473 if (pen1
.IsOk() && pen
.IsOk())
475 if (pen1
.GetWidth() == pen
.GetWidth() &&
476 pen1
.GetStyle() == pen
.GetStyle() &&
477 pen1
.GetColour() == pen
.GetColour())
483 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
485 const wxBrush
& brush1
= dc
.GetBrush();
486 if (brush1
.IsOk() && brush
.IsOk())
488 if (brush1
.GetStyle() == brush
.GetStyle() &&
489 brush1
.GetColour() == brush
.GetColour())
497 * This is the base for drawable objects.
500 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
502 wxRichTextObject
::wxRichTextObject(wxRichTextObject
* parent
)
510 wxRichTextObject
::~wxRichTextObject()
514 void wxRichTextObject
::Dereference()
522 void wxRichTextObject
::Copy(const wxRichTextObject
& obj
)
525 m_maxSize
= obj
.m_maxSize
;
526 m_minSize
= obj
.m_minSize
;
528 m_range
= obj
.m_range
;
529 m_ownRange
= obj
.m_ownRange
;
530 m_attributes
= obj
.m_attributes
;
531 m_properties
= obj
.m_properties
;
532 m_descent
= obj
.m_descent
;
536 // Get/set the top-level container of this object.
537 wxRichTextParagraphLayoutBox
* wxRichTextObject
::GetContainer() const
539 const wxRichTextObject
* p
= this;
544 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
551 void wxRichTextObject
::SetMargins(int margin
)
553 SetMargins(margin
, margin
, margin
, margin
);
556 void wxRichTextObject
::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
558 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
559 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
564 int wxRichTextObject
::GetLeftMargin() const
566 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
569 int wxRichTextObject
::GetRightMargin() const
571 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
574 int wxRichTextObject
::GetTopMargin() const
576 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
579 int wxRichTextObject
::GetBottomMargin() const
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
584 // Calculate the available content space in the given rectangle, given the
585 // margins, border and padding specified in the object's attributes.
586 wxRect wxRichTextObject
::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
588 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
589 marginRect
= outerRect
;
590 wxRichTextAttr
attr(GetAttributes());
591 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
592 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
596 // Invalidate the buffer. With no argument, invalidates whole buffer.
597 void wxRichTextObject
::Invalidate(const wxRichTextRange
& invalidRange
)
599 if (invalidRange
!= wxRICHTEXT_NONE
)
601 // If this is a floating object, size may not be recalculated
602 // after floats have been collected in an early stage of Layout.
603 // So avoid resetting the cache for floating objects during layout.
604 if (!IsFloating() || !wxRichTextBuffer
::GetFloatingLayoutMode())
605 SetCachedSize(wxDefaultSize
);
606 SetMaxSize(wxDefaultSize
);
607 SetMinSize(wxDefaultSize
);
611 // Convert units in tenths of a millimetre to device units
612 int wxRichTextObject
::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
617 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
618 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
623 // Convert units in tenths of a millimetre to device units
624 int wxRichTextObject
::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
626 // There are ppi pixels in 254.1 "1/10 mm"
628 double pixels
= ((double) units
* (double)ppi
) / 254.1;
632 // If the result is very small, make it at least one pixel in size.
633 if (pixels
== 0 && units
> 0)
639 // Convert units in pixels to tenths of a millimetre
640 int wxRichTextObject
::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
645 scale
= GetBuffer()->GetScale();
647 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
650 int wxRichTextObject
::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
652 // There are ppi pixels in 254.1 "1/10 mm"
654 double p
= double(pixels
);
659 int units
= int( p
* 254.1 / (double) ppi
);
663 // Draw the borders and background for the given rectangle and attributes.
664 // Width and height are taken to be the outer margin size, not the content.
665 bool wxRichTextObject
::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
667 // Assume boxRect is the area around the content
668 wxRect marginRect
= boxRect
;
669 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
671 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
673 // Margin is transparent. Draw background from margin.
674 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
677 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
679 // TODO: get selection colour from control?
680 colour
= wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
683 colour
= attr
.GetBackgroundColour();
686 wxBrush
brush(colour
);
690 dc
.DrawRectangle(borderRect
);
693 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
695 wxRichTextAttr editBorderAttr
= attr
;
696 // TODO: make guideline colour configurable
697 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
701 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
704 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
705 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
707 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
708 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
714 bool wxRichTextObject
::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
716 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
717 wxTextAttrDimensionConverter
converter(dc
, buffer ? buffer
->GetScale() : 1.0);
719 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
721 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
722 wxColour
col(attr
.GetLeft().GetColour());
724 // If pen width is > 1, resorts to a solid rectangle.
727 int penStyle
= wxSOLID
;
728 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
730 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
731 penStyle
= wxLONG_DASH
;
732 wxPen
pen(col
, 1, penStyle
);
734 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
737 else if (borderLeft
> 1)
743 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
747 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
749 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
751 wxColour
col(attr
.GetRight().GetColour());
753 // If pen width is > 1, resorts to a solid rectangle.
754 if (borderRight
== 1)
756 int penStyle
= wxSOLID
;
757 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
759 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
760 penStyle
= wxLONG_DASH
;
761 wxPen
pen(col
, 1, penStyle
);
763 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
766 else if (borderRight
> 1)
772 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
776 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
778 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
780 wxColour
col(attr
.GetTop().GetColour());
782 // If pen width is > 1, resorts to a solid rectangle.
785 int penStyle
= wxSOLID
;
786 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
788 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
789 penStyle
= wxLONG_DASH
;
790 wxPen
pen(col
, 1, penStyle
);
792 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
795 else if (borderTop
> 1)
801 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
805 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
807 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
808 wxColour
col(attr
.GetBottom().GetColour());
810 // If pen width is > 1, resorts to a solid rectangle.
811 if (borderBottom
== 1)
813 int penStyle
= wxSOLID
;
814 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
816 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
817 penStyle
= wxLONG_DASH
;
818 wxPen
pen(col
, 1, penStyle
);
820 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
823 else if (borderBottom
> 1)
829 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
836 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
837 // or marginRect (outer), and the other must be the default rectangle (no width or height).
838 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
841 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843 bool wxRichTextObject
::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
845 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
846 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
847 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
848 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
850 wxTextAttrDimensionConverter
converter(dc
, buffer ? buffer
->GetScale() : 1.0);
852 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
853 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
854 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
855 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
856 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
857 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
858 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
859 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
861 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
862 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
863 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
864 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
865 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
866 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
867 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
868 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
871 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
872 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
873 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
874 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
875 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
876 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
877 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
879 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
880 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
881 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
882 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
883 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
884 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
885 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
886 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
889 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
890 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
891 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
893 if (marginRect
!= wxRect())
895 contentRect
.x
= marginRect
.x
+ leftTotal
;
896 contentRect
.y
= marginRect
.y
+ topTotal
;
897 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
898 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
902 marginRect
.x
= contentRect
.x
- leftTotal
;
903 marginRect
.y
= contentRect
.y
- topTotal
;
904 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
905 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
908 borderRect
.x
= marginRect
.x
+ marginLeft
;
909 borderRect
.y
= marginRect
.y
+ marginTop
;
910 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
911 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
913 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
914 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
915 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
916 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
918 // The outline is outside the margin and doesn't influence the overall box position or content size.
919 outlineRect
.x
= marginRect
.x
- outlineLeft
;
920 outlineRect
.y
= marginRect
.y
- outlineTop
;
921 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
922 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
927 // Get the total margin for the object in pixels, taking into account margin, padding and border size
928 bool wxRichTextObject
::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
929 int& topMargin
, int& bottomMargin
)
931 // Assume boxRect is the area around the content
932 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
933 marginRect
= wxRect(0, 0, 1000, 1000);
935 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
937 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
938 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
939 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
940 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
945 // Returns the rectangle which the child has available to it given restrictions specified in the
946 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
947 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
948 // E.g. a cell that's 50% of its parent.
949 wxRect wxRichTextObject
::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
951 wxRect rect
= availableParentSpace
;
954 scale
= buffer
->GetScale();
956 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
958 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
959 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
961 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
962 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
964 // Can specify either left or right for the position (we're assuming we can't
965 // set the left and right edges to effectively set the size. Would we want to do that?)
966 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
970 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
973 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
974 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
979 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
983 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
986 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
987 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
992 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
993 rect
.SetWidth(availableParentSpace
.GetWidth());
998 // Dump to output stream for debugging
999 void wxRichTextObject
::Dump(wxTextOutputStream
& stream
)
1001 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1002 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");
1003 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");
1006 // Gets the containing buffer
1007 wxRichTextBuffer
* wxRichTextObject
::GetBuffer() const
1009 const wxRichTextObject
* obj
= this;
1010 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1011 obj
= obj
->GetParent();
1012 return wxDynamicCast(obj
, wxRichTextBuffer
);
1015 // Get the absolute object position, by traversing up the child/parent hierarchy
1016 wxPoint wxRichTextObject
::GetAbsolutePosition() const
1018 wxPoint pt
= GetPosition();
1020 wxRichTextObject
* p
= GetParent();
1023 pt
= pt
+ p
->GetPosition();
1030 // Hit-testing: returns a flag indicating hit test details, plus
1031 // information about position
1032 int wxRichTextObject
::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1035 return wxRICHTEXT_HITTEST_NONE
;
1037 wxRect rect
= GetRect();
1038 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1039 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1042 *contextObj
= GetParentContainer();
1043 textPosition
= GetRange().GetStart();
1044 return wxRICHTEXT_HITTEST_ON
;
1047 return wxRICHTEXT_HITTEST_NONE
;
1050 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1051 // lays out the object again using the maximum ('best') size
1052 bool wxRichTextObject
::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1053 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1054 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1057 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1058 wxRect originalAvailableRect
= availableChildRect
;
1059 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1061 wxSize maxSize
= GetMaxSize();
1063 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1067 // Redo the layout with a fixed, minimum size this time.
1068 Invalidate(wxRICHTEXT_ALL
);
1069 wxRichTextAttr
newAttr(attr
);
1070 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1073 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1075 // If a paragraph, align the whole paragraph.
1076 // Problem with this: if we're limited by a floating object, a line may be centered
1077 // w.r.t. the smaller resulting box rather than the actual available width.
1078 // FIXME: aligning whole paragraph not compatible with floating objects
1079 if (attr
.HasAlignment() && (!wxRichTextBuffer
::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1081 // centering, right-justification
1082 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1084 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1086 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1088 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1092 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1106 // Move the object recursively, by adding the offset from old to new
1107 void wxRichTextObject
::Move(const wxPoint
& pt
)
1114 * wxRichTextCompositeObject
1115 * This is the base for drawable objects.
1118 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1120 wxRichTextCompositeObject
::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1121 wxRichTextObject(parent
)
1125 wxRichTextCompositeObject
::~wxRichTextCompositeObject()
1130 /// Get the nth child
1131 wxRichTextObject
* wxRichTextCompositeObject
::GetChild(size_t n
) const
1133 wxASSERT ( n
< m_children
.GetCount() );
1135 return m_children
.Item(n
)->GetData();
1138 /// Append a child, returning the position
1139 size_t wxRichTextCompositeObject
::AppendChild(wxRichTextObject
* child
)
1141 m_children
.Append(child
);
1142 child
->SetParent(this);
1143 return m_children
.GetCount() - 1;
1146 /// Insert the child in front of the given object, or at the beginning
1147 bool wxRichTextCompositeObject
::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1151 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1152 m_children
.Insert(node
, child
);
1155 m_children
.Insert(child
);
1156 child
->SetParent(this);
1161 /// Delete the child
1162 bool wxRichTextCompositeObject
::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1164 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(child
);
1167 wxRichTextObject
* obj
= node
->GetData();
1168 m_children
.Erase(node
);
1177 /// Delete all children
1178 bool wxRichTextCompositeObject
::DeleteChildren()
1180 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1183 wxRichTextObjectList
::compatibility_iterator oldNode
= node
;
1185 wxRichTextObject
* child
= node
->GetData();
1186 child
->Dereference(); // Only delete if reference count is zero
1188 node
= node
->GetNext();
1189 m_children
.Erase(oldNode
);
1195 /// Get the child count
1196 size_t wxRichTextCompositeObject
::GetChildCount() const
1198 return m_children
.GetCount();
1202 void wxRichTextCompositeObject
::Copy(const wxRichTextCompositeObject
& obj
)
1204 wxRichTextObject
::Copy(obj
);
1208 wxRichTextObjectList
::compatibility_iterator node
= obj
.m_children
.GetFirst();
1211 wxRichTextObject
* child
= node
->GetData();
1212 wxRichTextObject
* newChild
= child
->Clone();
1213 newChild
->SetParent(this);
1214 m_children
.Append(newChild
);
1216 node
= node
->GetNext();
1220 /// Hit-testing: returns a flag indicating hit test details, plus
1221 /// information about position
1222 int wxRichTextCompositeObject
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1225 return wxRICHTEXT_HITTEST_NONE
;
1227 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1230 wxRichTextObject
* child
= node
->GetData();
1232 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1234 // Just check if we hit the overall object
1235 int ret
= child
->wxRichTextObject
::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1236 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1239 else if (child
->IsShown())
1241 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1242 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1246 node
= node
->GetNext();
1249 return wxRICHTEXT_HITTEST_NONE
;
1252 /// Finds the absolute position and row height for the given character position
1253 bool wxRichTextCompositeObject
::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1255 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1258 wxRichTextObject
* child
= node
->GetData();
1260 // Don't recurse if the child is a top-level object,
1261 // such as a text box, because the character position will no longer
1262 // apply. By definition, a top-level object has its own range of
1263 // character positions.
1264 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1267 node
= node
->GetNext();
1274 void wxRichTextCompositeObject
::CalculateRange(long start
, long& end
)
1276 long current
= start
;
1277 long lastEnd
= current
;
1285 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1288 wxRichTextObject
* child
= node
->GetData();
1291 child
->CalculateRange(current
, childEnd
);
1294 current
= childEnd
+ 1;
1296 node
= node
->GetNext();
1301 // A top-level object always has a range of size 1,
1302 // because its children don't count at this level.
1304 m_range
.SetRange(start
, start
);
1306 // An object with no children has zero length
1307 if (m_children
.GetCount() == 0)
1309 m_ownRange
.SetRange(0, lastEnd
);
1315 // An object with no children has zero length
1316 if (m_children
.GetCount() == 0)
1319 m_range
.SetRange(start
, end
);
1323 /// Delete range from layout.
1324 bool wxRichTextCompositeObject
::DeleteRange(const wxRichTextRange
& range
)
1326 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1330 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1331 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
1333 // Delete the range in each paragraph
1335 // When a chunk has been deleted, internally the content does not
1336 // now match the ranges.
1337 // However, so long as deletion is not done on the same object twice this is OK.
1338 // If you may delete content from the same object twice, recalculate
1339 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1340 // adjust the range you're deleting accordingly.
1342 if (!obj
->GetRange().IsOutside(range
))
1344 // No need to delete within a top-level object; just removing this object will do fine
1345 if (!obj
->IsTopLevel())
1346 obj
->DeleteRange(range
);
1348 // Delete an empty object, or paragraph within this range.
1349 if (obj
->IsEmpty() ||
1350 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1352 // An empty paragraph has length 1, so won't be deleted unless the
1353 // whole range is deleted.
1354 RemoveChild(obj
, true);
1364 /// Get any text in this object for the given range
1365 wxString wxRichTextCompositeObject
::GetTextForRange(const wxRichTextRange
& range
) const
1368 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1371 wxRichTextObject
* child
= node
->GetData();
1372 wxRichTextRange childRange
= range
;
1373 if (!child
->GetRange().IsOutside(range
))
1375 childRange
.LimitTo(child
->GetRange());
1377 wxString childText
= child
->GetTextForRange(childRange
);
1381 node
= node
->GetNext();
1387 /// Get the child object at the given character position
1388 wxRichTextObject
* wxRichTextCompositeObject
::GetChildAtPosition(long pos
) const
1390 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1393 wxRichTextObject
* child
= node
->GetData();
1394 if (child
->GetRange().GetStart() == pos
)
1396 node
= node
->GetNext();
1401 /// Recursively merge all pieces that can be merged.
1402 bool wxRichTextCompositeObject
::Defragment(wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
)
1404 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1407 wxRichTextObject
* child
= node
->GetData();
1408 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1410 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1412 composite
->Defragment(context
);
1414 // Optimization: if there are no virtual attributes, we won't need to
1415 // to split objects in order to paint individually attributed chunks.
1416 // So only merge in this case.
1417 if (!context
.GetVirtualAttributesEnabled())
1419 if (node
->GetNext())
1421 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1422 if (child
->CanMerge(nextChild
, context
) && child
->Merge(nextChild
, context
))
1424 nextChild
->Dereference();
1425 m_children
.Erase(node
->GetNext());
1428 node
= node
->GetNext();
1431 node
= node
->GetNext();
1435 // If we might have virtual attributes, we first see if we have to split
1436 // objects so that they may be painted with potential virtual attributes,
1437 // since text objects can only draw or measure with a single attributes object
1439 wxRichTextObject
* childAfterSplit
= child
;
1440 if (child
->CanSplit(context
))
1442 childAfterSplit
= child
->Split(context
);
1443 node
= m_children
.Find(childAfterSplit
);
1446 if (node
->GetNext())
1448 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1450 // First split child and nextChild so we have smaller fragments to merge.
1451 // Then Merge only has to test per-object virtual attributes
1452 // because for an object with all the same sub-object attributes,
1453 // then any general virtual attributes should be merged with sub-objects by
1454 // the implementation.
1456 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1458 if (nextChildAfterSplit
->CanSplit(context
))
1459 nextChildAfterSplit
= nextChild
->Split(context
);
1461 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1463 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1464 // Note that we use nextChild because if we had split nextChild, the first object always
1465 // remains (and further parts are appended). However we must use childAfterSplit since
1466 // it's the last part of a possibly split child.
1468 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1470 nextChild
->Dereference();
1471 m_children
.Erase(node
->GetNext());
1473 // Don't set node -- we'll see if we can merge again with the next
1474 // child. UNLESS we split this or the next child, in which case we know we have to
1475 // move on to the end of the next child.
1477 node
= m_children
.Find(nextChildAfterSplit
);
1482 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1484 node
= node
->GetNext();
1488 node
= node
->GetNext();
1492 node
= node
->GetNext();
1495 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1496 if (GetChildCount() > 1)
1498 node
= m_children
.GetFirst();
1501 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
1502 wxRichTextObject
* child
= node
->GetData();
1503 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1505 if (child
->IsEmpty())
1507 child
->Dereference();
1508 m_children
.Erase(node
);
1513 node
= node
->GetNext();
1520 /// Dump to output stream for debugging
1521 void wxRichTextCompositeObject
::Dump(wxTextOutputStream
& stream
)
1523 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1526 wxRichTextObject
* child
= node
->GetData();
1527 child
->Dump(stream
);
1528 node
= node
->GetNext();
1532 /// Get/set the object size for the given range. Returns false if the range
1533 /// is invalid for this object.
1534 bool wxRichTextCompositeObject
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
1536 if (!range
.IsWithin(GetRange()))
1541 wxArrayInt childExtents
;
1548 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1551 wxRichTextObject
* child
= node
->GetData();
1552 if (!child
->GetRange().IsOutside(range
))
1554 // Floating objects have a zero size within the paragraph.
1555 if (child
->IsFloating() && wxRichTextBuffer
::GetFloatingLayoutMode())
1560 if (partialExtents
->GetCount() > 0)
1561 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1565 partialExtents
->Add(0 /* zero size */ + lastSize
);
1572 wxRichTextRange rangeToUse
= range
;
1573 rangeToUse
.LimitTo(child
->GetRange());
1574 if (child
->IsTopLevel())
1575 rangeToUse
= child
->GetOwnRange();
1577 int childDescent
= 0;
1579 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1580 // but it's only going to be used after caching has taken place.
1581 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1583 childDescent
= child
->GetDescent();
1584 childSize
= child
->GetCachedSize();
1586 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1587 sz
.x
+= childSize
.x
;
1588 descent
= wxMax(descent
, childDescent
);
1590 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
1592 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1593 sz
.x
+= childSize
.x
;
1594 descent
= wxMax(descent
, childDescent
);
1596 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1598 child
->SetCachedSize(childSize
);
1599 child
->SetDescent(childDescent
);
1605 if (partialExtents
->GetCount() > 0)
1606 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1611 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1613 partialExtents
->Add(childExtents
[i
] + lastSize
);
1623 node
= node
->GetNext();
1629 // Invalidate the buffer. With no argument, invalidates whole buffer.
1630 void wxRichTextCompositeObject
::Invalidate(const wxRichTextRange
& invalidRange
)
1632 wxRichTextObject
::Invalidate(invalidRange
);
1634 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1637 wxRichTextObject
* child
= node
->GetData();
1638 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1642 else if (child
->IsTopLevel())
1644 if (wxRichTextBuffer
::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1646 // Don't invalidate subhierarchy if we've already been laid out
1650 if (invalidRange
== wxRICHTEXT_NONE
)
1651 child
->Invalidate(wxRICHTEXT_NONE
);
1653 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1657 child
->Invalidate(invalidRange
);
1658 node
= node
->GetNext();
1662 // Move the object recursively, by adding the offset from old to new
1663 void wxRichTextCompositeObject
::Move(const wxPoint
& pt
)
1665 wxPoint oldPos
= GetPosition();
1667 wxPoint offset
= pt
- oldPos
;
1669 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1672 wxRichTextObject
* child
= node
->GetData();
1673 wxPoint childPos
= child
->GetPosition() + offset
;
1674 child
->Move(childPos
);
1675 node
= node
->GetNext();
1681 * wxRichTextParagraphLayoutBox
1682 * This box knows how to lay out paragraphs.
1685 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1687 wxRichTextParagraphLayoutBox
::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1688 wxRichTextCompositeObject(parent
)
1693 wxRichTextParagraphLayoutBox
::~wxRichTextParagraphLayoutBox()
1695 if (m_floatCollector
)
1697 delete m_floatCollector
;
1698 m_floatCollector
= NULL
;
1702 /// Initialize the object.
1703 void wxRichTextParagraphLayoutBox
::Init()
1707 // For now, assume is the only box and has no initial size.
1708 m_range
= wxRichTextRange(0, -1);
1709 m_ownRange
= wxRichTextRange(0, -1);
1711 m_invalidRange
= wxRICHTEXT_ALL
;
1713 m_partialParagraph
= false;
1714 m_floatCollector
= NULL
;
1717 void wxRichTextParagraphLayoutBox
::Clear()
1721 if (m_floatCollector
)
1722 delete m_floatCollector
;
1723 m_floatCollector
= NULL
;
1724 m_partialParagraph
= false;
1728 void wxRichTextParagraphLayoutBox
::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1732 wxRichTextCompositeObject
::Copy(obj
);
1734 m_partialParagraph
= obj
.m_partialParagraph
;
1735 m_defaultAttributes
= obj
.m_defaultAttributes
;
1738 // Gather information about floating objects; only gather floats for those paragraphs that
1739 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1741 bool wxRichTextParagraphLayoutBox
::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1743 if (m_floatCollector
!= NULL
)
1744 delete m_floatCollector
;
1745 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1746 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1747 // Only gather floats up to the point we'll start formatting paragraphs.
1748 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1750 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1751 wxASSERT (child
!= NULL
);
1753 m_floatCollector
->CollectFloat(child
);
1754 node
= node
->GetNext();
1760 // Returns the style sheet associated with the overall buffer.
1761 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox
::GetStyleSheet() const
1763 return GetBuffer() ?
GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1766 // Get the number of floating objects at this level
1767 int wxRichTextParagraphLayoutBox
::GetFloatingObjectCount() const
1769 if (m_floatCollector
)
1770 return m_floatCollector
->GetFloatingObjectCount();
1775 // Get a list of floating objects
1776 bool wxRichTextParagraphLayoutBox
::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1778 if (m_floatCollector
)
1780 return m_floatCollector
->GetFloatingObjects(objects
);
1787 void wxRichTextParagraphLayoutBox
::UpdateRanges()
1791 start
= GetRange().GetStart();
1793 CalculateRange(start
, end
);
1797 int wxRichTextParagraphLayoutBox
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1800 return wxRICHTEXT_HITTEST_NONE
;
1802 int ret
= wxRICHTEXT_HITTEST_NONE
;
1803 if (wxRichTextBuffer
::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1804 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1806 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1807 return wxRichTextCompositeObject
::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1815 /// Draw the floating objects
1816 void wxRichTextParagraphLayoutBox
::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1818 if (wxRichTextBuffer
::GetFloatingLayoutMode() && m_floatCollector
)
1819 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1822 void wxRichTextParagraphLayoutBox
::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1827 from
->RemoveChild(obj
);
1828 to
->AppendChild(obj
);
1832 bool wxRichTextParagraphLayoutBox
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1837 wxRect
thisRect(GetPosition(), GetCachedSize());
1839 wxRichTextAttr
attr(GetAttributes());
1840 context
.ApplyVirtualAttributes(attr
, this);
1843 if (selection
.IsValid() && GetParentContainer() != this && selection
.GetContainer() == this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1844 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1846 // Don't draw guidelines if at top level
1847 int theseFlags
= flags
;
1849 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1850 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1852 if (wxRichTextBuffer
::GetFloatingLayoutMode())
1853 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1855 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1858 wxRichTextObject
* child
= node
->GetData();
1860 if (child
&& !child
->GetRange().IsOutside(range
))
1862 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1863 wxRichTextRange childRange
= range
;
1864 if (child
->IsTopLevel())
1866 childRange
= child
->GetOwnRange();
1869 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1874 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1879 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1882 node
= node
->GetNext();
1887 /// Lay the item out
1888 bool wxRichTextParagraphLayoutBox
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1890 SetPosition(rect
.GetPosition());
1895 wxRect availableSpace
;
1896 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1898 wxRichTextAttr
attr(GetAttributes());
1899 context
.ApplyVirtualAttributes(attr
, this);
1901 // If only laying out a specific area, the passed rect has a different meaning:
1902 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1903 // so that during a size, only the visible part will be relaid out, or
1904 // it would take too long causing flicker. As an approximation, we assume that
1905 // everything up to the start of the visible area is laid out correctly.
1908 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1909 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1911 // Invalidate the part of the buffer from the first visible line
1912 // to the end. If other parts of the buffer are currently invalid,
1913 // then they too will be taken into account if they are above
1914 // the visible point.
1916 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1918 startPos
= line
->GetAbsoluteRange().GetStart();
1920 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1924 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1927 // Fix the width if we're at the top level
1929 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1931 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1932 wxRichTextObject
::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1933 topMargin
, bottomMargin
);
1938 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1939 int maxMaxWidth
= 0;
1941 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1942 int maxMinWidth
= 0;
1944 // If we have vertical alignment, we must recalculate everything.
1945 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1946 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1948 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
1950 bool layoutAll
= true;
1952 // Get invalid range, rounding to paragraph start/end.
1953 wxRichTextRange invalidRange
= GetInvalidRange(true);
1955 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1958 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1960 else // If we know what range is affected, start laying out from that point on.
1961 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1963 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1966 wxRichTextObjectList
::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1967 wxRichTextObjectList
::compatibility_iterator previousNode
;
1969 previousNode
= firstNode
->GetPrevious();
1974 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1975 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1978 // Now we're going to start iterating from the first affected paragraph.
1986 // Gather information about only those floating objects that will not be formatted,
1987 // after which floats will be gathered per-paragraph during layout.
1988 if (wxRichTextBuffer
::GetFloatingLayoutMode())
1989 UpdateFloatingObjects(availableSpace
, node ? node
->GetData() : (wxRichTextObject
*) NULL
);
1991 // A way to force speedy rest-of-buffer layout (the 'else' below)
1992 bool forceQuickLayout
= false;
1994 // First get the size of the paragraphs we won't be laying out
1995 wxRichTextObjectList
::compatibility_iterator n
= m_children
.GetFirst();
1996 while (n
&& n
!= node
)
1998 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2001 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2002 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2003 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2010 // Assume this box only contains paragraphs
2012 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2013 // Unsure if this is needed
2014 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2016 if (child
&& child
->IsShown())
2018 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2019 if ( !forceQuickLayout
&&
2021 child
->GetLines().IsEmpty() ||
2022 !child
->GetRange().IsOutside(invalidRange
)) )
2024 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2025 // lays out the object again using the minimum size
2026 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2027 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2029 // Layout must set the cached size
2030 availableSpace
.y
+= child
->GetCachedSize().y
;
2031 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2032 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2033 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2035 // If we're just formatting the visible part of the buffer,
2036 // and we're now past the bottom of the window, and we don't have any
2037 // floating objects (since they may cause wrapping to change for the rest of the
2038 // the buffer), start quick layout.
2039 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2040 forceQuickLayout
= true;
2044 // We're outside the immediately affected range, so now let's just
2045 // move everything up or down. This assumes that all the children have previously
2046 // been laid out and have wrapped line lists associated with them.
2047 // TODO: check all paragraphs before the affected range.
2049 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2053 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2056 if (child
->GetLines().GetCount() == 0)
2058 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2059 // lays out the object again using the minimum size
2060 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2061 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2063 //child->Layout(dc, availableChildRect, style);
2066 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2068 availableSpace
.y
+= child
->GetCachedSize().y
;
2069 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2070 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2071 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2074 node
= node
->GetNext();
2080 node
= node
->GetNext();
2083 node
= m_children
.GetLast();
2084 if (node
&& node
->GetData()->IsShown())
2086 wxRichTextObject
* child
= node
->GetData();
2087 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2090 maxHeight
= 0; // topMargin + bottomMargin;
2092 // Check the bottom edge of any floating object
2093 if (wxRichTextBuffer
::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2095 int bottom
= GetFloatCollector()->GetLastRectBottom();
2096 if (bottom
> maxHeight
)
2100 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2102 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2103 int w
= r
.GetWidth();
2105 // Convert external to content rect
2106 w
= w
- leftMargin
- rightMargin
;
2107 maxWidth
= wxMax(maxWidth
, w
);
2108 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2112 // TODO: Make sure the layout box's position reflects
2113 // the position of the children, but without
2114 // breaking layout of a box within a paragraph.
2117 // TODO: (also in para layout) should set the
2118 // object's size to an absolute one if specified,
2119 // but if not specified, calculate it from content.
2121 // We need to add back the margins etc.
2123 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2124 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2125 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2126 SetCachedSize(marginRect
.GetSize());
2129 // The maximum size is the greatest of all maximum widths for all paragraphs.
2131 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2132 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2133 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2134 SetMaxSize(marginRect
.GetSize());
2137 // The minimum size is the greatest of all minimum widths for all paragraphs.
2139 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2140 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2141 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2142 SetMinSize(marginRect
.GetSize());
2145 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2146 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2149 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2150 if (leftOverSpace
> 0)
2152 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2154 yOffset
= (leftOverSpace
/2);
2156 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2158 yOffset
= leftOverSpace
;
2162 // Move all the children to vertically align the content
2163 // This doesn't take into account floating objects, unfortunately.
2166 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2169 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2171 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2173 node
= node
->GetNext();
2178 m_invalidRange
= wxRICHTEXT_NONE
;
2183 /// Get/set the size for the given range.
2184 bool wxRichTextParagraphLayoutBox
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2188 wxRichTextObjectList
::compatibility_iterator startPara
= wxRichTextObjectList
::compatibility_iterator();
2189 wxRichTextObjectList
::compatibility_iterator endPara
= wxRichTextObjectList
::compatibility_iterator();
2191 // First find the first paragraph whose starting position is within the range.
2192 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2195 // child is a paragraph
2196 wxRichTextObject
* child
= node
->GetData();
2197 const wxRichTextRange
& r
= child
->GetRange();
2199 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2205 node
= node
->GetNext();
2208 // Next find the last paragraph containing part of the range
2209 node
= m_children
.GetFirst();
2212 // child is a paragraph
2213 wxRichTextObject
* child
= node
->GetData();
2214 const wxRichTextRange
& r
= child
->GetRange();
2216 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2222 node
= node
->GetNext();
2225 if (!startPara
|| !endPara
)
2228 // Now we can add up the sizes
2229 for (node
= startPara
; node
; node
= node
->GetNext())
2231 // child is a paragraph
2232 wxRichTextObject
* child
= node
->GetData();
2233 const wxRichTextRange
& childRange
= child
->GetRange();
2234 wxRichTextRange rangeToFind
= range
;
2235 rangeToFind
.LimitTo(childRange
);
2237 if (child
->IsTopLevel())
2238 rangeToFind
= child
->GetOwnRange();
2242 int childDescent
= 0;
2243 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
, parentSize
);
2245 descent
= wxMax(childDescent
, descent
);
2247 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2248 sz
.y
+= childSize
.y
;
2250 if (node
== endPara
)
2259 /// Get the paragraph at the given position
2260 wxRichTextParagraph
* wxRichTextParagraphLayoutBox
::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2265 // First find the first paragraph whose starting position is within the range.
2266 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2269 // child is a paragraph
2270 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2271 // wxASSERT (child != NULL);
2275 // Return first child in buffer if position is -1
2279 if (child
->GetRange().Contains(pos
))
2283 node
= node
->GetNext();
2288 /// Get the line at the given position
2289 wxRichTextLine
* wxRichTextParagraphLayoutBox
::GetLineAtPosition(long pos
, bool caretPosition
) const
2294 // First find the first paragraph whose starting position is within the range.
2295 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2298 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2299 if (obj
->GetRange().Contains(pos
))
2301 // child is a paragraph
2302 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2303 // wxASSERT (child != NULL);
2307 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2310 wxRichTextLine
* line
= node2
->GetData();
2312 wxRichTextRange range
= line
->GetAbsoluteRange();
2314 if (range
.Contains(pos
) ||
2316 // If the position is end-of-paragraph, then return the last line of
2317 // of the paragraph.
2318 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2321 node2
= node2
->GetNext();
2326 node
= node
->GetNext();
2329 int lineCount
= GetLineCount();
2331 return GetLineForVisibleLineNumber(lineCount
-1);
2336 /// Get the line at the given y pixel position, or the last line.
2337 wxRichTextLine
* wxRichTextParagraphLayoutBox
::GetLineAtYPosition(int y
) const
2339 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2342 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2343 // wxASSERT (child != NULL);
2347 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2350 wxRichTextLine
* line
= node2
->GetData();
2352 wxRect
rect(line
->GetRect());
2354 if (y
<= rect
.GetBottom())
2357 node2
= node2
->GetNext();
2361 node
= node
->GetNext();
2365 int lineCount
= GetLineCount();
2367 return GetLineForVisibleLineNumber(lineCount
-1);
2372 /// Get the number of visible lines
2373 int wxRichTextParagraphLayoutBox
::GetLineCount() const
2377 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2380 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2381 // wxASSERT (child != NULL);
2384 count
+= child
->GetLines().GetCount();
2386 node
= node
->GetNext();
2392 /// Get the paragraph for a given line
2393 wxRichTextParagraph
* wxRichTextParagraphLayoutBox
::GetParagraphForLine(wxRichTextLine
* line
) const
2395 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2398 /// Get the line size at the given position
2399 wxSize wxRichTextParagraphLayoutBox
::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2401 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2404 return line
->GetSize();
2407 return wxSize(0, 0);
2411 /// Convenience function to add a paragraph of text
2412 wxRichTextRange wxRichTextParagraphLayoutBox
::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2414 // Don't use the base style, just the default style, and the base style will
2415 // be combined at display time.
2416 // Divide into paragraph and character styles.
2418 wxRichTextAttr defaultCharStyle
;
2419 wxRichTextAttr defaultParaStyle
;
2421 // If the default style is a named paragraph style, don't apply any character formatting
2422 // to the initial text string.
2423 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2425 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2427 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2430 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2432 wxRichTextAttr
* pStyle
= paraStyle ? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2433 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2435 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2436 para
->GetAttributes().GetTextBoxAttr().Reset();
2442 return para
->GetRange();
2445 /// Adds multiple paragraphs, based on newlines.
2446 wxRichTextRange wxRichTextParagraphLayoutBox
::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2448 // Don't use the base style, just the default style, and the base style will
2449 // be combined at display time.
2450 // Divide into paragraph and character styles.
2452 wxRichTextAttr defaultCharStyle
;
2453 wxRichTextAttr defaultParaStyle
;
2455 // If the default style is a named paragraph style, don't apply any character formatting
2456 // to the initial text string.
2457 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2459 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2461 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2464 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2466 wxRichTextAttr
* pStyle
= paraStyle ? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2467 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2469 wxRichTextParagraph
* firstPara
= NULL
;
2470 wxRichTextParagraph
* lastPara
= NULL
;
2472 wxRichTextRange
range(-1, -1);
2475 size_t len
= text
.length();
2477 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2478 para
->GetAttributes().GetTextBoxAttr().Reset();
2487 wxChar ch
= text
[i
];
2488 if (ch
== wxT('\n') || ch
== wxT('\r'))
2492 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2493 plainText
->SetText(line
);
2495 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2496 para
->GetAttributes().GetTextBoxAttr().Reset();
2501 line
= wxEmptyString
;
2512 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2513 plainText
->SetText(line
);
2518 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2521 /// Convenience function to add an image
2522 wxRichTextRange wxRichTextParagraphLayoutBox
::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2524 // Don't use the base style, just the default style, and the base style will
2525 // be combined at display time.
2526 // Divide into paragraph and character styles.
2528 wxRichTextAttr defaultCharStyle
;
2529 wxRichTextAttr defaultParaStyle
;
2531 // If the default style is a named paragraph style, don't apply any character formatting
2532 // to the initial text string.
2533 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2535 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2537 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2540 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2542 wxRichTextAttr
* pStyle
= paraStyle ? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2543 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2545 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2546 para
->GetAttributes().GetTextBoxAttr().Reset();
2548 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2552 return para
->GetRange();
2556 /// Insert fragment into this box at the given position. If partialParagraph is true,
2557 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2560 bool wxRichTextParagraphLayoutBox
::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2562 // First, find the first paragraph whose starting position is within the range.
2563 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2566 wxRichTextAttr originalAttr
= para
->GetAttributes();
2568 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(para
);
2570 // Now split at this position, returning the object to insert the new
2571 // ones in front of.
2572 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2574 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2575 // text, for example, so let's optimize.
2577 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2579 // Add the first para to this para...
2580 wxRichTextObjectList
::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2584 // Iterate through the fragment paragraph inserting the content into this paragraph.
2585 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2586 wxASSERT (firstPara
!= NULL
);
2588 wxRichTextObjectList
::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2591 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2596 para
->AppendChild(newObj
);
2600 // Insert before nextObject
2601 para
->InsertChild(newObj
, nextObject
);
2604 objectNode
= objectNode
->GetNext();
2611 // Procedure for inserting a fragment consisting of a number of
2614 // 1. Remove and save the content that's after the insertion point, for adding
2615 // back once we've added the fragment.
2616 // 2. Add the content from the first fragment paragraph to the current
2618 // 3. Add remaining fragment paragraphs after the current paragraph.
2619 // 4. Add back the saved content from the first paragraph. If partialParagraph
2620 // is true, add it to the last paragraph added and not a new one.
2622 // 1. Remove and save objects after split point.
2623 wxList savedObjects
;
2625 para
->MoveToList(nextObject
, savedObjects
);
2627 // 2. Add the content from the 1st fragment paragraph.
2628 wxRichTextObjectList
::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2632 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2633 wxASSERT(firstPara
!= NULL
);
2635 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2636 para
->SetAttributes(firstPara
->GetAttributes());
2638 // Save empty paragraph attributes for appending later
2639 // These are character attributes deliberately set for a new paragraph. Without this,
2640 // we couldn't pass default attributes when appending a new paragraph.
2641 wxRichTextAttr emptyParagraphAttributes
;
2643 wxRichTextObjectList
::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2645 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2646 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2650 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2653 para
->AppendChild(newObj
);
2655 objectNode
= objectNode
->GetNext();
2658 // 3. Add remaining fragment paragraphs after the current paragraph.
2659 wxRichTextObjectList
::compatibility_iterator nextParagraphNode
= node
->GetNext();
2660 wxRichTextObject
* nextParagraph
= NULL
;
2661 if (nextParagraphNode
)
2662 nextParagraph
= nextParagraphNode
->GetData();
2664 wxRichTextObjectList
::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2665 wxRichTextParagraph
* finalPara
= para
;
2667 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2669 // If there was only one paragraph, we need to insert a new one.
2672 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2673 wxASSERT( para
!= NULL
);
2675 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2678 InsertChild(finalPara
, nextParagraph
);
2680 AppendChild(finalPara
);
2685 // If there was only one paragraph, or we have full paragraphs in our fragment,
2686 // we need to insert a new one.
2689 finalPara
= new wxRichTextParagraph
;
2692 InsertChild(finalPara
, nextParagraph
);
2694 AppendChild(finalPara
);
2697 // 4. Add back the remaining content.
2701 finalPara
->MoveFromList(savedObjects
);
2703 // Ensure there's at least one object
2704 if (finalPara
->GetChildCount() == 0)
2706 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2707 text
->SetAttributes(emptyParagraphAttributes
);
2709 finalPara
->AppendChild(text
);
2713 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2714 finalPara
->SetAttributes(firstPara
->GetAttributes());
2715 else if (finalPara
&& finalPara
!= para
)
2716 finalPara
->SetAttributes(originalAttr
);
2724 wxRichTextObjectList
::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2727 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2728 wxASSERT( para
!= NULL
);
2730 AppendChild(para
->Clone());
2739 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2740 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2741 bool wxRichTextParagraphLayoutBox
::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2743 wxRichTextObjectList
::compatibility_iterator i
= GetChildren().GetFirst();
2746 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2747 wxASSERT( para
!= NULL
);
2749 if (!para
->GetRange().IsOutside(range
))
2751 fragment
.AppendChild(para
->Clone());
2756 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2757 if (!fragment
.IsEmpty())
2759 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2760 wxASSERT( firstPara
!= NULL
);
2762 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2763 wxASSERT( lastPara
!= NULL
);
2765 if (!firstPara
|| !lastPara
)
2768 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2770 long firstPos
= firstPara
->GetRange().GetStart();
2772 // Adjust for renumbering from zero
2773 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2776 fragment
.CalculateRange(0, end
);
2778 // Chop off the start of the paragraph
2779 if (topTailRange
.GetStart() > 0)
2781 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2782 firstPara
->DeleteRange(r
);
2784 // Make sure the numbering is correct
2785 fragment
.CalculateRange(0, end
);
2787 // Now, we've deleted some positions, so adjust the range
2789 topTailRange
.SetStart(range
.GetLength());
2790 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2794 topTailRange
.SetStart(range
.GetLength());
2795 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2798 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2800 lastPara
->DeleteRange(topTailRange
);
2802 // Make sure the numbering is correct
2804 fragment
.CalculateRange(0, end
);
2806 // We only have part of a paragraph at the end
2807 fragment
.SetPartialParagraph(true);
2811 // We have a partial paragraph (don't save last new paragraph marker)
2812 // or complete paragraph
2813 fragment
.SetPartialParagraph(isFragment
);
2820 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2821 /// starting from zero at the start of the buffer.
2822 long wxRichTextParagraphLayoutBox
::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2829 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2832 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2833 // wxASSERT( child != NULL );
2837 if (child
->GetRange().Contains(pos
))
2839 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2842 wxRichTextLine
* line
= node2
->GetData();
2843 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2845 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2847 // If the caret is displayed at the end of the previous wrapped line,
2848 // we want to return the line it's _displayed_ at (not the actual line
2849 // containing the position).
2850 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2851 return lineCount
- 1;
2858 node2
= node2
->GetNext();
2860 // If we didn't find it in the lines, it must be
2861 // the last position of the paragraph. So return the last line.
2865 lineCount
+= child
->GetLines().GetCount();
2868 node
= node
->GetNext();
2875 /// Given a line number, get the corresponding wxRichTextLine object.
2876 wxRichTextLine
* wxRichTextParagraphLayoutBox
::GetLineForVisibleLineNumber(long lineNumber
) const
2880 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2883 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2884 // wxASSERT(child != NULL);
2888 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2890 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
2893 wxRichTextLine
* line
= node2
->GetData();
2895 if (lineCount
== lineNumber
)
2900 node2
= node2
->GetNext();
2904 lineCount
+= child
->GetLines().GetCount();
2907 node
= node
->GetNext();
2914 /// Delete range from layout.
2915 bool wxRichTextParagraphLayoutBox
::DeleteRange(const wxRichTextRange
& range
)
2917 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
2919 wxRichTextParagraph
* firstPara
= NULL
;
2922 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2923 // wxASSERT (obj != NULL);
2925 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
2929 // Delete the range in each paragraph
2931 if (!obj
->GetRange().IsOutside(range
))
2933 // Deletes the content of this object within the given range
2934 obj
->DeleteRange(range
);
2936 wxRichTextRange thisRange
= obj
->GetRange();
2937 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2939 // If the whole paragraph is within the range to delete,
2940 // delete the whole thing.
2941 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2943 // Delete the whole object
2944 RemoveChild(obj
, true);
2947 else if (!firstPara
)
2950 // If the range includes the paragraph end, we need to join this
2951 // and the next paragraph.
2952 if (range
.GetEnd() <= thisRange
.GetEnd())
2954 // We need to move the objects from the next paragraph
2955 // to this paragraph
2957 wxRichTextParagraph
* nextParagraph
= NULL
;
2958 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2959 nextParagraph
= obj
;
2962 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2964 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2967 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2969 wxRichTextAttr nextParaAttr
;
2970 if (applyFinalParagraphStyle
)
2972 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2973 // not the next one.
2974 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2975 nextParaAttr
= thisAttr
;
2977 nextParaAttr
= nextParagraph
->GetAttributes();
2980 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2982 // Move the objects to the previous para
2983 wxRichTextObjectList
::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2987 wxRichTextObject
* obj1
= node1
->GetData();
2989 firstPara
->AppendChild(obj1
);
2991 wxRichTextObjectList
::compatibility_iterator next1
= node1
->GetNext();
2992 nextParagraph
->GetChildren().Erase(node1
);
2997 // Delete the paragraph
2998 RemoveChild(nextParagraph
, true);
3001 // Avoid empty paragraphs
3002 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3004 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3005 firstPara
->AppendChild(text
);
3008 if (applyFinalParagraphStyle
)
3009 firstPara
->SetAttributes(nextParaAttr
);
3022 /// Get any text in this object for the given range
3023 wxString wxRichTextParagraphLayoutBox
::GetTextForRange(const wxRichTextRange
& range
) const
3027 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3030 wxRichTextObject
* child
= node
->GetData();
3031 if (!child
->GetRange().IsOutside(range
))
3033 wxRichTextRange childRange
= range
;
3034 childRange
.LimitTo(child
->GetRange());
3036 wxString childText
= child
->GetTextForRange(childRange
);
3040 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3045 node
= node
->GetNext();
3051 /// Get all the text
3052 wxString wxRichTextParagraphLayoutBox
::GetText() const
3054 return GetTextForRange(GetOwnRange());
3057 /// Get the paragraph by number
3058 wxRichTextParagraph
* wxRichTextParagraphLayoutBox
::GetParagraphAtLine(long paragraphNumber
) const
3060 if ((size_t) paragraphNumber
>= GetChildCount())
3063 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3066 /// Get the length of the paragraph
3067 int wxRichTextParagraphLayoutBox
::GetParagraphLength(long paragraphNumber
) const
3069 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3071 return para
->GetRange().GetLength() - 1; // don't include newline
3076 /// Get the text of the paragraph
3077 wxString wxRichTextParagraphLayoutBox
::GetParagraphText(long paragraphNumber
) const
3079 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3081 return para
->GetTextForRange(para
->GetRange());
3083 return wxEmptyString
;
3086 /// Convert zero-based line column and paragraph number to a position.
3087 long wxRichTextParagraphLayoutBox
::XYToPosition(long x
, long y
) const
3089 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3092 return para
->GetRange().GetStart() + x
;
3098 /// Convert zero-based position to line column and paragraph number
3099 bool wxRichTextParagraphLayoutBox
::PositionToXY(long pos
, long* x
, long* y
) const
3101 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3105 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3108 wxRichTextObject
* child
= node
->GetData();
3112 node
= node
->GetNext();
3116 *x
= pos
- para
->GetRange().GetStart();
3124 /// Get the leaf object in a paragraph at this position.
3125 /// Given a line number, get the corresponding wxRichTextLine object.
3126 wxRichTextObject
* wxRichTextParagraphLayoutBox
::GetLeafObjectAtPosition(long position
) const
3128 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3131 wxRichTextObjectList
::compatibility_iterator node
= para
->GetChildren().GetFirst();
3135 wxRichTextObject
* child
= node
->GetData();
3136 if (child
->GetRange().Contains(position
))
3139 node
= node
->GetNext();
3141 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3142 return para
->GetChildren().GetLast()->GetData();
3147 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3148 bool wxRichTextParagraphLayoutBox
::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3150 bool characterStyle
= false;
3151 bool paragraphStyle
= false;
3153 if (style
.IsCharacterStyle())
3154 characterStyle
= true;
3155 if (style
.IsParagraphStyle())
3156 paragraphStyle
= true;
3158 wxRichTextBuffer
* buffer
= GetBuffer();
3160 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3161 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3162 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3163 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3164 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3165 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3167 // Apply paragraph style first, if any
3168 wxRichTextAttr
wholeStyle(style
);
3170 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3172 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3174 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3177 // Limit the attributes to be set to the content to only character attributes.
3178 wxRichTextAttr
characterAttributes(wholeStyle
);
3179 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3181 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3183 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3185 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3188 // If we are associated with a control, make undoable; otherwise, apply immediately
3191 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3193 wxRichTextAction
* action
= NULL
;
3195 if (haveControl
&& withUndo
)
3197 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3198 action
->SetRange(range
);
3199 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3202 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3205 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3206 // wxASSERT (para != NULL);
3208 if (para
&& para
->GetChildCount() > 0)
3210 // Stop searching if we're beyond the range of interest
3211 if (para
->GetRange().GetStart() > range
.GetEnd())
3214 if (!para
->GetRange().IsOutside(range
))
3216 // We'll be using a copy of the paragraph to make style changes,
3217 // not updating the buffer directly.
3218 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3220 if (haveControl
&& withUndo
)
3222 newPara
= new wxRichTextParagraph(*para
);
3223 action
->GetNewParagraphs().AppendChild(newPara
);
3225 // Also store the old ones for Undo
3226 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3231 // If we're specifying paragraphs only, then we really mean character formatting
3232 // to be included in the paragraph style
3233 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3237 // Removes the given style from the paragraph
3238 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3240 else if (resetExistingStyle
)
3241 newPara
->GetAttributes() = wholeStyle
;
3246 // Only apply attributes that will make a difference to the combined
3247 // style as seen on the display
3248 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3249 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3252 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3256 // When applying paragraph styles dynamically, don't change the text objects' attributes
3257 // since they will computed as needed. Only apply the character styling if it's _only_
3258 // character styling. This policy is subject to change and might be put under user control.
3260 // Hm. we might well be applying a mix of paragraph and character styles, in which
3261 // case we _do_ want to apply character styles regardless of what para styles are set.
3262 // But if we're applying a paragraph style, which has some character attributes, but
3263 // we only want the paragraphs to hold this character style, then we _don't_ want to
3264 // apply the character style. So we need to be able to choose.
3266 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3268 wxRichTextRange
childRange(range
);
3269 childRange
.LimitTo(newPara
->GetRange());
3271 // Find the starting position and if necessary split it so
3272 // we can start applying a different style.
3273 // TODO: check that the style actually changes or is different
3274 // from style outside of range
3275 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3276 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3278 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3279 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3281 firstObject
= newPara
->SplitAt(range
.GetStart());
3283 // Increment by 1 because we're apply the style one _after_ the split point
3284 long splitPoint
= childRange
.GetEnd();
3285 if (splitPoint
!= newPara
->GetRange().GetEnd())
3289 if (splitPoint
== newPara
->GetRange().GetEnd())
3290 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3292 // lastObject is set as a side-effect of splitting. It's
3293 // returned as the object before the new object.
3294 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3296 wxASSERT(firstObject
!= NULL
);
3297 wxASSERT(lastObject
!= NULL
);
3299 if (!firstObject
|| !lastObject
)
3302 wxRichTextObjectList
::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3303 wxRichTextObjectList
::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3305 wxASSERT(firstNode
);
3308 wxRichTextObjectList
::compatibility_iterator node2
= firstNode
;
3312 wxRichTextObject
* child
= node2
->GetData();
3316 // Removes the given style from the paragraph
3317 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3319 else if (resetExistingStyle
)
3321 // Preserve the URL as it's not really a formatting style but a property of the object
3323 if (child
->GetAttributes().HasURL() && !characterAttributes
.HasURL())
3324 url
= child
->GetAttributes().GetURL();
3326 child
->GetAttributes() = characterAttributes
;
3329 child
->GetAttributes().SetURL(url
);
3335 // Only apply attributes that will make a difference to the combined
3336 // style as seen on the display
3337 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3338 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3341 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3344 if (node2
== lastNode
)
3347 node2
= node2
->GetNext();
3353 node
= node
->GetNext();
3356 // Do action, or delay it until end of batch.
3357 if (haveControl
&& withUndo
)
3358 buffer
->SubmitAction(action
);
3363 // Just change the attributes for this single object.
3364 void wxRichTextParagraphLayoutBox
::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3366 wxRichTextBuffer
* buffer
= GetBuffer();
3367 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3368 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3369 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3371 wxRichTextAction
*action
= NULL
;
3372 wxRichTextAttr newAttr
= obj
->GetAttributes();
3373 if (resetExistingStyle
)
3376 newAttr
.Apply(textAttr
);
3378 if (haveControl
&& withUndo
)
3380 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3381 action
->SetRange(obj
->GetRange().FromInternal());
3382 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3383 action
->MakeObject(obj
);
3385 action
->GetAttributes() = newAttr
;
3388 obj
->GetAttributes() = newAttr
;
3390 if (haveControl
&& withUndo
)
3391 buffer
->SubmitAction(action
);
3394 /// Get the text attributes for this position.
3395 bool wxRichTextParagraphLayoutBox
::GetStyle(long position
, wxRichTextAttr
& style
)
3397 return DoGetStyle(position
, style
, true);
3400 bool wxRichTextParagraphLayoutBox
::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3402 return DoGetStyle(position
, style
, false);
3405 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3406 /// context attributes.
3407 bool wxRichTextParagraphLayoutBox
::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3409 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3411 if (style
.IsParagraphStyle())
3413 obj
= GetParagraphAtPosition(position
);
3418 // Start with the base style
3419 style
= GetAttributes();
3420 style
.GetTextBoxAttr().Reset();
3422 // Apply the paragraph style
3423 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3426 style
= obj
->GetAttributes();
3433 obj
= GetLeafObjectAtPosition(position
);
3438 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3439 style
= para ? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3442 style
= obj
->GetAttributes();
3450 static bool wxHasStyle(long flags
, long style
)
3452 return (flags
& style
) != 0;
3455 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3457 bool wxRichTextParagraphLayoutBox
::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3459 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3464 /// Get the combined style for a range - if any attribute is different within the range,
3465 /// that attribute is not present within the flags.
3466 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3468 bool wxRichTextParagraphLayoutBox
::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3470 style
= wxRichTextAttr();
3472 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3473 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3475 wxRichTextObjectList
::compatibility_iterator node
= GetChildren().GetFirst();
3478 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3479 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3481 if (para
->GetChildren().GetCount() == 0)
3483 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3485 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3489 wxRichTextRange
paraRange(para
->GetRange());
3490 paraRange
.LimitTo(range
);
3492 // First collect paragraph attributes only
3493 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3494 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3495 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3497 wxRichTextObjectList
::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3501 wxRichTextObject
* child
= childNode
->GetData();
3502 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3504 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3506 // Now collect character attributes only
3507 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3509 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3512 childNode
= childNode
->GetNext();
3516 node
= node
->GetNext();
3521 /// Set default style
3522 bool wxRichTextParagraphLayoutBox
::SetDefaultStyle(const wxRichTextAttr
& style
)
3524 m_defaultAttributes
= style
;
3528 /// Test if this whole range has character attributes of the specified kind. If any
3529 /// of the attributes are different within the range, the test fails. You
3530 /// can use this to implement, for example, bold button updating. style must have
3531 /// flags indicating which attributes are of interest.
3532 bool wxRichTextParagraphLayoutBox
::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3535 int matchingCount
= 0;
3537 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3540 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3541 // wxASSERT (para != NULL);
3545 // Stop searching if we're beyond the range of interest
3546 if (para
->GetRange().GetStart() > range
.GetEnd())
3547 return foundCount
== matchingCount
&& foundCount
!= 0;
3549 if (!para
->GetRange().IsOutside(range
))
3551 wxRichTextObjectList
::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3555 wxRichTextObject
* child
= node2
->GetData();
3556 // Allow for empty string if no buffer
3557 wxRichTextRange childRange
= child
->GetRange();
3558 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3559 childRange
.SetEnd(childRange
.GetEnd()+1);
3561 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3564 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3566 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3570 node2
= node2
->GetNext();
3575 node
= node
->GetNext();
3578 return foundCount
== matchingCount
&& foundCount
!= 0;
3581 /// Test if this whole range has paragraph attributes of the specified kind. If any
3582 /// of the attributes are different within the range, the test fails. You
3583 /// can use this to implement, for example, centering button updating. style must have
3584 /// flags indicating which attributes are of interest.
3585 bool wxRichTextParagraphLayoutBox
::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3588 int matchingCount
= 0;
3590 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3593 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3594 // wxASSERT (para != NULL);
3598 // Stop searching if we're beyond the range of interest
3599 if (para
->GetRange().GetStart() > range
.GetEnd())
3600 return foundCount
== matchingCount
&& foundCount
!= 0;
3602 if (!para
->GetRange().IsOutside(range
))
3604 wxRichTextAttr textAttr
= GetAttributes();
3605 // Apply the paragraph style
3606 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3609 if (textAttr
.EqPartial(style
, false /* strong test */))
3614 node
= node
->GetNext();
3616 return foundCount
== matchingCount
&& foundCount
!= 0;
3619 void wxRichTextParagraphLayoutBox
::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3621 wxRichTextBuffer
* buffer
= GetBuffer();
3622 if (buffer
&& buffer
->GetRichTextCtrl())
3623 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3626 /// Set character or paragraph properties
3627 bool wxRichTextParagraphLayoutBox
::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3629 wxRichTextBuffer
* buffer
= GetBuffer();
3631 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3632 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3633 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3634 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3635 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3637 // If we are associated with a control, make undoable; otherwise, apply immediately
3640 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3642 wxRichTextAction
* action
= NULL
;
3644 if (haveControl
&& withUndo
)
3646 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3647 action
->SetRange(range
);
3648 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3651 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3654 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3655 // wxASSERT (para != NULL);
3657 if (para
&& para
->GetChildCount() > 0)
3659 // Stop searching if we're beyond the range of interest
3660 if (para
->GetRange().GetStart() > range
.GetEnd())
3663 if (!para
->GetRange().IsOutside(range
))
3665 // We'll be using a copy of the paragraph to make style changes,
3666 // not updating the buffer directly.
3667 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3669 if (haveControl
&& withUndo
)
3671 newPara
= new wxRichTextParagraph(*para
);
3672 action
->GetNewParagraphs().AppendChild(newPara
);
3674 // Also store the old ones for Undo
3675 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3682 if (removeProperties
)
3684 // Removes the given style from the paragraph
3686 newPara
->GetProperties().RemoveProperties(properties
);
3688 else if (resetExistingProperties
)
3689 newPara
->GetProperties() = properties
;
3691 newPara
->GetProperties().MergeProperties(properties
);
3694 // When applying paragraph styles dynamically, don't change the text objects' attributes
3695 // since they will computed as needed. Only apply the character styling if it's _only_
3696 // character styling. This policy is subject to change and might be put under user control.
3698 // Hm. we might well be applying a mix of paragraph and character styles, in which
3699 // case we _do_ want to apply character styles regardless of what para styles are set.
3700 // But if we're applying a paragraph style, which has some character attributes, but
3701 // we only want the paragraphs to hold this character style, then we _don't_ want to
3702 // apply the character style. So we need to be able to choose.
3704 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3706 wxRichTextRange
childRange(range
);
3707 childRange
.LimitTo(newPara
->GetRange());
3709 // Find the starting position and if necessary split it so
3710 // we can start applying different properties.
3711 // TODO: check that the properties actually change or are different
3712 // from properties outside of range
3713 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3714 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3716 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3717 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3719 firstObject
= newPara
->SplitAt(range
.GetStart());
3721 // Increment by 1 because we're apply the style one _after_ the split point
3722 long splitPoint
= childRange
.GetEnd();
3723 if (splitPoint
!= newPara
->GetRange().GetEnd())
3727 if (splitPoint
== newPara
->GetRange().GetEnd())
3728 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3730 // lastObject is set as a side-effect of splitting. It's
3731 // returned as the object before the new object.
3732 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3734 wxASSERT(firstObject
!= NULL
);
3735 wxASSERT(lastObject
!= NULL
);
3737 if (!firstObject
|| !lastObject
)
3740 wxRichTextObjectList
::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3741 wxRichTextObjectList
::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3743 wxASSERT(firstNode
);
3746 wxRichTextObjectList
::compatibility_iterator node2
= firstNode
;
3750 wxRichTextObject
* child
= node2
->GetData();
3752 if (removeProperties
)
3754 // Removes the given properties from the paragraph
3755 child
->GetProperties().RemoveProperties(properties
);
3757 else if (resetExistingProperties
)
3758 child
->GetProperties() = properties
;
3761 child
->GetProperties().MergeProperties(properties
);
3764 if (node2
== lastNode
)
3767 node2
= node2
->GetNext();
3773 node
= node
->GetNext();
3776 // Do action, or delay it until end of batch.
3777 if (haveControl
&& withUndo
)
3778 buffer
->SubmitAction(action
);
3783 void wxRichTextParagraphLayoutBox
::Reset()
3787 wxRichTextBuffer
* buffer
= GetBuffer();
3788 if (buffer
&& buffer
->GetRichTextCtrl())
3790 wxRichTextEvent
event(wxEVT_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3791 event
.SetEventObject(buffer
->GetRichTextCtrl());
3792 event
.SetContainer(this);
3794 buffer
->SendEvent(event
, true);
3797 AddParagraph(wxEmptyString
);
3799 PrepareContent(*this);
3801 InvalidateHierarchy(wxRICHTEXT_ALL
);
3804 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3805 void wxRichTextParagraphLayoutBox
::Invalidate(const wxRichTextRange
& invalidRange
)
3807 wxRichTextCompositeObject
::Invalidate(invalidRange
);
3809 DoInvalidate(invalidRange
);
3812 // Do the (in)validation for this object only
3813 void wxRichTextParagraphLayoutBox
::DoInvalidate(const wxRichTextRange
& invalidRange
)
3815 if (invalidRange
== wxRICHTEXT_ALL
)
3817 m_invalidRange
= wxRICHTEXT_ALL
;
3819 // Already invalidating everything
3820 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3825 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3826 m_invalidRange
.SetStart(invalidRange
.GetStart());
3827 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3828 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3832 // Do the (in)validation both up and down the hierarchy
3833 void wxRichTextParagraphLayoutBox
::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3835 Invalidate(invalidRange
);
3837 if (invalidRange
!= wxRICHTEXT_NONE
)
3839 // Now go up the hierarchy
3840 wxRichTextObject
* thisObj
= this;
3841 wxRichTextObject
* p
= GetParent();
3844 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3846 l
->DoInvalidate(thisObj
->GetRange());
3854 /// Get invalid range, rounding to entire paragraphs if argument is true.
3855 wxRichTextRange wxRichTextParagraphLayoutBox
::GetInvalidRange(bool wholeParagraphs
) const
3857 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3858 return m_invalidRange
;
3860 wxRichTextRange range
= m_invalidRange
;
3862 if (wholeParagraphs
)
3864 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3866 range
.SetStart(para1
->GetRange().GetStart());
3868 // FIXME: be more intelligent about this. Check if we have floating objects
3869 // before the end of the range. But it's not clear how we can in general
3870 // tell where it's safe to stop laying out.
3871 // Anyway, this code is central to efficiency when laying in floating mode.
3872 if (!wxRichTextBuffer
::GetFloatingLayoutMode())
3874 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3876 range
.SetEnd(para2
->GetRange().GetEnd());
3879 // Floating layout means that all children should be laid out,
3880 // because we can't tell how the whole buffer will be affected.
3881 range
.SetEnd(GetOwnRange().GetEnd());
3886 /// Apply the style sheet to the buffer, for example if the styles have changed.
3887 bool wxRichTextParagraphLayoutBox
::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3889 wxASSERT(styleSheet
!= NULL
);
3895 wxRichTextAttr
attr(GetBasicStyle());
3896 if (GetBasicStyle().HasParagraphStyleName())
3898 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3901 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3902 SetBasicStyle(attr
);
3907 if (GetBasicStyle().HasCharacterStyleName())
3909 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3912 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3913 SetBasicStyle(attr
);
3918 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
3921 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3922 // wxASSERT (para != NULL);
3926 // Combine paragraph and list styles. If there is a list style in the original attributes,
3927 // the current indentation overrides anything else and is used to find the item indentation.
3928 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3929 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3930 // exception as above).
3931 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3932 // So when changing a list style interactively, could retrieve level based on current style, then
3933 // set appropriate indent and apply new style.
3937 if (para
->GetAttributes().HasOutlineLevel())
3938 outline
= para
->GetAttributes().GetOutlineLevel();
3939 if (para
->GetAttributes().HasBulletNumber())
3940 num
= para
->GetAttributes().GetBulletNumber();
3942 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3944 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3946 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3947 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3948 if (paraDef
&& !listDef
)
3950 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3953 else if (listDef
&& !paraDef
)
3955 // Set overall style defined for the list style definition
3956 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3958 // Apply the style for this level
3959 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3962 else if (listDef
&& paraDef
)
3964 // Combines overall list style, style for level, and paragraph style
3965 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3969 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3971 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3973 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3975 // Overall list definition style
3976 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3978 // Style for this level
3979 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3983 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3985 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3988 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3994 para
->GetAttributes().SetOutlineLevel(outline
);
3996 para
->GetAttributes().SetBulletNumber(num
);
3999 node
= node
->GetNext();
4001 return foundCount
!= 0;
4005 bool wxRichTextParagraphLayoutBox
::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4007 wxRichTextBuffer
* buffer
= GetBuffer();
4008 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4010 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4011 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4012 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4013 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4015 // Current number, if numbering
4018 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4020 // If we are associated with a control, make undoable; otherwise, apply immediately
4023 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4025 wxRichTextAction
* action
= NULL
;
4027 if (haveControl
&& withUndo
)
4029 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4030 action
->SetRange(range
);
4031 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4034 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
4037 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4038 // wxASSERT (para != NULL);
4040 if (para
&& para
->GetChildCount() > 0)
4042 // Stop searching if we're beyond the range of interest
4043 if (para
->GetRange().GetStart() > range
.GetEnd())
4046 if (!para
->GetRange().IsOutside(range
))
4048 // We'll be using a copy of the paragraph to make style changes,
4049 // not updating the buffer directly.
4050 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4052 if (haveControl
&& withUndo
)
4054 newPara
= new wxRichTextParagraph(*para
);
4055 action
->GetNewParagraphs().AppendChild(newPara
);
4057 // Also store the old ones for Undo
4058 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4065 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4066 int thisLevel
= specifyLevel ? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4068 // How is numbering going to work?
4069 // If we are renumbering, or numbering for the first time, we need to keep
4070 // track of the number for each level. But we might be simply applying a different
4072 // In Word, applying a style to several paragraphs, even if at different levels,
4073 // reverts the level back to the same one. So we could do the same here.
4074 // Renumbering will need to be done when we promote/demote a paragraph.
4076 // Apply the overall list style, and item style for this level
4077 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4078 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4080 // Now we need to do numbering
4081 // Preserve the existing list item continuation bullet style, if any
4082 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4083 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4088 newPara
->GetAttributes().SetBulletNumber(n
);
4094 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4096 // if def is NULL, remove list style, applying any associated paragraph style
4097 // to restore the attributes
4099 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4100 newPara
->GetAttributes().SetLeftIndent(0, 0);
4101 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4102 newPara
->GetAttributes().SetBulletStyle(0);
4104 // Eliminate the main list-related attributes
4105 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
);
4107 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4109 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4112 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4119 node
= node
->GetNext();
4122 // Do action, or delay it until end of batch.
4123 if (haveControl
&& withUndo
)
4124 buffer
->SubmitAction(action
);
4129 bool wxRichTextParagraphLayoutBox
::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4131 wxRichTextBuffer
* buffer
= GetBuffer();
4132 if (buffer
&& buffer
->GetStyleSheet())
4134 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4136 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4141 /// Clear list for given range
4142 bool wxRichTextParagraphLayoutBox
::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4144 return SetListStyle(range
, NULL
, flags
);
4147 /// Number/renumber any list elements in the given range
4148 bool wxRichTextParagraphLayoutBox
::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4150 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4153 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4154 bool wxRichTextParagraphLayoutBox
::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4155 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4157 wxRichTextBuffer
* buffer
= GetBuffer();
4158 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4160 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4161 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4163 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4166 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4168 // Max number of levels
4169 const int maxLevels
= 10;
4171 // The level we're looking at now
4172 int currentLevel
= -1;
4174 // The item number for each level
4175 int levels
[maxLevels
];
4178 // Reset all numbering
4179 for (i
= 0; i
< maxLevels
; i
++)
4181 if (startFrom
!= -1)
4182 levels
[i
] = startFrom
-1;
4183 else if (renumber
) // start again
4186 levels
[i
] = -1; // start from the number we found, if any
4190 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4193 // If we are associated with a control, make undoable; otherwise, apply immediately
4196 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4198 wxRichTextAction
* action
= NULL
;
4200 if (haveControl
&& withUndo
)
4202 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4203 action
->SetRange(range
);
4204 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4207 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
4210 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4211 // wxASSERT (para != NULL);
4213 if (para
&& para
->GetChildCount() > 0)
4215 // Stop searching if we're beyond the range of interest
4216 if (para
->GetRange().GetStart() > range
.GetEnd())
4219 if (!para
->GetRange().IsOutside(range
))
4221 // We'll be using a copy of the paragraph to make style changes,
4222 // not updating the buffer directly.
4223 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4225 if (haveControl
&& withUndo
)
4227 newPara
= new wxRichTextParagraph(*para
);
4228 action
->GetNewParagraphs().AppendChild(newPara
);
4230 // Also store the old ones for Undo
4231 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4236 wxRichTextListStyleDefinition
* defToUse
= def
;
4239 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4240 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4245 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4246 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4248 // If we've specified a level to apply to all, change the level.
4249 if (specifiedLevel
!= -1)
4250 thisLevel
= specifiedLevel
;
4252 // Do promotion if specified
4253 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4255 thisLevel
= thisLevel
- promoteBy
;
4262 // Apply the overall list style, and item style for this level
4263 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4264 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4266 // Preserve the existing list item continuation bullet style, if any
4267 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4268 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4270 // OK, we've (re)applied the style, now let's get the numbering right.
4272 if (currentLevel
== -1)
4273 currentLevel
= thisLevel
;
4275 // Same level as before, do nothing except increment level's number afterwards
4276 if (currentLevel
== thisLevel
)
4279 // A deeper level: start renumbering all levels after current level
4280 else if (thisLevel
> currentLevel
)
4282 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4286 currentLevel
= thisLevel
;
4288 else if (thisLevel
< currentLevel
)
4290 currentLevel
= thisLevel
;
4293 // Use the current numbering if -1 and we have a bullet number already
4294 if (levels
[currentLevel
] == -1)
4296 if (newPara
->GetAttributes().HasBulletNumber())
4297 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4299 levels
[currentLevel
] = 1;
4303 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4304 levels
[currentLevel
] ++;
4307 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4309 // Create the bullet text if an outline list
4310 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4313 for (i
= 0; i
<= currentLevel
; i
++)
4315 if (!text
.IsEmpty())
4317 text
+= wxString
::Format(wxT("%d"), levels
[i
]);
4319 newPara
->GetAttributes().SetBulletText(text
);
4325 node
= node
->GetNext();
4328 // Do action, or delay it until end of batch.
4329 if (haveControl
&& withUndo
)
4330 buffer
->SubmitAction(action
);
4335 bool wxRichTextParagraphLayoutBox
::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4337 wxRichTextBuffer
* buffer
= GetBuffer();
4338 if (buffer
->GetStyleSheet())
4340 wxRichTextListStyleDefinition
* def
= NULL
;
4341 if (!defName
.IsEmpty())
4342 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4343 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4348 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4349 bool wxRichTextParagraphLayoutBox
::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4352 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4353 // to NumberList with a flag indicating promotion is required within one of the ranges.
4354 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4355 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4356 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4357 // list position will start from 1.
4358 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4359 // We can end the renumbering at this point.
4361 // For now, only renumber within the promotion range.
4363 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4366 bool wxRichTextParagraphLayoutBox
::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4368 wxRichTextBuffer
* buffer
= GetBuffer();
4369 if (buffer
->GetStyleSheet())
4371 wxRichTextListStyleDefinition
* def
= NULL
;
4372 if (!defName
.IsEmpty())
4373 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4374 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4379 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4380 /// position of the paragraph that it had to start looking from.
4381 bool wxRichTextParagraphLayoutBox
::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4383 // TODO: add GetNextChild/GetPreviousChild to composite
4384 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4385 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4387 wxRichTextObjectList
::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4390 node
= node
->GetPrevious();
4392 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4394 previousParagraph
= NULL
;
4397 previousParagraph
= NULL
;
4400 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4403 wxRichTextBuffer
* buffer
= GetBuffer();
4404 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4405 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4407 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4410 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4411 // int thisLevel = def->FindLevelForIndent(thisIndent);
4413 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4415 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4416 if (previousParagraph
->GetAttributes().HasBulletName())
4417 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4418 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4419 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4421 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4422 attr
.SetBulletNumber(nextNumber
);
4426 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4427 if (!text
.IsEmpty())
4429 int pos
= text
.Find(wxT('.'), true);
4430 if (pos
!= wxNOT_FOUND
)
4432 text
= text
.Mid(0, text
.Length() - pos
- 1);
4435 text
= wxEmptyString
;
4436 if (!text
.IsEmpty())
4438 text
+= wxString
::Format(wxT("%d"), nextNumber
);
4439 attr
.SetBulletText(text
);
4453 * wxRichTextParagraph
4454 * This object represents a single paragraph (or in a straight text editor, a line).
4457 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4459 wxArrayInt wxRichTextParagraph
::sm_defaultTabs
;
4461 wxRichTextParagraph
::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4462 wxRichTextCompositeObject(parent
)
4465 SetAttributes(*style
);
4468 wxRichTextParagraph
::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4469 wxRichTextCompositeObject(parent
)
4472 SetAttributes(*paraStyle
);
4474 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4477 wxRichTextParagraph
::~wxRichTextParagraph()
4483 bool wxRichTextParagraph
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4488 // Currently we don't merge these attributes with the parent, but we
4489 // should consider whether we should (e.g. if we set a border colour
4490 // for all paragraphs). But generally box attributes are likely to be
4491 // different for different objects.
4492 wxRect paraRect
= GetRect();
4493 wxRichTextAttr attr
= GetCombinedAttributes();
4494 context
.ApplyVirtualAttributes(attr
, this);
4496 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4498 // Draw the bullet, if any
4499 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4501 if (attr
.GetLeftSubIndent() != 0)
4503 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4504 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4506 wxRichTextAttr
bulletAttr(attr
);
4508 // Combine with the font of the first piece of content, if one is specified
4509 if (GetChildren().GetCount() > 0)
4511 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4512 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4514 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4518 // Get line height from first line, if any
4519 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ?
(wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4522 int lineHeight
wxDUMMY_INITIALIZE(0);
4525 lineHeight
= line
->GetSize().y
;
4526 linePos
= line
->GetPosition() + GetPosition();
4531 if (bulletAttr
.HasFont() && GetBuffer())
4532 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4534 font
= (*wxNORMAL_FONT
);
4536 wxCheckSetFont(dc
, font
);
4538 lineHeight
= dc
.GetCharHeight();
4539 linePos
= GetPosition();
4540 linePos
.y
+= spaceBeforePara
;
4543 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4545 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4547 if (wxRichTextBuffer
::GetRenderer())
4548 wxRichTextBuffer
::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4550 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4552 if (wxRichTextBuffer
::GetRenderer())
4553 wxRichTextBuffer
::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4557 wxString bulletText
= GetBulletText();
4559 if (!bulletText
.empty() && wxRichTextBuffer
::GetRenderer())
4560 wxRichTextBuffer
::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4565 // Draw the range for each line, one object at a time.
4567 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
4570 wxRichTextLine
* line
= node
->GetData();
4571 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4573 // Lines are specified relative to the paragraph
4575 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4577 // Don't draw if off the screen
4578 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4580 wxPoint objectPosition
= linePosition
;
4581 int maxDescent
= line
->GetDescent();
4583 // Loop through objects until we get to the one within range
4584 wxRichTextObjectList
::compatibility_iterator node2
= m_children
.GetFirst();
4589 wxRichTextObject
* child
= node2
->GetData();
4591 if ((!child
->IsFloating() || !wxRichTextBuffer
::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4593 // Draw this part of the line at the correct position
4594 wxRichTextRange
objectRange(child
->GetRange());
4595 objectRange
.LimitTo(lineRange
);
4598 if (child
->IsTopLevel())
4600 objectSize
= child
->GetCachedSize();
4601 objectRange
= child
->GetOwnRange();
4605 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4606 if (i
< (int) line
->GetObjectSizes().GetCount())
4608 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4614 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4618 // Use the child object's width, but the whole line's height
4619 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4620 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4622 objectPosition
.x
+= objectSize
.x
;
4625 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4626 // Can break out of inner loop now since we've passed this line's range
4629 node2
= node2
->GetNext();
4633 node
= node
->GetNext();
4639 // Get the range width using partial extents calculated for the whole paragraph.
4640 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4642 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4644 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4647 int leftMostPos
= 0;
4648 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4649 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4651 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4653 int w
= rightMostPos
- leftMostPos
;
4658 /// Lay the item out
4659 bool wxRichTextParagraph
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4661 // Deal with floating objects firstly before the normal layout
4662 wxRichTextBuffer
* buffer
= GetBuffer();
4665 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4667 if (wxRichTextBuffer
::GetFloatingLayoutMode())
4669 wxASSERT(collector
!= NULL
);
4671 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4674 wxRichTextAttr attr
= GetCombinedAttributes();
4675 context
.ApplyVirtualAttributes(attr
, this);
4679 // Increase the size of the paragraph due to spacing
4680 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4681 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4682 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4683 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4684 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4686 int lineSpacing
= 0;
4688 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4689 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.HasFont())
4691 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4694 wxCheckSetFont(dc
, font
);
4695 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4699 // Start position for each line relative to the paragraph
4700 int startPositionFirstLine
= leftIndent
;
4701 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4703 // If we have a bullet in this paragraph, the start position for the first line's text
4704 // is actually leftIndent + leftSubIndent.
4705 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4706 startPositionFirstLine
= startPositionSubsequentLines
;
4708 long lastEndPos
= GetRange().GetStart()-1;
4709 long lastCompletedEndPos
= lastEndPos
;
4711 int currentWidth
= 0;
4712 SetPosition(rect
.GetPosition());
4714 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4717 int maxHeight
= currentPosition
.y
;
4722 int lineDescent
= 0;
4724 wxRichTextObjectList
::compatibility_iterator node
;
4726 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4728 wxArrayInt partialExtents
;
4731 int paraDescent
= 0;
4733 // This calculates the partial text extents
4734 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4736 node
= m_children
.GetFirst();
4739 wxRichTextObject
* child
= node
->GetData();
4741 //child->SetCachedSize(wxDefaultSize);
4742 child
->Layout(dc
, context
, rect
, style
);
4744 node
= node
->GetNext();
4750 // We may need to go back to a previous child, in which case create the new line,
4751 // find the child corresponding to the start position of the string, and
4754 wxRect availableRect
;
4756 node
= m_children
.GetFirst();
4759 wxRichTextObject
* child
= node
->GetData();
4761 // If floating, ignore. We already laid out floats.
4762 // Also ignore if empty object, except if we haven't got any
4764 if ((child
->IsFloating() && wxRichTextBuffer
::GetFloatingLayoutMode())
4765 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4768 node
= node
->GetNext();
4772 // If this is e.g. a composite text box, it will need to be laid out itself.
4773 // But if just a text fragment or image, for example, this will
4774 // do nothing. NB: won't we need to set the position after layout?
4775 // since for example if position is dependent on vertical line size, we
4776 // can't tell the position until the size is determined. So possibly introduce
4777 // another layout phase.
4779 // We may only be looking at part of a child, if we searched back for wrapping
4780 // and found a suitable point some way into the child. So get the size for the fragment
4783 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4784 long lastPosToUse
= child
->GetRange().GetEnd();
4785 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4787 if (lineBreakInThisObject
)
4788 lastPosToUse
= nextBreakPos
;
4791 int childDescent
= 0;
4793 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4794 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4795 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4797 if (child
->IsTopLevel())
4799 wxSize oldSize
= child
->GetCachedSize();
4801 child
->Invalidate(wxRICHTEXT_ALL
);
4802 child
->SetPosition(wxPoint(0, 0));
4804 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4805 // lays out the object again using the minimum size
4806 // The position will be determined by its location in its line,
4807 // and not by the child's actual position.
4808 child
->LayoutToBestSize(dc
, context
, buffer
,
4809 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4811 if (oldSize
!= child
->GetCachedSize())
4813 partialExtents
.Clear();
4815 // Recalculate the partial text extents since the child object changed size
4816 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4820 // Problem: we need to layout composites here for which we need the available width,
4821 // but we can't get the available width without using the float collector which
4822 // needs to know the object height.
4824 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4826 childSize
= child
->GetCachedSize();
4827 childDescent
= child
->GetDescent();
4831 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4832 // Get height only, then the width using the partial extents
4833 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4834 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4836 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4841 int loopIterations
= 0;
4843 // If there are nested objects that need to lay themselves out, we have to do this in a
4844 // loop because the height of the object may well depend on the available width.
4845 // And because of floating object positioning, the available width depends on the
4846 // height of the object and whether it will clash with the floating objects.
4847 // So, we see whether the available width changes due to the presence of floating images.
4848 // If it does, then we'll use the new restricted width to find the object height again.
4849 // If this causes another restriction in the available width, we'll try again, until
4850 // either we lose patience or the available width settles down.
4855 wxRect oldAvailableRect
= availableRect
;
4857 // Available width depends on the floating objects and the line height.
4858 // Note: the floating objects may be placed vertically along the two sides of
4859 // buffer, so we may have different available line widths with different
4860 // [startY, endY]. So, we can't determine how wide the available
4861 // space is until we know the exact line height.
4862 if (childDescent
== 0)
4864 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4865 lineDescent
= maxDescent
;
4866 lineAscent
= maxAscent
;
4870 lineDescent
= wxMax(childDescent
, maxDescent
);
4871 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4873 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4875 if (wxRichTextBuffer
::GetFloatingLayoutMode() && collector
)
4877 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4879 // Adjust availableRect to the space that is available when taking floating objects into account.
4881 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4883 int newX
= floatAvailableRect
.x
+ startOffset
;
4884 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4885 availableRect
.x
= newX
;
4886 availableRect
.width
= newW
;
4889 if (floatAvailableRect
.width
< availableRect
.width
)
4890 availableRect
.width
= floatAvailableRect
.width
;
4893 currentPosition
.x
= availableRect
.x
- rect
.x
;
4895 if (child
->IsTopLevel() && loopIterations
<= 20)
4897 if (availableRect
!= oldAvailableRect
)
4899 wxSize oldSize
= child
->GetCachedSize();
4901 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4902 // lays out the object again using the minimum size
4903 child
->Invalidate(wxRICHTEXT_ALL
);
4904 child
->LayoutToBestSize(dc
, context
, buffer
,
4905 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4906 childSize
= child
->GetCachedSize();
4907 childDescent
= child
->GetDescent();
4909 if (oldSize
!= child
->GetCachedSize())
4911 partialExtents
.Clear();
4913 // Recalculate the partial text extents since the child object changed size
4914 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4917 // Go around the loop finding the available rect for the given floating objects
4927 if (child
->IsTopLevel())
4929 // We can move it to the correct position at this point
4930 // TODO: probably need to add margin
4931 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4935 // 1) There was a line break BEFORE the natural break
4936 // 2) There was a line break AFTER the natural break
4937 // 3) It's the last line
4938 // 4) The child still fits (carry on) - 'else' clause
4940 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4942 (childSize
.x
+ currentWidth
> availableRect
.width
)
4945 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4949 long wrapPosition
= 0;
4950 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4951 wrapPosition
= child
->GetRange().GetEnd();
4954 // Find a place to wrap. This may walk back to previous children,
4955 // for example if a word spans several objects.
4956 // Note: one object must contains only one wxTextAtrr, so the line height will not
4957 // change inside one object. Thus, we can pass the remain line width to the
4958 // FindWrapPosition function.
4959 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4961 // If the function failed, just cut it off at the end of this child.
4962 wrapPosition
= child
->GetRange().GetEnd();
4965 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4966 if (wrapPosition
<= lastCompletedEndPos
)
4967 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4969 // Line end position shouldn't be the same as the end, or greater.
4970 if (wrapPosition
>= GetRange().GetEnd())
4971 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4973 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4975 // Let's find the actual size of the current line now
4977 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4981 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4982 if (!child
->IsEmpty())
4984 // Get height only, then the width using the partial extents
4985 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4986 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4990 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
4992 currentWidth
= actualSize
.x
;
4994 // The descent for the whole line at this point, is the correct max descent
4995 maxDescent
= childDescent
;
4997 maxAscent
= actualSize
.y
-childDescent
;
4999 // lineHeight is given by the height for the whole line, since it will
5000 // take into account ascend/descend.
5001 lineHeight
= actualSize
.y
;
5003 if (lineHeight
== 0 && buffer
)
5005 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5006 wxCheckSetFont(dc
, font
);
5007 lineHeight
= dc
.GetCharHeight();
5010 if (maxDescent
== 0)
5013 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5017 wxRichTextLine
* line
= AllocateLine(lineCount
);
5019 // Set relative range so we won't have to change line ranges when paragraphs are moved
5020 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5021 line
->SetPosition(currentPosition
);
5022 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5023 line
->SetDescent(maxDescent
);
5025 maxHeight
= currentPosition
.y
+ lineHeight
;
5027 // Now move down a line. TODO: add margins, spacing
5028 currentPosition
.y
+= lineHeight
;
5029 currentPosition
.y
+= lineSpacing
;
5032 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5037 // TODO: account for zero-length objects
5038 // wxASSERT(wrapPosition > lastCompletedEndPos);
5040 lastEndPos
= wrapPosition
;
5041 lastCompletedEndPos
= lastEndPos
;
5045 if (wrapPosition
< GetRange().GetEnd()-1)
5047 // May need to set the node back to a previous one, due to searching back in wrapping
5048 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5049 if (childAfterWrapPosition
)
5050 node
= m_children
.Find(childAfterWrapPosition
);
5052 node
= node
->GetNext();
5055 node
= node
->GetNext();
5057 // Apply paragraph styles such as alignment to the wrapped line
5058 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5062 // We still fit, so don't add a line, and keep going
5063 currentWidth
+= childSize
.x
;
5065 if (childDescent
== 0)
5067 // An object with a zero descend value wants to take up the whole
5068 // height regardless of baseline
5069 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5073 maxDescent
= wxMax(childDescent
, maxDescent
);
5074 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5077 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5079 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5080 lastEndPos
= child
->GetRange().GetEnd();
5082 node
= node
->GetNext();
5086 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5088 // Add the last line - it's the current pos -> last para pos
5089 // Substract -1 because the last position is always the end-paragraph position.
5090 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5092 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5094 wxRichTextLine
* line
= AllocateLine(lineCount
);
5096 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5098 // Set relative range so we won't have to change line ranges when paragraphs are moved
5099 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5101 line
->SetPosition(currentPosition
);
5103 if (lineHeight
== 0 && buffer
)
5105 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5106 wxCheckSetFont(dc
, font
);
5107 lineHeight
= dc
.GetCharHeight();
5110 if (maxDescent
== 0)
5113 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5116 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5117 line
->SetDescent(maxDescent
);
5118 currentPosition
.y
+= lineHeight
;
5119 currentPosition
.y
+= lineSpacing
;
5122 // Apply paragraph styles such as alignment to the wrapped line
5123 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5126 // Remove remaining unused line objects, if any
5127 ClearUnusedLines(lineCount
);
5129 // We need to add back the margins etc.
5131 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5132 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5133 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5134 SetCachedSize(marginRect
.GetSize());
5137 // The maximum size is the length of the paragraph stretched out into a line.
5138 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5139 // this size. TODO: take into account line breaks.
5141 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5142 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5143 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5144 SetMaxSize(marginRect
.GetSize());
5147 // Find the greatest minimum size. Currently we only look at non-text objects,
5148 // which isn't ideal but it would be slow to find the maximum word width to
5149 // use as the minimum.
5152 node
= m_children
.GetFirst();
5155 wxRichTextObject
* child
= node
->GetData();
5157 // If floating, ignore. We already laid out floats.
5158 // Also ignore if empty object, except if we haven't got any
5160 if ((!child
->IsFloating() || !wxRichTextBuffer
::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5162 if (child
->GetCachedSize().x
> minWidth
)
5163 minWidth
= child
->GetMinSize().x
;
5165 node
= node
->GetNext();
5168 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5169 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5170 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5171 SetMinSize(marginRect
.GetSize());
5174 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5175 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5176 // Use the text extents to calculate the size of each fragment in each line
5177 wxRichTextLineList
::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5180 wxRichTextLine
* line
= lineNode
->GetData();
5181 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5183 // Loop through objects until we get to the one within range
5184 wxRichTextObjectList
::compatibility_iterator node2
= m_children
.GetFirst();
5188 wxRichTextObject
* child
= node2
->GetData();
5190 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5192 wxRichTextRange rangeToUse
= lineRange
;
5193 rangeToUse
.LimitTo(child
->GetRange());
5195 // Find the size of the child from the text extents, and store in an array
5196 // for drawing later
5198 if (rangeToUse
.GetStart() > GetRange().GetStart())
5199 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5200 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5201 int sz
= right
- left
;
5202 line
->GetObjectSizes().Add(sz
);
5204 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5205 // Can break out of inner loop now since we've passed this line's range
5208 node2
= node2
->GetNext();
5211 lineNode
= lineNode
->GetNext();
5219 /// Apply paragraph styles, such as centering, to wrapped lines
5220 /// TODO: take into account box attributes, possibly
5221 void wxRichTextParagraph
::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5223 if (!attr
.HasAlignment())
5226 wxPoint pos
= line
->GetPosition();
5227 wxPoint originalPos
= pos
;
5228 wxSize size
= line
->GetSize();
5230 // centering, right-justification
5231 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5233 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5234 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5235 line
->SetPosition(pos
);
5237 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5239 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5240 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5241 line
->SetPosition(pos
);
5244 if (pos
!= originalPos
)
5246 wxPoint inc
= pos
- originalPos
;
5248 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5252 wxRichTextObject
* child
= node
->GetData();
5253 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5254 child
->Move(child
->GetPosition() + inc
);
5256 node
= node
->GetNext();
5261 /// Insert text at the given position
5262 bool wxRichTextParagraph
::InsertText(long pos
, const wxString
& text
)
5264 wxRichTextObject
* childToUse
= NULL
;
5265 wxRichTextObjectList
::compatibility_iterator nodeToUse
= wxRichTextObjectList
::compatibility_iterator();
5267 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5270 wxRichTextObject
* child
= node
->GetData();
5271 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5278 node
= node
->GetNext();
5283 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5286 int posInString
= pos
- textObject
->GetRange().GetStart();
5288 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5289 text
+ textObject
->GetText().Mid(posInString
);
5290 textObject
->SetText(newText
);
5292 int textLength
= text
.length();
5294 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5295 textObject
->GetRange().GetEnd() + textLength
));
5297 // Increment the end range of subsequent fragments in this paragraph.
5298 // We'll set the paragraph range itself at a higher level.
5300 wxRichTextObjectList
::compatibility_iterator node
= nodeToUse
->GetNext();
5303 wxRichTextObject
* child
= node
->GetData();
5304 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5305 textObject
->GetRange().GetEnd() + textLength
));
5307 node
= node
->GetNext();
5314 // TODO: if not a text object, insert at closest position, e.g. in front of it
5320 // Don't pass parent initially to suppress auto-setting of parent range.
5321 // We'll do that at a higher level.
5322 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5324 AppendChild(textObject
);
5331 void wxRichTextParagraph
::Copy(const wxRichTextParagraph
& obj
)
5333 wxRichTextCompositeObject
::Copy(obj
);
5336 /// Clear the cached lines
5337 void wxRichTextParagraph
::ClearLines()
5339 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5342 /// Get/set the object size for the given range. Returns false if the range
5343 /// is invalid for this object.
5344 bool wxRichTextParagraph
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
5346 if (!range
.IsWithin(GetRange()))
5349 if (flags
& wxRICHTEXT_UNFORMATTED
)
5351 // Just use unformatted data, assume no line breaks
5354 wxArrayInt childExtents
;
5363 int maxLineHeight
= 0;
5365 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5368 wxRichTextObject
* child
= node
->GetData();
5369 if (!child
->GetRange().IsOutside(range
))
5371 // Floating objects have a zero size within the paragraph.
5372 if (child
->IsFloating() && wxRichTextBuffer
::GetFloatingLayoutMode())
5377 if (partialExtents
->GetCount() > 0)
5378 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5382 partialExtents
->Add(0 /* zero size */ + lastSize
);
5389 wxRichTextRange rangeToUse
= range
;
5390 rangeToUse
.LimitTo(child
->GetRange());
5391 int childDescent
= 0;
5393 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5394 // but it's only going to be used after caching has taken place.
5395 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5397 childDescent
= child
->GetDescent();
5398 childSize
= child
->GetCachedSize();
5400 if (childDescent
== 0)
5402 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5406 maxDescent
= wxMax(maxDescent
, childDescent
);
5407 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5410 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5412 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5413 sz
.x
+= childSize
.x
;
5414 descent
= maxDescent
;
5416 else if (child
->IsTopLevel())
5418 childDescent
= child
->GetDescent();
5419 childSize
= child
->GetCachedSize();
5421 if (childDescent
== 0)
5423 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5427 maxDescent
= wxMax(maxDescent
, childDescent
);
5428 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5431 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5433 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5434 sz
.x
+= childSize
.x
;
5435 descent
= maxDescent
;
5437 // FIXME: this won't change the original values.
5438 // Should we be calling GetRangeSize above instead of using cached values?
5440 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5442 child
->SetCachedSize(childSize
);
5443 child
->SetDescent(childDescent
);
5450 if (partialExtents
->GetCount() > 0)
5451 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5455 partialExtents
->Add(childSize
.x
+ lastSize
);
5458 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5460 if (childDescent
== 0)
5462 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5466 maxDescent
= wxMax(maxDescent
, childDescent
);
5467 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5470 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5472 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5473 sz
.x
+= childSize
.x
;
5474 descent
= maxDescent
;
5476 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5478 child
->SetCachedSize(childSize
);
5479 child
->SetDescent(childDescent
);
5485 if (partialExtents
->GetCount() > 0)
5486 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5491 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5493 partialExtents
->Add(childExtents
[i
] + lastSize
);
5503 node
= node
->GetNext();
5509 // Use formatted data, with line breaks
5512 // We're going to loop through each line, and then for each line,
5513 // call GetRangeSize for the fragment that comprises that line.
5514 // Only we have to do that multiple times within the line, because
5515 // the line may be broken into pieces. For now ignore line break commands
5516 // (so we can assume that getting the unformatted size for a fragment
5517 // within a line is the actual size)
5519 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
5522 wxRichTextLine
* line
= node
->GetData();
5523 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5524 if (!lineRange
.IsOutside(range
))
5528 int maxLineHeight
= 0;
5529 int maxLineWidth
= 0;
5531 wxRichTextObjectList
::compatibility_iterator node2
= m_children
.GetFirst();
5534 wxRichTextObject
* child
= node2
->GetData();
5536 if ((!child
->IsFloating() || !wxRichTextBuffer
::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5538 wxRichTextRange rangeToUse
= lineRange
;
5539 rangeToUse
.LimitTo(child
->GetRange());
5540 if (child
->IsTopLevel())
5541 rangeToUse
= child
->GetOwnRange();
5544 int childDescent
= 0;
5545 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5547 if (childDescent
== 0)
5549 // Assume that if descent is zero, this child can occupy the full line height
5550 // and does not need space for the line's maximum descent. So we influence
5551 // the overall max line height only.
5552 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5556 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5557 maxDescent
= wxMax(maxAscent
, childDescent
);
5559 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5560 maxLineWidth
+= childSize
.x
;
5564 node2
= node2
->GetNext();
5567 descent
= wxMax(descent
, maxDescent
);
5569 // Increase size by a line (TODO: paragraph spacing)
5570 sz
.y
+= maxLineHeight
;
5571 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5573 node
= node
->GetNext();
5580 /// Finds the absolute position and row height for the given character position
5581 bool wxRichTextParagraph
::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5585 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5587 *height
= line
->GetSize().y
;
5589 *height
= dc
.GetCharHeight();
5591 // -1 means 'the start of the buffer'.
5594 pt
= pt
+ line
->GetPosition();
5599 // The final position in a paragraph is taken to mean the position
5600 // at the start of the next paragraph.
5601 if (index
== GetRange().GetEnd())
5603 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5604 wxASSERT( parent
!= NULL
);
5606 // Find the height at the next paragraph, if any
5607 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5610 *height
= line
->GetSize().y
;
5611 pt
= line
->GetAbsolutePosition();
5615 *height
= dc
.GetCharHeight();
5616 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5617 pt
= wxPoint(indent
, GetCachedSize().y
);
5623 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5626 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
5629 wxRichTextLine
* line
= node
->GetData();
5630 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5631 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5633 // If this is the last point in the line, and we're forcing the
5634 // returned value to be the start of the next line, do the required
5636 if (index
== lineRange
.GetEnd() && forceLineStart
)
5638 if (node
->GetNext())
5640 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5641 *height
= nextLine
->GetSize().y
;
5642 pt
= nextLine
->GetAbsolutePosition();
5647 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5649 wxRichTextRange
r(lineRange
.GetStart(), index
);
5653 // We find the size of the line up to this point,
5654 // then we can add this size to the line start position and
5655 // paragraph start position to find the actual position.
5657 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5659 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5660 *height
= line
->GetSize().y
;
5667 node
= node
->GetNext();
5673 /// Hit-testing: returns a flag indicating hit test details, plus
5674 /// information about position
5675 int wxRichTextParagraph
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5678 return wxRICHTEXT_HITTEST_NONE
;
5680 // If we're in the top-level container, then we can return
5681 // a suitable hit test code even if the point is outside the container area,
5682 // so that we can position the caret sensibly even if we don't
5683 // click on valid content. If we're not at the top-level, and the point
5684 // is not within this paragraph object, then we don't want to stop more
5685 // precise hit-testing from working prematurely, so return immediately.
5686 // NEW STRATEGY: use the parent boundary to test whether we're in the
5687 // right region, not the paragraph, since the paragraph may be positioned
5688 // some way in from where the user clicks.
5691 wxRichTextObject
* tempObj
, *tempContextObj
;
5692 if (GetParent() && GetParent()->wxRichTextObject
::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5693 return wxRICHTEXT_HITTEST_NONE
;
5696 wxRichTextObjectList
::compatibility_iterator objNode
= m_children
.GetFirst();
5699 wxRichTextObject
* child
= objNode
->GetData();
5700 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5701 // and also, if this seems composite but actually is marked as atomic,
5703 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5704 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5707 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5708 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5713 objNode
= objNode
->GetNext();
5716 wxPoint paraPos
= GetPosition();
5718 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetFirst();
5721 wxRichTextLine
* line
= node
->GetData();
5722 wxPoint linePos
= paraPos
+ line
->GetPosition();
5723 wxSize lineSize
= line
->GetSize();
5724 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5726 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5728 if (pt
.x
< linePos
.x
)
5730 textPosition
= lineRange
.GetStart();
5731 *obj
= FindObjectAtPosition(textPosition
);
5732 *contextObj
= GetContainer();
5733 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5735 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5737 textPosition
= lineRange
.GetEnd();
5738 *obj
= FindObjectAtPosition(textPosition
);
5739 *contextObj
= GetContainer();
5740 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5744 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5745 wxArrayInt partialExtents
;
5750 // This calculates the partial text extents
5751 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5753 int lastX
= linePos
.x
;
5755 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5757 int nextX
= partialExtents
[i
] + linePos
.x
;
5759 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5761 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5763 *obj
= FindObjectAtPosition(textPosition
);
5764 *contextObj
= GetContainer();
5766 // So now we know it's between i-1 and i.
5767 // Let's see if we can be more precise about
5768 // which side of the position it's on.
5770 int midPoint
= (nextX
+ lastX
)/2;
5771 if (pt
.x
>= midPoint
)
5772 return wxRICHTEXT_HITTEST_AFTER
;
5774 return wxRICHTEXT_HITTEST_BEFORE
;
5781 int lastX
= linePos
.x
;
5782 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5787 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5789 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5791 int nextX
= childSize
.x
+ linePos
.x
;
5793 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5797 *obj
= FindObjectAtPosition(textPosition
);
5798 *contextObj
= GetContainer();
5800 // So now we know it's between i-1 and i.
5801 // Let's see if we can be more precise about
5802 // which side of the position it's on.
5804 int midPoint
= (nextX
+ lastX
)/2;
5805 if (pt
.x
>= midPoint
)
5806 return wxRICHTEXT_HITTEST_AFTER
;
5808 return wxRICHTEXT_HITTEST_BEFORE
;
5819 node
= node
->GetNext();
5822 return wxRICHTEXT_HITTEST_NONE
;
5825 /// Split an object at this position if necessary, and return
5826 /// the previous object, or NULL if inserting at beginning.
5827 wxRichTextObject
* wxRichTextParagraph
::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5829 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5832 wxRichTextObject
* child
= node
->GetData();
5834 if (pos
== child
->GetRange().GetStart())
5838 if (node
->GetPrevious())
5839 *previousObject
= node
->GetPrevious()->GetData();
5841 *previousObject
= NULL
;
5847 if (child
->GetRange().Contains(pos
))
5849 // This should create a new object, transferring part of
5850 // the content to the old object and the rest to the new object.
5851 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5853 // If we couldn't split this object, just insert in front of it.
5856 // Maybe this is an empty string, try the next one
5861 // Insert the new object after 'child'
5862 if (node
->GetNext())
5863 m_children
.Insert(node
->GetNext(), newObject
);
5865 m_children
.Append(newObject
);
5866 newObject
->SetParent(this);
5869 *previousObject
= child
;
5875 node
= node
->GetNext();
5878 *previousObject
= NULL
;
5882 /// Move content to a list from obj on
5883 void wxRichTextParagraph
::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5885 wxRichTextObjectList
::compatibility_iterator node
= m_children
.Find(obj
);
5888 wxRichTextObject
* child
= node
->GetData();
5891 wxRichTextObjectList
::compatibility_iterator oldNode
= node
;
5893 node
= node
->GetNext();
5895 m_children
.DeleteNode(oldNode
);
5899 /// Add content back from list
5900 void wxRichTextParagraph
::MoveFromList(wxList
& list
)
5902 for (wxList
::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5904 AppendChild((wxRichTextObject
*) node
->GetData());
5909 void wxRichTextParagraph
::CalculateRange(long start
, long& end
)
5911 wxRichTextCompositeObject
::CalculateRange(start
, end
);
5913 // Add one for end of paragraph
5916 m_range
.SetRange(start
, end
);
5919 /// Find the object at the given position
5920 wxRichTextObject
* wxRichTextParagraph
::FindObjectAtPosition(long position
)
5922 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5925 wxRichTextObject
* obj
= node
->GetData();
5926 if (obj
->GetRange().Contains(position
) ||
5927 obj
->GetRange().GetStart() == position
||
5928 obj
->GetRange().GetEnd() == position
)
5931 node
= node
->GetNext();
5936 /// Get the plain text searching from the start or end of the range.
5937 /// The resulting string may be shorter than the range given.
5938 bool wxRichTextParagraph
::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5940 text
= wxEmptyString
;
5944 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
5947 wxRichTextObject
* obj
= node
->GetData();
5948 if (!obj
->GetRange().IsOutside(range
))
5950 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5953 text
+= textObj
->GetTextForRange(range
);
5961 node
= node
->GetNext();
5966 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetLast();
5969 wxRichTextObject
* obj
= node
->GetData();
5970 if (!obj
->GetRange().IsOutside(range
))
5972 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5975 text
= textObj
->GetTextForRange(range
) + text
;
5979 text
= wxT(" ") + text
;
5983 node
= node
->GetPrevious();
5990 /// Find a suitable wrap position.
5991 bool wxRichTextParagraph
::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5993 if (range
.GetLength() <= 0)
5996 // Find the first position where the line exceeds the available space.
5998 long breakPosition
= range
.GetEnd();
6000 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
6001 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
6005 if (range
.GetStart() > GetRange().GetStart())
6006 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
6011 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
6013 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6015 if (widthFromStartOfThisRange
> availableSpace
)
6017 breakPosition
= i
-1;
6025 // Binary chop for speed
6026 long minPos
= range
.GetStart();
6027 long maxPos
= range
.GetEnd();
6030 if (minPos
== maxPos
)
6033 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6035 if (sz
.x
> availableSpace
)
6036 breakPosition
= minPos
- 1;
6039 else if ((maxPos
- minPos
) == 1)
6042 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6044 if (sz
.x
> availableSpace
)
6045 breakPosition
= minPos
- 1;
6048 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6049 if (sz
.x
> availableSpace
)
6050 breakPosition
= maxPos
-1;
6056 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6059 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6061 if (sz
.x
> availableSpace
)
6073 // Now we know the last position on the line.
6074 // Let's try to find a word break.
6077 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6079 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6080 if (newLinePos
!= wxNOT_FOUND
)
6082 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6086 int spacePos
= plainText
.Find(wxT(' '), true);
6087 int tabPos
= plainText
.Find(wxT('\t'), true);
6088 int pos
= wxMax(spacePos
, tabPos
);
6089 if (pos
!= wxNOT_FOUND
)
6091 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6092 breakPosition
= breakPosition
- positionsFromEndOfString
;
6097 wrapPosition
= breakPosition
;
6102 /// Get the bullet text for this paragraph.
6103 wxString wxRichTextParagraph
::GetBulletText()
6105 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6106 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6107 return wxEmptyString
;
6109 int number
= GetAttributes().GetBulletNumber();
6112 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6114 text
.Printf(wxT("%d"), number
);
6116 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6118 // TODO: Unicode, and also check if number > 26
6119 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6121 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6123 // TODO: Unicode, and also check if number > 26
6124 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6126 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6128 text
= wxRichTextDecimalToRoman(number
);
6130 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6132 text
= wxRichTextDecimalToRoman(number
);
6135 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6137 text
= GetAttributes().GetBulletText();
6140 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6142 // The outline style relies on the text being computed statically,
6143 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6144 // should be stored in the attributes; if not, just use the number for this
6145 // level, as previously computed.
6146 if (!GetAttributes().GetBulletText().IsEmpty())
6147 text
= GetAttributes().GetBulletText();
6150 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6152 text
= wxT("(") + text
+ wxT(")");
6154 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6156 text
= text
+ wxT(")");
6159 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6167 /// Allocate or reuse a line object
6168 wxRichTextLine
* wxRichTextParagraph
::AllocateLine(int pos
)
6170 if (pos
< (int) m_cachedLines
.GetCount())
6172 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6178 wxRichTextLine
* line
= new wxRichTextLine(this);
6179 m_cachedLines
.Append(line
);
6184 /// Clear remaining unused line objects, if any
6185 bool wxRichTextParagraph
::ClearUnusedLines(int lineCount
)
6187 int cachedLineCount
= m_cachedLines
.GetCount();
6188 if ((int) cachedLineCount
> lineCount
)
6190 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6192 wxRichTextLineList
::compatibility_iterator node
= m_cachedLines
.GetLast();
6193 wxRichTextLine
* line
= node
->GetData();
6194 m_cachedLines
.Erase(node
);
6201 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6202 /// retrieve the actual style.
6203 wxRichTextAttr wxRichTextParagraph
::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6205 wxRichTextAttr attr
;
6206 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6209 attr
= buf
->GetBasicStyle();
6210 if (!includingBoxAttr
)
6212 attr
.GetTextBoxAttr().Reset();
6213 // The background colour will be painted by the container, and we don't
6214 // want to unnecessarily overwrite the background when we're drawing text
6215 // because this may erase the guideline (which appears just under the text
6216 // if there's no padding).
6217 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6219 wxRichTextApplyStyle(attr
, GetAttributes());
6222 attr
= GetAttributes();
6224 wxRichTextApplyStyle(attr
, contentStyle
);
6228 /// Get combined attributes of the base style and paragraph style.
6229 wxRichTextAttr wxRichTextParagraph
::GetCombinedAttributes(bool includingBoxAttr
) const
6231 wxRichTextAttr attr
;
6232 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6235 attr
= buf
->GetBasicStyle();
6236 if (!includingBoxAttr
)
6237 attr
.GetTextBoxAttr().Reset();
6238 wxRichTextApplyStyle(attr
, GetAttributes());
6241 attr
= GetAttributes();
6246 // Create default tabstop array
6247 void wxRichTextParagraph
::InitDefaultTabs()
6249 // create a default tab list at 10 mm each.
6250 for (int i
= 0; i
< 20; ++i
)
6252 sm_defaultTabs
.Add(i
*100);
6256 // Clear default tabstop array
6257 void wxRichTextParagraph
::ClearDefaultTabs()
6259 sm_defaultTabs
.Clear();
6262 void wxRichTextParagraph
::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6264 wxRichTextObjectList
::compatibility_iterator node
= GetChildren().GetFirst();
6267 wxRichTextObject
* anchored
= node
->GetData();
6268 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6271 wxRichTextAttr
parentAttr(GetAttributes());
6272 context
.ApplyVirtualAttributes(parentAttr
, this);
6275 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6277 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6278 parentAttr
, anchored
->GetAttributes(),
6279 parentRect
, availableSpace
,
6281 wxSize size
= anchored
->GetCachedSize();
6285 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6289 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6291 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6292 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6294 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6298 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6300 /* Update the offset */
6301 int newOffsetY
= pos
- rect
.y
;
6302 if (newOffsetY
!= offsetY
)
6304 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6305 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6306 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6309 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6311 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6312 x
= rect
.x
+ rect
.width
- size
.x
;
6314 //anchored->SetPosition(wxPoint(x, pos));
6315 anchored
->Move(wxPoint(x
, pos
)); // should move children
6316 anchored
->SetCachedSize(size
);
6317 floatCollector
->CollectFloat(this, anchored
);
6320 node
= node
->GetNext();
6324 // Get the first position from pos that has a line break character.
6325 long wxRichTextParagraph
::GetFirstLineBreakPosition(long pos
)
6327 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
6330 wxRichTextObject
* obj
= node
->GetData();
6331 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6333 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6336 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6341 node
= node
->GetNext();
6348 * This object represents a line in a paragraph, and stores
6349 * offsets from the start of the paragraph representing the
6350 * start and end positions of the line.
6353 wxRichTextLine
::wxRichTextLine(wxRichTextParagraph
* parent
)
6359 void wxRichTextLine
::Init(wxRichTextParagraph
* parent
)
6362 m_range
.SetRange(-1, -1);
6363 m_pos
= wxPoint(0, 0);
6364 m_size
= wxSize(0, 0);
6366 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6367 m_objectSizes
.Clear();
6372 void wxRichTextLine
::Copy(const wxRichTextLine
& obj
)
6374 m_range
= obj
.m_range
;
6375 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6376 m_objectSizes
= obj
.m_objectSizes
;
6380 /// Get the absolute object position
6381 wxPoint wxRichTextLine
::GetAbsolutePosition() const
6383 return m_parent
->GetPosition() + m_pos
;
6386 /// Get the absolute range
6387 wxRichTextRange wxRichTextLine
::GetAbsoluteRange() const
6389 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6390 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6395 * wxRichTextPlainText
6396 * This object represents a single piece of text.
6399 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6401 wxRichTextPlainText
::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6402 wxRichTextObject(parent
)
6405 SetAttributes(*style
);
6410 #define USE_KERNING_FIX 1
6412 // If insufficient tabs are defined, this is the tab width used
6413 #define WIDTH_FOR_DEFAULT_TABS 50
6416 bool wxRichTextPlainText
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6418 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6419 wxASSERT (para
!= NULL
);
6421 wxRichTextAttr
textAttr(para ? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6422 context
.ApplyVirtualAttributes(textAttr
, this);
6424 // Let's make the assumption for now that for content in a paragraph, including
6425 // text, we never have a discontinuous selection. So we only deal with a
6427 wxRichTextRange selectionRange
;
6428 if (selection
.IsValid())
6430 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6431 if (selectionRanges
.GetCount() > 0)
6432 selectionRange
= selectionRanges
[0];
6434 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6437 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6439 int offset
= GetRange().GetStart();
6441 wxString str
= m_text
;
6442 if (context
.HasVirtualText(this))
6444 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6448 // Replace line break characters with spaces
6449 wxString toRemove
= wxRichTextLineBreakChar
;
6450 str
.Replace(toRemove
, wxT(" "));
6451 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6454 long len
= range
.GetLength();
6455 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6457 // Test for the optimized situations where all is selected, or none
6460 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6461 wxCheckSetFont(dc
, textFont
);
6462 int charHeight
= dc
.GetCharHeight();
6465 if ( textFont
.IsOk() )
6467 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6469 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6470 wxCheckSetFont(dc
, textFont
);
6471 charHeight
= dc
.GetCharHeight();
6474 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6476 if (textFont
.IsUsingSizeInPixels())
6478 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6479 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6485 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6486 textFont
.SetPointSize(static_cast<int>(size
));
6490 wxCheckSetFont(dc
, textFont
);
6492 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6494 if (textFont
.IsUsingSizeInPixels())
6496 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6497 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6499 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6500 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6504 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6505 textFont
.SetPointSize(static_cast<int>(size
));
6507 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6508 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6510 wxCheckSetFont(dc
, textFont
);
6515 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6521 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6524 // TODO: new selection code
6526 // (a) All selected.
6527 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6529 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6531 // (b) None selected.
6532 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6534 // Draw all unselected
6535 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6539 // (c) Part selected, part not
6540 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6542 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6544 // 1. Initial unselected chunk, if any, up until start of selection.
6545 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6547 int r1
= range
.GetStart();
6548 int s1
= selectionRange
.GetStart()-1;
6549 int fragmentLen
= s1
- r1
+ 1;
6550 if (fragmentLen
< 0)
6552 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6554 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6556 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6559 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6561 // Compensate for kerning difference
6562 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6563 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6565 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6566 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6567 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6568 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6570 int kerningDiff
= (w1
+ w3
) - w2
;
6571 x
= x
- kerningDiff
;
6576 // 2. Selected chunk, if any.
6577 if (selectionRange
.GetEnd() >= range
.GetStart())
6579 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6580 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6582 int fragmentLen
= s2
- s1
+ 1;
6583 if (fragmentLen
< 0)
6585 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6587 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6589 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6592 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6594 // Compensate for kerning difference
6595 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6596 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6598 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6599 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6600 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6601 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6603 int kerningDiff
= (w1
+ w3
) - w2
;
6604 x
= x
- kerningDiff
;
6609 // 3. Remaining unselected chunk, if any
6610 if (selectionRange
.GetEnd() < range
.GetEnd())
6612 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6613 int r2
= range
.GetEnd();
6615 int fragmentLen
= r2
- s2
+ 1;
6616 if (fragmentLen
< 0)
6618 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6620 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6622 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6629 bool wxRichTextPlainText
::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6631 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6633 wxArrayInt tabArray
;
6637 if (attr
.GetTabs().IsEmpty())
6638 tabArray
= wxRichTextParagraph
::GetDefaultTabs();
6640 tabArray
= attr
.GetTabs();
6641 tabCount
= tabArray
.GetCount();
6643 for (int i
= 0; i
< tabCount
; ++i
)
6645 int pos
= tabArray
[i
];
6646 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6653 int nextTabPos
= -1;
6659 wxColour
highlightColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6660 wxColour
highlightTextColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6662 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6663 wxCheckSetPen(dc
, wxPen(highlightColour
));
6664 dc
.SetTextForeground(highlightTextColour
);
6665 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6669 dc
.SetTextForeground(attr
.GetTextColour());
6671 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6673 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6674 dc
.SetTextBackground(attr
.GetBackgroundColour());
6677 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6680 wxCoord x_orig
= GetParent()->GetPosition().x
;
6683 // the string has a tab
6684 // break up the string at the Tab
6685 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6686 str
= str
.AfterFirst(wxT('\t'));
6687 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6689 bool not_found
= true;
6690 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6692 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6694 // Find the next tab position.
6695 // Even if we're at the end of the tab array, we must still draw the chunk.
6697 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6699 if (nextTabPos
<= tabPos
)
6701 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6702 nextTabPos
= tabPos
+ defaultTabWidth
;
6709 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6710 dc
.DrawRectangle(selRect
);
6712 dc
.DrawText(stringChunk
, x
, y
);
6714 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6716 wxPen oldPen
= dc
.GetPen();
6717 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6718 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6719 wxCheckSetPen(dc
, oldPen
);
6725 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6730 dc
.GetTextExtent(str
, & w
, & h
);
6733 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6734 dc
.DrawRectangle(selRect
);
6736 dc
.DrawText(str
, x
, y
);
6738 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6740 wxPen oldPen
= dc
.GetPen();
6741 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6742 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6743 wxCheckSetPen(dc
, oldPen
);
6752 /// Lay the item out
6753 bool wxRichTextPlainText
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6755 // Only lay out if we haven't already cached the size
6757 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6759 // Eventually we want to have a reasonable estimate of minimum size.
6760 m_minSize
= wxSize(0, 0);
6765 void wxRichTextPlainText
::Copy(const wxRichTextPlainText
& obj
)
6767 wxRichTextObject
::Copy(obj
);
6769 m_text
= obj
.m_text
;
6772 /// Get/set the object size for the given range. Returns false if the range
6773 /// is invalid for this object.
6774 bool wxRichTextPlainText
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), const wxPoint
& position
, const wxSize
& WXUNUSED(parentSize
), wxArrayInt
* partialExtents
) const
6776 if (!range
.IsWithin(GetRange()))
6779 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6780 wxASSERT (para
!= NULL
);
6782 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6784 wxRichTextAttr
textAttr(para ? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6785 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6787 // Always assume unformatted text, since at this level we have no knowledge
6788 // of line breaks - and we don't need it, since we'll calculate size within
6789 // formatted text by doing it in chunks according to the line ranges
6791 bool bScript(false);
6792 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6795 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6796 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6798 wxFont textFont
= font
;
6799 if (textFont
.IsUsingSizeInPixels())
6801 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6802 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6806 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6807 textFont
.SetPointSize(static_cast<int>(size
));
6809 wxCheckSetFont(dc
, textFont
);
6812 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6814 wxFont textFont
= font
;
6815 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6816 wxCheckSetFont(dc
, textFont
);
6821 wxCheckSetFont(dc
, font
);
6825 bool haveDescent
= false;
6826 int startPos
= range
.GetStart() - GetRange().GetStart();
6827 long len
= range
.GetLength();
6829 wxString
str(m_text
);
6830 if (context
.HasVirtualText(this))
6832 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6836 wxString toReplace
= wxRichTextLineBreakChar
;
6837 str
.Replace(toReplace
, wxT(" "));
6839 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6841 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6842 stringChunk
.MakeUpper();
6846 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6848 // the string has a tab
6849 wxArrayInt tabArray
;
6850 if (textAttr
.GetTabs().IsEmpty())
6851 tabArray
= wxRichTextParagraph
::GetDefaultTabs();
6853 tabArray
= textAttr
.GetTabs();
6855 int tabCount
= tabArray
.GetCount();
6857 for (int i
= 0; i
< tabCount
; ++i
)
6859 int pos
= tabArray
[i
];
6860 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6864 int nextTabPos
= -1;
6866 while (stringChunk
.Find(wxT('\t')) >= 0)
6868 int absoluteWidth
= 0;
6870 // the string has a tab
6871 // break up the string at the Tab
6872 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6873 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6878 if (partialExtents
->GetCount() > 0)
6879 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6883 // Add these partial extents
6885 dc
.GetPartialTextExtents(stringFragment
, p
);
6887 for (j
= 0; j
< p
.GetCount(); j
++)
6888 partialExtents
->Add(oldWidth
+ p
[j
]);
6890 if (partialExtents
->GetCount() > 0)
6891 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6893 absoluteWidth
= relativeX
;
6897 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6899 absoluteWidth
= width
+ relativeX
;
6903 bool notFound
= true;
6904 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6906 nextTabPos
= tabArray
.Item(i
);
6908 // Find the next tab position.
6909 // Even if we're at the end of the tab array, we must still process the chunk.
6911 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6913 if (nextTabPos
<= absoluteWidth
)
6915 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6916 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6920 width
= nextTabPos
- relativeX
;
6923 partialExtents
->Add(width
);
6929 if (!stringChunk
.IsEmpty())
6934 if (partialExtents
->GetCount() > 0)
6935 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6939 // Add these partial extents
6941 dc
.GetPartialTextExtents(stringChunk
, p
);
6943 for (j
= 0; j
< p
.GetCount(); j
++)
6944 partialExtents
->Add(oldWidth
+ p
[j
]);
6948 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6956 int charHeight
= dc
.GetCharHeight();
6957 if ((*partialExtents
).GetCount() > 0)
6958 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6961 size
= wxSize(w
, charHeight
);
6965 size
= wxSize(width
, dc
.GetCharHeight());
6969 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6977 /// Do a split, returning an object containing the second part, and setting
6978 /// the first part in 'this'.
6979 wxRichTextObject
* wxRichTextPlainText
::DoSplit(long pos
)
6981 long index
= pos
- GetRange().GetStart();
6983 if (index
< 0 || index
>= (int) m_text
.length())
6986 wxString firstPart
= m_text
.Mid(0, index
);
6987 wxString secondPart
= m_text
.Mid(index
);
6991 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6992 newObject
->SetAttributes(GetAttributes());
6993 newObject
->SetProperties(GetProperties());
6995 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6996 GetRange().SetEnd(pos
-1);
7002 void wxRichTextPlainText
::CalculateRange(long start
, long& end
)
7004 end
= start
+ m_text
.length() - 1;
7005 m_range
.SetRange(start
, end
);
7009 bool wxRichTextPlainText
::DeleteRange(const wxRichTextRange
& range
)
7011 wxRichTextRange r
= range
;
7013 r
.LimitTo(GetRange());
7015 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7021 long startIndex
= r
.GetStart() - GetRange().GetStart();
7022 long len
= r
.GetLength();
7024 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7028 /// Get text for the given range.
7029 wxString wxRichTextPlainText
::GetTextForRange(const wxRichTextRange
& range
) const
7031 wxRichTextRange r
= range
;
7033 r
.LimitTo(GetRange());
7035 long startIndex
= r
.GetStart() - GetRange().GetStart();
7036 long len
= r
.GetLength();
7038 return m_text
.Mid(startIndex
, len
);
7041 /// Returns true if this object can merge itself with the given one.
7042 bool wxRichTextPlainText
::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7045 if (!context
.GetVirtualAttributesEnabled())
7047 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7048 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7052 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7053 if (!otherObj
|| m_text
.empty())
7056 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7059 // Check if differing virtual attributes makes it impossible to merge
7062 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7063 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7064 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7066 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7070 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7071 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7072 return virtualAttr1
== virtualAttr2
;
7077 /// Returns true if this object merged itself with the given one.
7078 /// The calling code will then delete the given object.
7079 bool wxRichTextPlainText
::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7081 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7082 wxASSERT( textObject
!= NULL
);
7086 m_text
+= textObject
->GetText();
7087 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7094 bool wxRichTextPlainText
::CanSplit(wxRichTextDrawingContext
& context
) const
7096 // If this object has any virtual attributes at all, whether for the whole object
7097 // or individual ones, we should try splitting it by calling Split.
7098 // Must be more than one character in order to be able to split.
7099 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7102 wxRichTextObject
* wxRichTextPlainText
::Split(wxRichTextDrawingContext
& context
)
7104 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7105 if (count
> 0 && GetParent())
7107 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7108 wxRichTextObjectList
::compatibility_iterator node
= parent
->GetChildren().Find(this);
7111 const wxRichTextAttr emptyAttr
;
7112 wxRichTextObjectList
::compatibility_iterator next
= node
->GetNext();
7114 wxArrayInt positions
;
7115 wxRichTextAttrArray attributes
;
7116 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7118 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7120 // We will gather up runs of text with the same virtual attributes
7122 int len
= m_text
.Length();
7125 // runStart and runEnd represent the accumulated run with a consistent attribute
7126 // that hasn't yet been appended
7129 wxRichTextAttr currentAttr
;
7130 wxString text
= m_text
;
7131 wxRichTextPlainText
* lastPlainText
= this;
7133 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7135 int pos
= positions
[i
];
7136 wxASSERT(pos
>= 0 && pos
< len
);
7137 if (pos
>= 0 && pos
< len
)
7139 const wxRichTextAttr
& attr
= attributes
[i
];
7146 // Check if there was a gap from the last known attribute and this.
7147 // In that case, we need to do something with the span of non-attributed text.
7148 else if ((pos
-1) > runEnd
)
7152 // We hadn't processed anything previously, so the previous run is from the text start
7153 // to just before this position. The current attribute remains empty.
7159 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7160 // then just extend the run.
7161 if (currentAttr
.IsDefault())
7167 // We need to add an object, or reuse the existing one.
7170 lastPlainText
= this;
7171 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7175 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7176 lastPlainText
= obj
;
7177 obj
->SetAttributes(GetAttributes());
7178 obj
->SetProperties(GetProperties());
7179 obj
->SetParent(parent
);
7181 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7183 parent
->GetChildren().Insert(next
, obj
);
7185 parent
->GetChildren().Append(obj
);
7188 runStart
= runEnd
+1;
7191 currentAttr
= emptyAttr
;
7196 wxASSERT(runEnd
== pos
-1);
7198 // Now we only have to deal with the previous run
7199 if (currentAttr
== attr
)
7201 // If we still have the same attributes, then we
7202 // simply increase the run size.
7209 // We need to add an object, or reuse the existing one.
7212 lastPlainText
= this;
7213 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7217 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7218 lastPlainText
= obj
;
7219 obj
->SetAttributes(GetAttributes());
7220 obj
->SetProperties(GetProperties());
7221 obj
->SetParent(parent
);
7223 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7225 parent
->GetChildren().Insert(next
, obj
);
7227 parent
->GetChildren().Append(obj
);
7239 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7240 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7241 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7243 // If the current attribute is empty, merge the run with the next fragment
7244 // which by definition (because it's not specified) has empty attributes.
7245 if (currentAttr
.IsDefault())
7248 if (runEnd
< (len
-1))
7250 // We need to add an object, or reuse the existing one.
7253 lastPlainText
= this;
7254 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7258 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7259 lastPlainText
= obj
;
7260 obj
->SetAttributes(GetAttributes());
7261 obj
->SetProperties(GetProperties());
7262 obj
->SetParent(parent
);
7264 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7266 parent
->GetChildren().Insert(next
, obj
);
7268 parent
->GetChildren().Append(obj
);
7271 runStart
= runEnd
+1;
7275 // Now the last, non-attributed fragment at the end, if any
7276 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7278 wxASSERT(runStart
!= 0);
7280 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7281 obj
->SetAttributes(GetAttributes());
7282 obj
->SetProperties(GetProperties());
7283 obj
->SetParent(parent
);
7285 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7287 parent
->GetChildren().Insert(next
, obj
);
7289 parent
->GetChildren().Append(obj
);
7291 lastPlainText
= obj
;
7295 return lastPlainText
;
7302 /// Dump to output stream for debugging
7303 void wxRichTextPlainText
::Dump(wxTextOutputStream
& stream
)
7305 wxRichTextObject
::Dump(stream
);
7306 stream
<< m_text
<< wxT("\n");
7309 /// Get the first position from pos that has a line break character.
7310 long wxRichTextPlainText
::GetFirstLineBreakPosition(long pos
)
7313 int len
= m_text
.length();
7314 int startPos
= pos
- m_range
.GetStart();
7315 for (i
= startPos
; i
< len
; i
++)
7317 wxChar ch
= m_text
[i
];
7318 if (ch
== wxRichTextLineBreakChar
)
7320 return i
+ m_range
.GetStart();
7328 * This is a kind of box, used to represent the whole buffer
7331 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7333 wxList wxRichTextBuffer
::sm_handlers
;
7334 wxList wxRichTextBuffer
::sm_drawingHandlers
;
7335 wxRichTextFieldTypeHashMap wxRichTextBuffer
::sm_fieldTypes
;
7336 wxRichTextRenderer
* wxRichTextBuffer
::sm_renderer
= NULL
;
7337 int wxRichTextBuffer
::sm_bulletRightMargin
= 20;
7338 float wxRichTextBuffer
::sm_bulletProportion
= (float) 0.3;
7339 bool wxRichTextBuffer
::sm_floatingLayoutMode
= true;
7342 void wxRichTextBuffer
::Init()
7344 m_commandProcessor
= new wxCommandProcessor
;
7345 m_styleSheet
= NULL
;
7347 m_batchedCommandDepth
= 0;
7348 m_batchedCommand
= NULL
;
7352 m_dimensionScale
= 1.0;
7358 wxRichTextBuffer
::~wxRichTextBuffer()
7360 delete m_commandProcessor
;
7361 delete m_batchedCommand
;
7364 ClearEventHandlers();
7367 void wxRichTextBuffer
::ResetAndClearCommands()
7371 GetCommandProcessor()->ClearCommands();
7374 Invalidate(wxRICHTEXT_ALL
);
7377 void wxRichTextBuffer
::Copy(const wxRichTextBuffer
& obj
)
7379 wxRichTextParagraphLayoutBox
::Copy(obj
);
7381 m_styleSheet
= obj
.m_styleSheet
;
7382 m_modified
= obj
.m_modified
;
7383 m_batchedCommandDepth
= 0;
7384 if (m_batchedCommand
)
7385 delete m_batchedCommand
;
7386 m_batchedCommand
= NULL
;
7387 m_suppressUndo
= obj
.m_suppressUndo
;
7388 m_invalidRange
= obj
.m_invalidRange
;
7389 m_dimensionScale
= obj
.m_dimensionScale
;
7390 m_fontScale
= obj
.m_fontScale
;
7393 /// Push style sheet to top of stack
7394 bool wxRichTextBuffer
::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7397 styleSheet
->InsertSheet(m_styleSheet
);
7399 SetStyleSheet(styleSheet
);
7404 /// Pop style sheet from top of stack
7405 wxRichTextStyleSheet
* wxRichTextBuffer
::PopStyleSheet()
7409 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7410 m_styleSheet
= oldSheet
->GetNextSheet();
7419 /// Submit command to insert paragraphs
7420 bool wxRichTextBuffer
::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7422 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7425 /// Submit command to insert paragraphs
7426 bool wxRichTextParagraphLayoutBox
::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7428 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7430 action
->GetNewParagraphs() = paragraphs
;
7432 action
->SetPosition(pos
);
7434 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7435 if (!paragraphs
.GetPartialParagraph())
7436 range
.SetEnd(range
.GetEnd()+1);
7438 // Set the range we'll need to delete in Undo
7439 action
->SetRange(range
);
7441 buffer
->SubmitAction(action
);
7446 /// Submit command to insert the given text
7447 bool wxRichTextBuffer
::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7450 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7452 return wxRichTextParagraphLayoutBox
::InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7455 /// Submit command to insert the given text
7456 bool wxRichTextParagraphLayoutBox
::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7458 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7460 wxRichTextAttr
* p
= NULL
;
7461 wxRichTextAttr paraAttr
;
7462 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7464 // Get appropriate paragraph style
7465 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7466 if (!paraAttr
.IsDefault())
7470 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7472 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7474 if (!text
.empty() && text
.Last() != wxT('\n'))
7476 // Don't count the newline when undoing
7478 action
->GetNewParagraphs().SetPartialParagraph(true);
7480 else if (!text
.empty() && text
.Last() == wxT('\n'))
7483 action
->SetPosition(pos
);
7485 // Set the range we'll need to delete in Undo
7486 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7488 buffer
->SubmitAction(action
);
7493 /// Submit command to insert the given text
7494 bool wxRichTextBuffer
::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7496 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7499 /// Submit command to insert the given text
7500 bool wxRichTextParagraphLayoutBox
::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7502 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7504 wxRichTextAttr
* p
= NULL
;
7505 wxRichTextAttr paraAttr
;
7506 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7508 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7509 if (!paraAttr
.IsDefault())
7513 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7514 // Don't include box attributes such as margins
7515 attr
.GetTextBoxAttr().Reset();
7517 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7518 action
->GetNewParagraphs().AppendChild(newPara
);
7519 action
->GetNewParagraphs().UpdateRanges();
7520 action
->GetNewParagraphs().SetPartialParagraph(false);
7521 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7525 newPara
->SetAttributes(*p
);
7527 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7529 if (para
&& para
->GetRange().GetEnd() == pos
)
7532 // Now see if we need to number the paragraph.
7533 if (newPara
->GetAttributes().HasBulletNumber())
7535 wxRichTextAttr numberingAttr
;
7536 if (FindNextParagraphNumber(para
, numberingAttr
))
7537 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7541 action
->SetPosition(pos
);
7543 // Use the default character style
7544 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7546 // Check whether the default style merely reflects the paragraph/basic style,
7547 // in which case don't apply it.
7548 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7549 defaultStyle
.GetTextBoxAttr().Reset();
7550 wxRichTextAttr toApply
;
7553 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7554 wxRichTextAttr newAttr
;
7555 // This filters out attributes that are accounted for by the current
7556 // paragraph/basic style
7557 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7560 toApply
= defaultStyle
;
7562 if (!toApply
.IsDefault())
7563 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7566 // Set the range we'll need to delete in Undo
7567 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7569 buffer
->SubmitAction(action
);
7574 /// Submit command to insert the given image
7575 bool wxRichTextBuffer
::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7576 const wxRichTextAttr
& textAttr
)
7578 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7581 /// Submit command to insert the given image
7582 bool wxRichTextParagraphLayoutBox
::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7583 wxRichTextCtrl
* ctrl
, int flags
,
7584 const wxRichTextAttr
& textAttr
)
7586 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7588 wxRichTextAttr
* p
= NULL
;
7589 wxRichTextAttr paraAttr
;
7590 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7592 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7593 if (!paraAttr
.IsDefault())
7597 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7599 // Don't include box attributes such as margins
7600 attr
.GetTextBoxAttr().Reset();
7602 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7604 newPara
->SetAttributes(*p
);
7606 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7607 newPara
->AppendChild(imageObject
);
7608 imageObject
->SetAttributes(textAttr
);
7609 action
->GetNewParagraphs().AppendChild(newPara
);
7610 action
->GetNewParagraphs().UpdateRanges();
7612 action
->GetNewParagraphs().SetPartialParagraph(true);
7614 action
->SetPosition(pos
);
7616 // Set the range we'll need to delete in Undo
7617 action
->SetRange(wxRichTextRange(pos
, pos
));
7619 buffer
->SubmitAction(action
);
7624 // Insert an object with no change of it
7625 wxRichTextObject
* wxRichTextBuffer
::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7627 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7630 // Insert an object with no change of it
7631 wxRichTextObject
* wxRichTextParagraphLayoutBox
::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7633 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7635 wxRichTextAttr
* p
= NULL
;
7636 wxRichTextAttr paraAttr
;
7637 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7639 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7640 if (!paraAttr
.IsDefault())
7644 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7646 // Don't include box attributes such as margins
7647 attr
.GetTextBoxAttr().Reset();
7649 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7651 newPara
->SetAttributes(*p
);
7653 newPara
->AppendChild(object
);
7654 action
->GetNewParagraphs().AppendChild(newPara
);
7655 action
->GetNewParagraphs().UpdateRanges();
7657 action
->GetNewParagraphs().SetPartialParagraph(true);
7659 action
->SetPosition(pos
);
7661 // Set the range we'll need to delete in Undo
7662 action
->SetRange(wxRichTextRange(pos
, pos
));
7664 buffer
->SubmitAction(action
);
7666 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7670 wxRichTextField
* wxRichTextParagraphLayoutBox
::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7671 const wxRichTextProperties
& properties
,
7672 wxRichTextCtrl
* ctrl
, int flags
,
7673 const wxRichTextAttr
& textAttr
)
7675 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7677 wxRichTextAttr
* p
= NULL
;
7678 wxRichTextAttr paraAttr
;
7679 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7681 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7682 if (!paraAttr
.IsDefault())
7686 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7688 // Don't include box attributes such as margins
7689 attr
.GetTextBoxAttr().Reset();
7691 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7693 newPara
->SetAttributes(*p
);
7695 wxRichTextField
* fieldObject
= new wxRichTextField();
7696 fieldObject
->wxRichTextObject
::SetProperties(properties
);
7697 fieldObject
->SetFieldType(fieldType
);
7698 fieldObject
->SetAttributes(textAttr
);
7699 newPara
->AppendChild(fieldObject
);
7700 action
->GetNewParagraphs().AppendChild(newPara
);
7701 action
->GetNewParagraphs().UpdateRanges();
7702 action
->GetNewParagraphs().SetPartialParagraph(true);
7703 action
->SetPosition(pos
);
7705 // Set the range we'll need to delete in Undo
7706 action
->SetRange(wxRichTextRange(pos
, pos
));
7708 buffer
->SubmitAction(action
);
7710 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7714 /// Get the style that is appropriate for a new paragraph at this position.
7715 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7717 wxRichTextAttr wxRichTextParagraphLayoutBox
::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7719 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7722 wxRichTextAttr attr
;
7723 bool foundAttributes
= false;
7725 // Look for a matching paragraph style
7726 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7728 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7731 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7732 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7734 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7737 foundAttributes
= true;
7738 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7742 // If we didn't find the 'next style', use this style instead.
7743 if (!foundAttributes
)
7745 foundAttributes
= true;
7746 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7751 // Also apply list style if present
7752 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7754 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7757 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7758 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7760 // Apply the overall list style, and item style for this level
7761 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7762 wxRichTextApplyStyle(attr
, listStyle
);
7763 attr
.SetOutlineLevel(thisLevel
);
7764 if (para
->GetAttributes().HasBulletNumber())
7765 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7769 if (!foundAttributes
)
7771 attr
= para
->GetAttributes();
7772 int flags
= attr
.GetFlags();
7774 // Eliminate character styles
7775 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7776 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7777 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7778 attr
.SetFlags(flags
);
7784 return wxRichTextAttr();
7787 /// Submit command to delete this range
7788 bool wxRichTextBuffer
::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7790 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7793 /// Submit command to delete this range
7794 bool wxRichTextParagraphLayoutBox
::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7796 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7798 action
->SetPosition(ctrl
->GetCaretPosition());
7800 // Set the range to delete
7801 action
->SetRange(range
);
7803 // Copy the fragment that we'll need to restore in Undo
7804 CopyFragment(range
, action
->GetOldParagraphs());
7806 // See if we're deleting a paragraph marker, in which case we need to
7807 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7808 if (range
.GetStart() == range
.GetEnd())
7810 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7811 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7813 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7814 if (nextPara
&& nextPara
!= para
)
7816 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7817 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7822 buffer
->SubmitAction(action
);
7827 /// Collapse undo/redo commands
7828 bool wxRichTextBuffer
::BeginBatchUndo(const wxString
& cmdName
)
7830 if (m_batchedCommandDepth
== 0)
7832 wxASSERT(m_batchedCommand
== NULL
);
7833 if (m_batchedCommand
)
7835 GetCommandProcessor()->Store(m_batchedCommand
);
7837 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7840 m_batchedCommandDepth
++;
7845 /// Collapse undo/redo commands
7846 bool wxRichTextBuffer
::EndBatchUndo()
7848 m_batchedCommandDepth
--;
7850 wxASSERT(m_batchedCommandDepth
>= 0);
7851 wxASSERT(m_batchedCommand
!= NULL
);
7853 if (m_batchedCommandDepth
== 0)
7855 GetCommandProcessor()->Store(m_batchedCommand
);
7856 m_batchedCommand
= NULL
;
7862 /// Submit immediately, or delay according to whether collapsing is on
7863 bool wxRichTextBuffer
::SubmitAction(wxRichTextAction
* action
)
7865 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7866 PrepareContent(action
->GetNewParagraphs());
7868 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7870 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7871 cmd
->AddAction(action
);
7873 cmd
->GetActions().Clear();
7876 m_batchedCommand
->AddAction(action
);
7880 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7881 cmd
->AddAction(action
);
7883 // Only store it if we're not suppressing undo.
7884 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7890 /// Begin suppressing undo/redo commands.
7891 bool wxRichTextBuffer
::BeginSuppressUndo()
7898 /// End suppressing undo/redo commands.
7899 bool wxRichTextBuffer
::EndSuppressUndo()
7906 /// Begin using a style
7907 bool wxRichTextBuffer
::BeginStyle(const wxRichTextAttr
& style
)
7909 wxRichTextAttr
newStyle(GetDefaultStyle());
7910 newStyle
.GetTextBoxAttr().Reset();
7912 // Save the old default style
7913 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7915 wxRichTextApplyStyle(newStyle
, style
);
7916 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7918 SetDefaultStyle(newStyle
);
7924 bool wxRichTextBuffer
::EndStyle()
7926 if (!m_attributeStack
.GetFirst())
7928 wxLogDebug(_("Too many EndStyle calls!"));
7932 wxList
::compatibility_iterator node
= m_attributeStack
.GetLast();
7933 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7934 m_attributeStack
.Erase(node
);
7936 SetDefaultStyle(*attr
);
7943 bool wxRichTextBuffer
::EndAllStyles()
7945 while (m_attributeStack
.GetCount() != 0)
7950 /// Clear the style stack
7951 void wxRichTextBuffer
::ClearStyleStack()
7953 for (wxList
::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7954 delete (wxRichTextAttr
*) node
->GetData();
7955 m_attributeStack
.Clear();
7958 /// Begin using bold
7959 bool wxRichTextBuffer
::BeginBold()
7961 wxRichTextAttr attr
;
7962 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7964 return BeginStyle(attr
);
7967 /// Begin using italic
7968 bool wxRichTextBuffer
::BeginItalic()
7970 wxRichTextAttr attr
;
7971 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7973 return BeginStyle(attr
);
7976 /// Begin using underline
7977 bool wxRichTextBuffer
::BeginUnderline()
7979 wxRichTextAttr attr
;
7980 attr
.SetFontUnderlined(true);
7982 return BeginStyle(attr
);
7985 /// Begin using point size
7986 bool wxRichTextBuffer
::BeginFontSize(int pointSize
)
7988 wxRichTextAttr attr
;
7989 attr
.SetFontSize(pointSize
);
7991 return BeginStyle(attr
);
7994 /// Begin using this font
7995 bool wxRichTextBuffer
::BeginFont(const wxFont
& font
)
7997 wxRichTextAttr attr
;
8000 return BeginStyle(attr
);
8003 /// Begin using this colour
8004 bool wxRichTextBuffer
::BeginTextColour(const wxColour
& colour
)
8006 wxRichTextAttr attr
;
8007 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
8008 attr
.SetTextColour(colour
);
8010 return BeginStyle(attr
);
8013 /// Begin using alignment
8014 bool wxRichTextBuffer
::BeginAlignment(wxTextAttrAlignment alignment
)
8016 wxRichTextAttr attr
;
8017 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8018 attr
.SetAlignment(alignment
);
8020 return BeginStyle(attr
);
8023 /// Begin left indent
8024 bool wxRichTextBuffer
::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8026 wxRichTextAttr attr
;
8027 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8028 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8030 return BeginStyle(attr
);
8033 /// Begin right indent
8034 bool wxRichTextBuffer
::BeginRightIndent(int rightIndent
)
8036 wxRichTextAttr attr
;
8037 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8038 attr
.SetRightIndent(rightIndent
);
8040 return BeginStyle(attr
);
8043 /// Begin paragraph spacing
8044 bool wxRichTextBuffer
::BeginParagraphSpacing(int before
, int after
)
8048 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8050 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8052 wxRichTextAttr attr
;
8053 attr
.SetFlags(flags
);
8054 attr
.SetParagraphSpacingBefore(before
);
8055 attr
.SetParagraphSpacingAfter(after
);
8057 return BeginStyle(attr
);
8060 /// Begin line spacing
8061 bool wxRichTextBuffer
::BeginLineSpacing(int lineSpacing
)
8063 wxRichTextAttr attr
;
8064 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8065 attr
.SetLineSpacing(lineSpacing
);
8067 return BeginStyle(attr
);
8070 /// Begin numbered bullet
8071 bool wxRichTextBuffer
::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8073 wxRichTextAttr attr
;
8074 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8075 attr
.SetBulletStyle(bulletStyle
);
8076 attr
.SetBulletNumber(bulletNumber
);
8077 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8079 return BeginStyle(attr
);
8082 /// Begin symbol bullet
8083 bool wxRichTextBuffer
::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8085 wxRichTextAttr attr
;
8086 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8087 attr
.SetBulletStyle(bulletStyle
);
8088 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8089 attr
.SetBulletText(symbol
);
8091 return BeginStyle(attr
);
8094 /// Begin standard bullet
8095 bool wxRichTextBuffer
::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8097 wxRichTextAttr attr
;
8098 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8099 attr
.SetBulletStyle(bulletStyle
);
8100 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8101 attr
.SetBulletName(bulletName
);
8103 return BeginStyle(attr
);
8106 /// Begin named character style
8107 bool wxRichTextBuffer
::BeginCharacterStyle(const wxString
& characterStyle
)
8109 if (GetStyleSheet())
8111 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8114 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8115 return BeginStyle(attr
);
8121 /// Begin named paragraph style
8122 bool wxRichTextBuffer
::BeginParagraphStyle(const wxString
& paragraphStyle
)
8124 if (GetStyleSheet())
8126 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8129 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8130 return BeginStyle(attr
);
8136 /// Begin named list style
8137 bool wxRichTextBuffer
::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8139 if (GetStyleSheet())
8141 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8144 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8146 attr
.SetBulletNumber(number
);
8148 return BeginStyle(attr
);
8155 bool wxRichTextBuffer
::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8157 wxRichTextAttr attr
;
8159 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8161 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8164 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8169 return BeginStyle(attr
);
8172 /// Adds a handler to the end
8173 void wxRichTextBuffer
::AddHandler(wxRichTextFileHandler
*handler
)
8175 sm_handlers
.Append(handler
);
8178 /// Inserts a handler at the front
8179 void wxRichTextBuffer
::InsertHandler(wxRichTextFileHandler
*handler
)
8181 sm_handlers
.Insert( handler
);
8184 /// Removes a handler
8185 bool wxRichTextBuffer
::RemoveHandler(const wxString
& name
)
8187 wxRichTextFileHandler
*handler
= FindHandler(name
);
8190 sm_handlers
.DeleteObject(handler
);
8198 /// Finds a handler by filename or, if supplied, type
8199 wxRichTextFileHandler
*wxRichTextBuffer
::FindHandlerFilenameOrType(const wxString
& filename
,
8200 wxRichTextFileType imageType
)
8202 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8203 return FindHandler(imageType
);
8204 else if (!filename
.IsEmpty())
8206 wxString path
, file
, ext
;
8207 wxFileName
::SplitPath(filename
, & path
, & file
, & ext
);
8208 return FindHandler(ext
, imageType
);
8215 /// Finds a handler by name
8216 wxRichTextFileHandler
* wxRichTextBuffer
::FindHandler(const wxString
& name
)
8218 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
8221 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8222 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8224 node
= node
->GetNext();
8229 /// Finds a handler by extension and type
8230 wxRichTextFileHandler
* wxRichTextBuffer
::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8232 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
8235 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8236 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8237 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8239 node
= node
->GetNext();
8244 /// Finds a handler by type
8245 wxRichTextFileHandler
* wxRichTextBuffer
::FindHandler(wxRichTextFileType type
)
8247 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
8250 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8251 if (handler
->GetType() == type
) return handler
;
8252 node
= node
->GetNext();
8257 void wxRichTextBuffer
::InitStandardHandlers()
8259 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8260 AddHandler(new wxRichTextPlainTextHandler
);
8263 void wxRichTextBuffer
::CleanUpHandlers()
8265 wxList
::compatibility_iterator node
= sm_handlers
.GetFirst();
8268 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8269 wxList
::compatibility_iterator next
= node
->GetNext();
8274 sm_handlers
.Clear();
8277 wxString wxRichTextBuffer
::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8284 wxList
::compatibility_iterator node
= GetHandlers().GetFirst();
8288 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8289 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8294 wildcard
+= wxT(";");
8295 wildcard
+= wxT("*.") + handler
->GetExtension();
8300 wildcard
+= wxT("|");
8301 wildcard
+= handler
->GetName();
8302 wildcard
+= wxT(" ");
8303 wildcard
+= _("files");
8304 wildcard
+= wxT(" (*.");
8305 wildcard
+= handler
->GetExtension();
8306 wildcard
+= wxT(")|*.");
8307 wildcard
+= handler
->GetExtension();
8309 types
->Add(handler
->GetType());
8314 node
= node
->GetNext();
8318 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8322 #if wxUSE_FFILE && wxUSE_STREAMS
8324 bool wxRichTextBuffer
::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8326 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8329 SetDefaultStyle(wxRichTextAttr());
8330 handler
->SetFlags(GetHandlerFlags());
8331 bool success
= handler
->LoadFile(this, filename
);
8332 Invalidate(wxRICHTEXT_ALL
);
8340 bool wxRichTextBuffer
::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8342 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8345 handler
->SetFlags(GetHandlerFlags());
8346 return handler
->SaveFile(this, filename
);
8351 #endif // wxUSE_FFILE && wxUSE_STREAMS
8354 /// Load from a stream
8355 bool wxRichTextBuffer
::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8357 wxRichTextFileHandler
* handler
= FindHandler(type
);
8360 SetDefaultStyle(wxRichTextAttr());
8361 handler
->SetFlags(GetHandlerFlags());
8362 bool success
= handler
->LoadFile(this, stream
);
8363 Invalidate(wxRICHTEXT_ALL
);
8370 /// Save to a stream
8371 bool wxRichTextBuffer
::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8373 wxRichTextFileHandler
* handler
= FindHandler(type
);
8376 handler
->SetFlags(GetHandlerFlags());
8377 return handler
->SaveFile(this, stream
);
8382 #endif // wxUSE_STREAMS
8384 /// Copy the range to the clipboard
8385 bool wxRichTextBuffer
::CopyToClipboard(const wxRichTextRange
& range
)
8387 bool success
= false;
8388 wxRichTextParagraphLayoutBox
* container
= this;
8389 if (GetRichTextCtrl())
8390 container
= GetRichTextCtrl()->GetFocusObject();
8392 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8394 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8396 wxTheClipboard
->Clear();
8398 // Add composite object
8400 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8403 wxString text
= container
->GetTextForRange(range
);
8406 text
= wxTextFile
::Translate(text
, wxTextFileType_Dos
);
8409 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8412 // Add rich text buffer data object. This needs the XML handler to be present.
8414 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8416 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8417 container
->CopyFragment(range
, *richTextBuf
);
8419 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8422 if (wxTheClipboard
->SetData(compositeObject
))
8425 wxTheClipboard
->Close();
8434 /// Paste the clipboard content to the buffer
8435 bool wxRichTextBuffer
::PasteFromClipboard(long position
)
8437 bool success
= false;
8438 wxRichTextParagraphLayoutBox
* container
= this;
8439 if (GetRichTextCtrl())
8440 container
= GetRichTextCtrl()->GetFocusObject();
8442 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8443 if (CanPasteFromClipboard())
8445 if (wxTheClipboard
->Open())
8447 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject
::GetRichTextBufferFormatId())))
8449 wxRichTextBufferDataObject data
;
8450 wxTheClipboard
->GetData(data
);
8451 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8454 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8455 if (GetRichTextCtrl())
8456 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8457 delete richTextBuffer
;
8460 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8462 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8466 wxTextDataObject data
;
8467 wxTheClipboard
->GetData(data
);
8468 wxString
text(data
.GetText());
8471 text2
.Alloc(text
.Length()+1);
8473 for (i
= 0; i
< text
.Length(); i
++)
8475 wxChar ch
= text
[i
];
8476 if (ch
!= wxT('\r'))
8480 wxString text2
= text
;
8482 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8484 if (GetRichTextCtrl())
8485 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8489 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8491 wxBitmapDataObject data
;
8492 wxTheClipboard
->GetData(data
);
8493 wxBitmap
bitmap(data
.GetBitmap());
8494 wxImage
image(bitmap
.ConvertToImage());
8496 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8498 action
->GetNewParagraphs().AddImage(image
);
8500 if (action
->GetNewParagraphs().GetChildCount() == 1)
8501 action
->GetNewParagraphs().SetPartialParagraph(true);
8503 action
->SetPosition(position
+1);
8505 // Set the range we'll need to delete in Undo
8506 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8508 SubmitAction(action
);
8512 wxTheClipboard
->Close();
8516 wxUnusedVar(position
);
8521 /// Can we paste from the clipboard?
8522 bool wxRichTextBuffer
::CanPasteFromClipboard() const
8524 bool canPaste
= false;
8525 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8526 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8528 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8530 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8532 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject
::GetRichTextBufferFormatId())) ||
8533 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8537 wxTheClipboard
->Close();
8543 /// Dumps contents of buffer for debugging purposes
8544 void wxRichTextBuffer
::Dump()
8548 wxStringOutputStream
stream(& text
);
8549 wxTextOutputStream
textStream(stream
);
8556 /// Add an event handler
8557 bool wxRichTextBuffer
::AddEventHandler(wxEvtHandler
* handler
)
8559 m_eventHandlers
.Append(handler
);
8563 /// Remove an event handler
8564 bool wxRichTextBuffer
::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8566 wxList
::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8569 m_eventHandlers
.Erase(node
);
8579 /// Clear event handlers
8580 void wxRichTextBuffer
::ClearEventHandlers()
8582 m_eventHandlers
.Clear();
8585 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8586 /// otherwise will stop at the first successful one.
8587 bool wxRichTextBuffer
::SendEvent(wxEvent
& event
, bool sendToAll
)
8589 bool success
= false;
8590 for (wxList
::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8592 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8593 if (handler
->ProcessEvent(event
))
8603 /// Set style sheet and notify of the change
8604 bool wxRichTextBuffer
::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8606 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8608 wxWindowID winid
= wxID_ANY
;
8609 if (GetRichTextCtrl())
8610 winid
= GetRichTextCtrl()->GetId();
8612 wxRichTextEvent
event(wxEVT_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8613 event
.SetEventObject(GetRichTextCtrl());
8614 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8615 event
.SetOldStyleSheet(oldSheet
);
8616 event
.SetNewStyleSheet(sheet
);
8619 if (SendEvent(event
) && !event
.IsAllowed())
8621 if (sheet
!= oldSheet
)
8627 if (oldSheet
&& oldSheet
!= sheet
)
8630 SetStyleSheet(sheet
);
8632 event
.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED
);
8633 event
.SetOldStyleSheet(NULL
);
8636 return SendEvent(event
);
8639 /// Set renderer, deleting old one
8640 void wxRichTextBuffer
::SetRenderer(wxRichTextRenderer
* renderer
)
8644 sm_renderer
= renderer
;
8647 /// Hit-testing: returns a flag indicating hit test details, plus
8648 /// information about position
8649 int wxRichTextBuffer
::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8651 int ret
= wxRichTextParagraphLayoutBox
::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8652 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8658 textPosition
= m_ownRange
.GetEnd()-1;
8661 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8665 void wxRichTextBuffer
::SetFontScale(double fontScale
)
8667 m_fontScale
= fontScale
;
8668 m_fontTable
.SetFontScale(fontScale
);
8671 void wxRichTextBuffer
::SetDimensionScale(double dimScale
)
8673 m_dimensionScale
= dimScale
;
8676 bool wxRichTextStdRenderer
::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8678 if (bulletAttr
.GetTextColour().IsOk())
8680 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8681 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8685 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8686 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8690 if (bulletAttr
.HasFont())
8692 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8695 font
= (*wxNORMAL_FONT
);
8697 wxCheckSetFont(dc
, font
);
8699 int charHeight
= dc
.GetCharHeight();
8701 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer
::GetBulletProportion());
8702 int bulletHeight
= bulletWidth
;
8706 // Calculate the top position of the character (as opposed to the whole line height)
8707 int y
= rect
.y
+ (rect
.height
- charHeight
);
8709 // Calculate where the bullet should be positioned
8710 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8712 // The margin between a bullet and text.
8713 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer
::GetBulletRightMargin());
8715 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8716 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8717 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8718 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8720 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8722 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8724 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8727 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8728 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8729 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8730 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8732 dc
.DrawPolygon(4, pts
);
8734 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8737 pts
[0].x
= x
; pts
[0].y
= y
;
8738 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8739 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8741 dc
.DrawPolygon(3, pts
);
8743 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8745 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8746 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8748 else // "standard/circle", and catch-all
8750 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8756 bool wxRichTextStdRenderer
::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8761 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8763 wxRichTextAttr fontAttr
;
8764 if (attr
.HasFontPixelSize())
8765 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8767 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8768 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8769 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8770 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8771 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8772 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8774 else if (attr
.HasFont())
8775 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8777 font
= (*wxNORMAL_FONT
);
8779 wxCheckSetFont(dc
, font
);
8781 if (attr
.GetTextColour().IsOk())
8782 dc
.SetTextForeground(attr
.GetTextColour());
8784 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8786 int charHeight
= dc
.GetCharHeight();
8788 dc
.GetTextExtent(text
, & tw
, & th
);
8792 // Calculate the top position of the character (as opposed to the whole line height)
8793 int y
= rect
.y
+ (rect
.height
- charHeight
);
8795 // The margin between a bullet and text.
8796 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer
::GetBulletRightMargin());
8798 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8799 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8800 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8801 x
= x
+ (rect
.width
)/2 - tw
/2;
8803 dc
.DrawText(text
, x
, y
);
8811 bool wxRichTextStdRenderer
::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8813 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8814 // with the buffer. The store will allow retrieval from memory, disk or other means.
8818 /// Enumerate the standard bullet names currently supported
8819 bool wxRichTextStdRenderer
::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8821 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8822 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8823 bulletNames
.Add(wxTRANSLATE("standard/square"));
8824 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8825 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8834 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8836 wxRichTextBox
::wxRichTextBox(wxRichTextObject
* parent
):
8837 wxRichTextParagraphLayoutBox(parent
)
8842 bool wxRichTextBox
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8847 // TODO: if the active object in the control, draw an indication.
8848 // We need to add the concept of active object, and not just focus object,
8849 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8850 // Ultimately we would like to be able to interactively resize an active object
8851 // using drag handles.
8852 return wxRichTextParagraphLayoutBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8856 void wxRichTextBox
::Copy(const wxRichTextBox
& obj
)
8858 wxRichTextParagraphLayoutBox
::Copy(obj
);
8861 // Edit properties via a GUI
8862 bool wxRichTextBox
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8864 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8865 boxDlg
.SetAttributes(GetAttributes());
8867 if (boxDlg
.ShowModal() == wxID_OK
)
8869 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8870 // indeterminate in the object.
8871 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8882 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8884 wxRichTextField
::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8885 wxRichTextParagraphLayoutBox(parent
)
8887 SetFieldType(fieldType
);
8891 bool wxRichTextField
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8896 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8897 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8900 // Fallback; but don't draw guidelines.
8901 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8902 return wxRichTextParagraphLayoutBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8905 bool wxRichTextField
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8907 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8908 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8912 return wxRichTextParagraphLayoutBox
::Layout(dc
, context
, rect
, parentRect
, style
);
8915 bool wxRichTextField
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
8917 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8919 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8921 return wxRichTextParagraphLayoutBox
::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8925 void wxRichTextField
::CalculateRange(long start
, long& end
)
8928 wxRichTextParagraphLayoutBox
::CalculateRange(start
, end
);
8930 wxRichTextObject
::CalculateRange(start
, end
);
8934 void wxRichTextField
::Copy(const wxRichTextField
& obj
)
8936 wxRichTextParagraphLayoutBox
::Copy(obj
);
8938 UpdateField(GetBuffer());
8941 // Edit properties via a GUI
8942 bool wxRichTextField
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8944 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8946 return fieldType
->EditProperties(this, parent
, buffer
);
8951 bool wxRichTextField
::CanEditProperties() const
8953 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8955 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8960 wxString wxRichTextField
::GetPropertiesMenuLabel() const
8962 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8964 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8966 return wxEmptyString
;
8969 bool wxRichTextField
::UpdateField(wxRichTextBuffer
* buffer
)
8971 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8973 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8978 bool wxRichTextField
::IsTopLevel() const
8980 wxRichTextFieldType
* fieldType
= wxRichTextBuffer
::FindFieldType(GetFieldType());
8982 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8987 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8989 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8991 wxRichTextFieldTypeStandard
::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8997 SetDisplayStyle(displayStyle
);
9000 wxRichTextFieldTypeStandard
::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
9006 SetDisplayStyle(displayStyle
);
9009 void wxRichTextFieldTypeStandard
::Init()
9011 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
9012 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
9013 m_textColour
= *wxWHITE
;
9014 m_borderColour
= *wxBLACK
;
9015 m_backgroundColour
= *wxBLACK
;
9016 m_verticalPadding
= 1;
9017 m_horizontalPadding
= 3;
9018 m_horizontalMargin
= 2;
9019 m_verticalMargin
= 0;
9022 void wxRichTextFieldTypeStandard
::Copy(const wxRichTextFieldTypeStandard
& field
)
9024 wxRichTextFieldType
::Copy(field
);
9026 m_label
= field
.m_label
;
9027 m_displayStyle
= field
.m_displayStyle
;
9028 m_font
= field
.m_font
;
9029 m_textColour
= field
.m_textColour
;
9030 m_borderColour
= field
.m_borderColour
;
9031 m_backgroundColour
= field
.m_backgroundColour
;
9032 m_verticalPadding
= field
.m_verticalPadding
;
9033 m_horizontalPadding
= field
.m_horizontalPadding
;
9034 m_horizontalMargin
= field
.m_horizontalMargin
;
9035 m_bitmap
= field
.m_bitmap
;
9038 bool wxRichTextFieldTypeStandard
::Draw(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
9040 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9041 return false; // USe default composite drawing
9042 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9046 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9047 wxBrush
backgroundBrush(m_backgroundColour
);
9048 wxColour
textColour(m_textColour
);
9050 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9052 wxColour
highlightColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9053 wxColour
highlightTextColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9055 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9056 backgroundBrush
= wxBrush(highlightColour
);
9058 wxCheckSetBrush(dc
, backgroundBrush
);
9059 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9060 dc
.DrawRectangle(rect
);
9063 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9066 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9067 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9068 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9070 // clientArea is where the text is actually written
9071 wxRect clientArea
= objectRect
;
9073 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9075 dc
.SetPen(borderPen
);
9076 dc
.SetBrush(backgroundBrush
);
9077 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9079 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9081 int arrowLength
= objectRect
.height
/2;
9082 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9085 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9086 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9087 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9088 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9089 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9090 dc
.SetPen(borderPen
);
9091 dc
.SetBrush(backgroundBrush
);
9092 dc
.DrawPolygon(5, pts
);
9094 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9096 int arrowLength
= objectRect
.height
/2;
9097 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9098 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9101 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9102 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9103 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9104 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9105 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9106 dc
.SetPen(borderPen
);
9107 dc
.SetBrush(backgroundBrush
);
9108 dc
.DrawPolygon(5, pts
);
9111 if (m_bitmap
.IsOk())
9113 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9114 int y
= clientArea
.y
+ m_verticalPadding
;
9115 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9117 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9119 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9120 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9121 dc
.SetLogicalFunction(wxINVERT
);
9122 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9123 dc
.SetLogicalFunction(wxCOPY
);
9128 wxString
label(m_label
);
9129 if (label
.IsEmpty())
9131 int w
, h
, maxDescent
;
9133 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9134 dc
.SetTextForeground(textColour
);
9136 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9137 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9138 dc
.DrawText(m_label
, x
, y
);
9145 bool wxRichTextFieldTypeStandard
::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9147 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9148 return false; // USe default composite layout
9150 wxSize size
= GetSize(obj
, dc
, context
, style
);
9151 obj
->SetCachedSize(size
);
9152 obj
->SetMinSize(size
);
9153 obj
->SetMaxSize(size
);
9157 bool wxRichTextFieldTypeStandard
::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
9159 if (IsTopLevel(obj
))
9160 return obj
->wxRichTextParagraphLayoutBox
::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9163 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9167 if (partialExtents
->GetCount() > 0)
9168 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9171 partialExtents
->Add(lastSize
+ sz
.x
);
9178 wxSize wxRichTextFieldTypeStandard
::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9181 int w
= 0, h
= 0, maxDescent
= 0;
9184 if (m_bitmap
.IsOk())
9186 w
= m_bitmap
.GetWidth();
9187 h
= m_bitmap
.GetHeight();
9189 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9193 wxString
label(m_label
);
9194 if (label
.IsEmpty())
9197 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9199 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9202 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9204 sz
.x
+= borderSize
*2;
9205 sz
.y
+= borderSize
*2;
9208 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9210 // Add space for the arrow
9211 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9217 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9219 wxRichTextCell
::wxRichTextCell(wxRichTextObject
* parent
):
9220 wxRichTextBox(parent
)
9225 bool wxRichTextCell
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9227 return wxRichTextBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9231 void wxRichTextCell
::Copy(const wxRichTextCell
& obj
)
9233 wxRichTextBox
::Copy(obj
);
9236 // Edit properties via a GUI
9237 bool wxRichTextCell
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9239 // We need to gather common attributes for all selected cells.
9241 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9242 bool multipleCells
= false;
9243 wxRichTextAttr attr
;
9245 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9246 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9248 wxRichTextAttr clashingAttr
, absentAttr
;
9249 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9251 int selectedCellCount
= 0;
9252 for (i
= 0; i
< sel
.GetCount(); i
++)
9254 const wxRichTextRange
& range
= sel
[i
];
9255 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9258 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9260 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9262 selectedCellCount
++;
9265 multipleCells
= selectedCellCount
> 1;
9269 attr
= GetAttributes();
9274 caption
= _("Multiple Cell Properties");
9276 caption
= _("Cell Properties");
9278 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9279 cellDlg
.SetAttributes(attr
);
9281 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9284 // We don't want position and floating controls for a cell.
9285 sizePage
->ShowPositionControls(false);
9286 sizePage
->ShowFloatingControls(false);
9289 if (cellDlg
.ShowModal() == wxID_OK
)
9293 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9294 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9295 // since it may represent clashing attributes across multiple objects.
9296 table
->SetCellStyle(sel
, attr
);
9299 // For a single object, indeterminate attributes set by the user should be reflected in the
9300 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9301 // the style directly instead of applying (which ignores indeterminate attributes,
9302 // leaving them as they were).
9303 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9310 // The next 2 methods return span values. Note that the default is 1, not 0
9311 int wxRichTextCell
::GetColspan() const
9314 if (GetProperties().HasProperty(wxT("colspan")))
9316 span
= GetProperties().GetPropertyLong(wxT("colspan"));
9322 int wxRichTextCell
::GetRowspan() const
9325 if (GetProperties().HasProperty(wxT("rowspan")))
9327 span
= GetProperties().GetPropertyLong(wxT("rowspan"));
9333 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9335 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9337 wxRichTextTable
::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9343 // Draws the object.
9344 bool wxRichTextTable
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9346 wxRichTextBox
::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9348 // Now draw the table outline, if any, to ensure there are no breaks caused by
9349 // different-coloured cell dividers overwriting the overall table border.
9350 int colCount
= GetColumnCount();
9351 int rowCount
= GetRowCount();
9353 for (col
= 0; col
< colCount
; col
++)
9355 for (row
= 0; row
< rowCount
; row
++)
9357 if (row
== 0 || row
== (rowCount
-1) || col
== 0 || col
== (colCount
-1))
9359 wxRichTextCell
* cell
= GetCell(row
, col
);
9360 if (cell
&& !cell
->GetRange().IsOutside(range
))
9362 wxRect
childRect(cell
->GetPosition(), cell
->GetCachedSize());
9363 wxRichTextAttr
attr(cell
->GetAttributes());
9365 attr
.GetTextBoxAttr().GetBorder().GetTop().Reset();
9366 if (row
!= (rowCount
-1))
9367 attr
.GetTextBoxAttr().GetBorder().GetBottom().Reset();
9369 attr
.GetTextBoxAttr().GetBorder().GetLeft().Reset();
9370 if (col
!= (colCount
-1))
9371 attr
.GetTextBoxAttr().GetBorder().GetRight().Reset();
9373 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
9375 wxRect
boxRect(cell
->GetPosition(), cell
->GetCachedSize());
9376 wxRect marginRect
= boxRect
;
9377 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
9379 cell
->GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9380 cell
->DrawBorder(dc
, GetBuffer(), attr
.GetTextBoxAttr().GetBorder(), borderRect
);
9390 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9391 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9394 // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
9395 int GetRowspanDisplacement(const wxRichTextTable
* table
, int row
, int col
, int paddingX
, const wxArrayInt
& colWidths
)
9397 // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
9398 // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
9399 // and all cells to their right are effectively shifted left. As a result there's no hole for
9400 // the spanning cell to fill.
9401 // So search back along the current row for hidden cells. However there's also the annoying issue of a
9402 // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
9403 // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
9404 // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
9405 // The answer is to look above each hidden cell in turn, which I think covers all bases.
9407 for (int prevcol
= 0; prevcol
< col
; ++prevcol
)
9409 if (!table
->GetCell(row
, prevcol
)->IsShown())
9411 // We've found a hidden cell. If it's hidden because of colspan to its left, it's
9412 // already been taken into account; but not if there's a rowspanning cell above
9413 for (int prevrow
= row
-1; prevrow
>= 0; --prevrow
)
9415 wxRichTextCell
* cell
= table
->GetCell(prevrow
, prevcol
);
9416 if (cell
&& cell
->IsShown())
9418 int rowSpan
= cell
->GetRowspan();
9419 if (rowSpan
> 1 && rowSpan
> (row
-prevrow
))
9421 // There is a rowspanning cell above above the hidden one, so we need
9422 // to right-shift the index cell by this column's width. Furthermore,
9423 // if the cell also colspans, we need to shift by all affected columns
9424 for (int colSpan
= 0; colSpan
< cell
->GetColspan(); ++colSpan
)
9425 deltaX
+= (colWidths
[prevcol
+colSpan
] + paddingX
);
9435 // Helper function for Layout() that expands any cell with rowspan > 1
9436 void ExpandCellsWithRowspan(const wxRichTextTable
* table
, int paddingY
, int& bottomY
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& availableSpace
, int style
)
9438 // This is called when the table's cell layout is otherwise complete.
9439 // For any cell with rowspan > 1, expand downwards into the row(s) below.
9441 // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
9442 // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.)
9443 const int rowCount
= table
->GetRowCount();
9444 const int colCount
= table
->GetColumnCount();
9446 rowTops
.Add(0, rowCount
+1);
9448 for (row
= 0; row
< rowCount
; ++row
)
9450 for (int column
= 0; column
< colCount
; ++column
)
9452 wxRichTextCell
* cell
= table
->GetCell(row
, column
);
9453 if (cell
&& cell
->IsShown())
9455 rowTops
[row
] = cell
->GetPosition().y
;
9460 rowTops
[rowCount
] = bottomY
+ paddingY
; // The table bottom, which was passed to us
9462 bool needsRelay
= false;
9464 for (row
= 0; row
< rowCount
-1; ++row
) // -1 as the bottom row can't rowspan
9466 for (int col
= 0; col
< colCount
; ++col
)
9468 wxRichTextCell
* cell
= table
->GetCell(row
, col
);
9469 if (cell
&& cell
->IsShown())
9471 int span
= cell
->GetRowspan();
9474 span
= wxMin(span
, rowCount
-row
); // Don't try to span below the table!
9478 int availableHeight
= rowTops
[row
+span
] - rowTops
[row
] - paddingY
;
9479 wxSize newSize
= wxSize(cell
->GetCachedSize().GetWidth(), availableHeight
);
9480 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), newSize
);
9481 cell
->Invalidate(wxRICHTEXT_ALL
);
9482 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9483 // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
9484 int overhang
= cell
->GetCachedSize().GetHeight() - availableHeight
;
9485 cell
->SetCachedSize(newSize
);
9489 // There are 3 things to get right:
9490 // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
9491 // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
9492 // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
9493 // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
9494 int deltaY
= overhang
/ span
;
9495 int spare
= overhang
% span
;
9497 // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
9498 // This is achieved by increasing the value stored in the following row's rowTops
9499 for (int spannedRows
= 0; spannedRows
< span
; ++spannedRows
)
9501 rowTops
[row
+spannedRows
+1] += ((deltaY
* (spannedRows
+1)) + (spannedRows
== 0 ? spare
:0));
9504 // Any rows below the span need shifting down
9505 for (int rowsBelow
= row
+ span
+1; rowsBelow
<= rowCount
; ++rowsBelow
)
9507 rowTops
[rowsBelow
] += overhang
;
9520 // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
9521 for (row
= 0; row
< rowCount
; ++row
)
9523 for (int col
= 0; col
< colCount
; ++col
)
9525 wxRichTextCell
* cell
= table
->GetCell(row
, col
);
9526 if (cell
&& cell
->IsShown())
9528 wxPoint
position(cell
->GetPosition().x
, rowTops
[row
]);
9530 // GetRowspan() will usually return 1, but may be greater
9531 wxSize
size(cell
->GetCachedSize().GetWidth(), rowTops
[row
+ cell
->GetRowspan()] - rowTops
[row
] - paddingY
);
9533 wxRect availableCellSpace
= wxRect(position
, size
);
9534 cell
->Invalidate(wxRICHTEXT_ALL
);
9535 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9536 cell
->SetCachedSize(size
);
9540 bottomY
= rowTops
[rowCount
] - paddingY
;
9544 // Lays the object out. rect is the space available for layout. Often it will
9545 // be the specified overall space for this object, if trying to constrain
9546 // layout to a particular size, or it could be the total space available in the
9547 // parent. rect is the overall size, so we must subtract margins and padding.
9548 // to get the actual available space.
9549 bool wxRichTextTable
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9551 SetPosition(rect
.GetPosition());
9553 // The meaty bit. Calculate sizes of all cells and rows. Try to use
9554 // minimum size if within alloted size, then divide up remaining size
9555 // between rows/cols.
9558 wxRichTextBuffer
* buffer
= GetBuffer();
9559 if (buffer
) scale
= buffer
->GetScale();
9561 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9562 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9564 wxRichTextAttr
attr(GetAttributes());
9565 context
.ApplyVirtualAttributes(attr
, this);
9567 bool tableHasPercentWidth
= (attr
.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
);
9568 // If we have no fixed table size, and assuming we're not pushed for
9569 // space, then we don't have to try to stretch the table to fit the contents.
9570 bool stretchToFitTableWidth
= tableHasPercentWidth
;
9572 int tableWidth
= rect
.width
;
9573 if (attr
.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth
)
9575 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9577 // Fixed table width, so we do want to stretch columns out if necessary.
9578 stretchToFitTableWidth
= true;
9580 // Shouldn't be able to exceed the size passed to this function
9581 tableWidth
= wxMin(rect
.width
, tableWidth
);
9584 // Get internal padding
9585 int paddingLeft
= 0, paddingTop
= 0;
9586 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9587 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9588 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9589 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9591 // Assume that left and top padding are also used for inter-cell padding.
9592 int paddingX
= paddingLeft
;
9593 int paddingY
= paddingTop
;
9595 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9596 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9598 // Internal table width - the area for content
9599 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9601 int rowCount
= m_cells
.GetCount();
9602 if (m_colCount
== 0 || rowCount
== 0)
9604 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9605 SetCachedSize(overallRect
.GetSize());
9607 // Zero content size
9608 SetMinSize(overallRect
.GetSize());
9609 SetMaxSize(GetMinSize());
9613 // The final calculated widths
9614 wxArrayInt colWidths
;
9615 colWidths
.Add(0, m_colCount
);
9617 wxArrayInt absoluteColWidths
;
9618 absoluteColWidths
.Add(0, m_colCount
);
9620 wxArrayInt percentageColWidths
;
9621 percentageColWidths
.Add(0, m_colCount
);
9622 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9623 // These are only relevant when the first column contains spanning information.
9624 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9625 wxArrayInt maxColWidths
;
9626 maxColWidths
.Add(0, m_colCount
);
9627 wxArrayInt minColWidths
;
9628 minColWidths
.Add(0, m_colCount
);
9630 wxSize
tableSize(tableWidth
, 0);
9634 for (i
= 0; i
< m_colCount
; i
++)
9636 absoluteColWidths
[i
] = 0;
9637 // absoluteColWidthsSpanning[i] = 0;
9638 percentageColWidths
[i
] = -1;
9639 // percentageColWidthsSpanning[i] = -1;
9641 maxColWidths
[i
] = 0;
9642 minColWidths
[i
] = 0;
9643 // columnSpans[i] = 1;
9646 // (0) Determine which cells are visible according to spans
9648 // __________________
9653 // |------------------|
9654 // |__________________| 4
9656 // To calculate cell visibility:
9657 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9658 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9659 // that cell, hide the cell.
9661 // We can also use this array to match the size of spanning cells to the grid. Or just do
9662 // this when we iterate through all cells.
9664 // 0.1: add spanning cells to an array
9665 wxRichTextRectArray rectArray
;
9666 for (j
= 0; j
< m_rowCount
; j
++)
9668 for (i
= 0; i
< m_colCount
; i
++)
9670 wxRichTextCell
* cell
= GetCell(j
, i
);
9671 int colSpan
= cell
->GetColspan();
9672 int rowSpan
= cell
->GetRowspan();
9673 if (colSpan
> 1 || rowSpan
> 1)
9675 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9679 // 0.2: find which cells are subsumed by a spanning cell
9680 for (j
= 0; j
< m_rowCount
; j
++)
9682 for (i
= 0; i
< m_colCount
; i
++)
9684 wxRichTextCell
* cell
= GetCell(j
, i
);
9685 if (rectArray
.GetCount() == 0)
9691 int colSpan
= cell
->GetColspan();
9692 int rowSpan
= cell
->GetRowspan();
9694 if (colSpan
> 1 || rowSpan
> 1)
9696 // Assume all spanning cells are shown
9702 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9704 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9716 // Find the first spanned cell in each row that spans the most columns and doesn't
9717 // overlap with a spanned cell starting at a previous column position.
9718 // This means we need to keep an array of rects so we can check. However
9719 // it does also mean that some spans simply may not be taken into account
9720 // where there are different spans happening on different rows. In these cases,
9721 // they will simply be as wide as their constituent columns.
9723 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9724 // the absolute or percentage width of each column.
9726 for (j
= 0; j
< m_rowCount
; j
++)
9728 // First get the overall margins so we can calculate percentage widths based on
9729 // the available content space for all cells on the row
9731 int overallRowContentMargin
= 0;
9732 int visibleCellCount
= 0;
9734 for (i
= 0; i
< m_colCount
; i
++)
9736 wxRichTextBox
* cell
= GetCell(j
, i
);
9737 if (cell
->IsShown())
9739 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9740 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9742 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9743 visibleCellCount
++;
9747 // Add in inter-cell padding
9748 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9750 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9751 wxSize
rowTableSize(rowContentWidth
, 0);
9752 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9754 for (i
= 0; i
< m_colCount
; i
++)
9756 wxRichTextCell
* cell
= GetCell(j
, i
);
9757 if (cell
->IsShown())
9759 int colSpan
= cell
->GetColspan();
9761 // Lay out cell to find min/max widths
9762 cell
->Invalidate(wxRICHTEXT_ALL
);
9763 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9767 int absoluteCellWidth
= -1;
9768 int percentageCellWidth
= -1;
9770 // I think we need to calculate percentages from the internal table size,
9771 // minus the padding between cells which we'll need to calculate from the
9772 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9773 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9774 // so if we want to conform to that we'll need to add in the overall cell margins.
9775 // However, this will make it difficult to specify percentages that add up to
9776 // 100% and still fit within the table width.
9777 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9778 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9779 // If we're using internal content size for the width, we would calculate the
9780 // the overall cell width for n cells as:
9781 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9782 // + thisOverallCellMargin
9783 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9784 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9786 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9788 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9789 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9791 percentageCellWidth
= w
;
9795 absoluteCellWidth
= w
;
9797 // Override absolute width with minimum width if necessary
9798 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9799 absoluteCellWidth
= cell
->GetMinSize().x
;
9802 if (absoluteCellWidth
!= -1)
9804 if (absoluteCellWidth
> absoluteColWidths
[i
])
9805 absoluteColWidths
[i
] = absoluteCellWidth
;
9808 if (percentageCellWidth
!= -1)
9810 if (percentageCellWidth
> percentageColWidths
[i
])
9811 percentageColWidths
[i
] = percentageCellWidth
;
9814 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9815 minColWidths
[i
] = cell
->GetMinSize().x
;
9816 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9817 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9823 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9824 // TODO: simply merge this into (1).
9825 for (i
= 0; i
< m_colCount
; i
++)
9827 if (absoluteColWidths
[i
] > 0)
9829 colWidths
[i
] = absoluteColWidths
[i
];
9831 else if (percentageColWidths
[i
] > 0)
9833 colWidths
[i
] = percentageColWidths
[i
];
9835 // This is rubbish - we calculated the absolute widths from percentages, so
9836 // we can't do it again here.
9837 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9841 // (3) Process absolute or proportional widths of spanning columns,
9842 // now that we know what our fixed column widths are going to be.
9843 // Spanned cells will try to adjust columns so the span will fit.
9844 // Even existing fixed column widths can be expanded if necessary.
9845 // Actually, currently fixed columns widths aren't adjusted; instead,
9846 // the algorithm favours earlier rows and adjusts unspecified column widths
9847 // the first time only. After that, we can't know whether the column has been
9848 // specified explicitly or not. (We could make a note if necessary.)
9849 for (j
= 0; j
< m_rowCount
; j
++)
9851 // First get the overall margins so we can calculate percentage widths based on
9852 // the available content space for all cells on the row
9854 int overallRowContentMargin
= 0;
9855 int visibleCellCount
= 0;
9857 for (i
= 0; i
< m_colCount
; i
++)
9859 wxRichTextBox
* cell
= GetCell(j
, i
);
9860 if (cell
->IsShown())
9862 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9863 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9865 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9866 visibleCellCount
++;
9870 // Add in inter-cell padding
9871 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9873 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9874 wxSize
rowTableSize(rowContentWidth
, 0);
9875 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9877 for (i
= 0; i
< m_colCount
; i
++)
9879 wxRichTextCell
* cell
= GetCell(j
, i
);
9880 if (cell
->IsShown())
9882 int colSpan
= cell
->GetColspan();
9885 int spans
= wxMin(colSpan
, m_colCount
- i
);
9889 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9891 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9892 // Override absolute width with minimum width if necessary
9893 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9894 cellWidth
= cell
->GetMinSize().x
;
9898 // Do we want to do this? It's the only chance we get to
9899 // use the cell's min/max sizes, so we need to work out
9900 // how we're going to balance the unspecified spanning cell
9901 // width with the possibility more-constrained constituent cell widths.
9902 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9903 // don't want to constraint all the spanned columns to fit into this cell.
9904 // OK, let's say that if any of the constituent columns don't fit,
9905 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9906 // cells to the columns later.
9907 cellWidth
= cell
->GetMinSize().x
;
9908 if (cell
->GetMaxSize().x
> cellWidth
)
9909 cellWidth
= cell
->GetMaxSize().x
;
9912 // Subtract the padding between cells
9913 int spanningWidth
= cellWidth
;
9914 spanningWidth
-= paddingX
* (spans
-1);
9916 if (spanningWidth
> 0)
9918 // Now share the spanning width between columns within that span
9919 // TODO: take into account min widths of columns within the span
9920 int spanningWidthLeft
= spanningWidth
;
9921 int stretchColCount
= 0;
9922 for (k
= i
; k
< (i
+spans
); k
++)
9924 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9925 spanningWidthLeft
-= colWidths
[k
];
9929 // Now divide what's left between the remaining columns
9931 if (stretchColCount
> 0)
9932 colShare
= spanningWidthLeft
/ stretchColCount
;
9933 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9935 // If fixed-width columns are currently too big, then we'll later
9936 // stretch the spanned cell to fit.
9938 if (spanningWidthLeft
> 0)
9940 for (k
= i
; k
< (i
+spans
); k
++)
9942 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9944 int newWidth
= colShare
;
9945 if (k
== (i
+spans
-1))
9946 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9947 colWidths
[k
] = newWidth
;
9958 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9959 // TODO: take into account min widths of columns within the span
9960 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9961 int widthLeft
= tableWidthMinusPadding
;
9962 int stretchColCount
= 0;
9963 for (i
= 0; i
< m_colCount
; i
++)
9965 // Subtract min width from width left, then
9966 // add the colShare to the min width
9967 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9968 widthLeft
-= colWidths
[i
];
9971 if (minColWidths
[i
] > 0)
9972 widthLeft
-= minColWidths
[i
];
9978 // Now divide what's left between the remaining columns
9980 if (stretchColCount
> 0)
9981 colShare
= widthLeft
/ stretchColCount
;
9982 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9984 // Check we don't have enough space, in which case shrink all columns, overriding
9985 // any absolute/proportional widths
9986 // TODO: actually we would like to divide up the shrinkage according to size.
9987 // How do we calculate the proportions that will achieve this?
9988 // Could first choose an arbitrary value for stretching cells, and then calculate
9989 // factors to multiply each width by.
9990 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9991 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9993 colShare
= tableWidthMinusPadding
/ m_colCount
;
9994 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9995 for (i
= 0; i
< m_colCount
; i
++)
9998 minColWidths
[i
] = 0;
10002 // We have to adjust the columns if either we need to shrink the
10003 // table to fit the parent/table width, or we explicitly set the
10004 // table width and need to stretch out the table.
10005 if (widthLeft
< 0 || stretchToFitTableWidth
)
10007 for (i
= 0; i
< m_colCount
; i
++)
10009 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
10011 if (minColWidths
[i
] > 0)
10012 colWidths
[i
] = minColWidths
[i
] + colShare
;
10014 colWidths
[i
] = colShare
;
10015 if (i
== (m_colCount
-1))
10016 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
10021 // TODO: if spanned cells have no specified or max width, make them the
10022 // as big as the columns they span. Do this for all spanned cells in all
10023 // rows, of course. Size any spanned cells left over at the end - even if they
10024 // have width > 0, make sure they're limited to the appropriate column edge.
10028 Sort out confusion between content width
10029 and overall width later. For now, assume we specify overall width.
10031 So, now we've laid out the table to fit into the given space
10032 and have used specified widths and minimum widths.
10034 Now we need to consider how we will try to take maximum width into account.
10038 // (??) TODO: take max width into account
10040 // (6) Lay out all cells again with the current values
10043 int y
= availableSpace
.y
;
10044 for (j
= 0; j
< m_rowCount
; j
++)
10046 int x
= availableSpace
.x
; // TODO: take into account centering etc.
10047 int maxCellHeight
= 0;
10048 int maxSpecifiedCellHeight
= 0;
10050 wxArrayInt actualWidths
;
10051 actualWidths
.Add(0, m_colCount
);
10053 wxTextAttrDimensionConverter
converter(dc
, scale
);
10054 for (i
= 0; i
< m_colCount
; i
++)
10056 wxRichTextCell
* cell
= GetCell(j
, i
);
10057 if (cell
->IsShown())
10059 // Get max specified cell height
10060 // Don't handle percentages for height
10061 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
10063 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
10064 if (h
> maxSpecifiedCellHeight
)
10065 maxSpecifiedCellHeight
= h
;
10068 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
10070 int colSpan
= cell
->GetColspan();
10071 wxRect availableCellSpace
;
10073 // Take into account spans
10076 // Calculate the size of this spanning cell from its constituent columns
10078 int spans
= wxMin(colSpan
, m_colCount
- i
);
10079 for (k
= i
; k
< (i
+spans
); k
++)
10083 xx
+= colWidths
[k
];
10085 availableCellSpace
= wxRect(x
, y
, xx
, -1);
10088 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
10090 // Store actual width so we can force cell to be the appropriate width on the final loop
10091 actualWidths
[i
] = availableCellSpace
.GetWidth();
10093 // We now need to shift right by the width of any rowspanning cells above-left of us
10094 int deltaX
= GetRowspanDisplacement(this, j
, i
, paddingX
, colWidths
);
10095 availableCellSpace
.SetX(availableCellSpace
.GetX() + deltaX
);
10098 cell
->Invalidate(wxRICHTEXT_ALL
);
10099 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
10101 // TODO: use GetCachedSize().x to compute 'natural' size
10103 x
+= (availableCellSpace
.GetWidth() + paddingX
);
10104 if ((cell
->GetCachedSize().y
> maxCellHeight
) && (cell
->GetRowspan() < 2))
10105 maxCellHeight
= cell
->GetCachedSize().y
;
10110 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
10112 for (i
= 0; i
< m_colCount
; i
++)
10114 wxRichTextCell
* cell
= GetCell(j
, i
);
10115 if (cell
->IsShown())
10117 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
10118 // Lay out cell with new height
10119 cell
->Invalidate(wxRICHTEXT_ALL
);
10120 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
10122 // Make sure the cell size really is the appropriate size,
10123 // not the calculated box size
10124 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
10126 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
10130 y
+= maxCellHeight
;
10131 if (j
< (m_rowCount
-1))
10135 // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
10136 ExpandCellsWithRowspan(this, paddingY
, y
, dc
, context
, availableSpace
, style
);
10138 // We need to add back the margins etc.
10140 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10141 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
10142 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10143 SetCachedSize(marginRect
.GetSize());
10146 // TODO: calculate max size
10148 SetMaxSize(GetCachedSize());
10151 // TODO: calculate min size
10153 SetMinSize(GetCachedSize());
10156 // TODO: currently we use either a fixed table width or the parent's size.
10157 // We also want to be able to calculate the table width from its content,
10158 // whether using fixed column widths or cell content min/max width.
10159 // Probably need a boolean flag to say whether we need to stretch cells
10160 // to fit the table width, or to simply use min/max cell widths. The
10161 // trouble with this is that if cell widths are not specified, they
10162 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
10163 // Anyway, ignoring that problem, we probably need to factor layout into a function
10164 // that can can calculate the maximum unconstrained layout in case table size is
10165 // not specified. Then LayoutToBestSize() can choose to use either parent size to
10166 // constrain Layout(), or the previously-calculated max size to constraint layout.
10171 // Finds the absolute position and row height for the given character position
10172 bool wxRichTextTable
::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
10174 wxRichTextCell
* child
= GetCell(index
+1);
10177 // Find the position at the start of the child cell, since the table doesn't
10178 // have any caret position of its own.
10179 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
10185 // Get the cell at the given character position (in the range of the table).
10186 wxRichTextCell
* wxRichTextTable
::GetCell(long pos
) const
10188 int row
= 0, col
= 0;
10189 if (GetCellRowColumnPosition(pos
, row
, col
))
10191 return GetCell(row
, col
);
10197 // Get the row/column for a given character position
10198 bool wxRichTextTable
::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
10200 if (m_colCount
== 0 || m_rowCount
== 0)
10203 row
= (int) (pos
/ m_colCount
);
10204 col
= pos
- (row
* m_colCount
);
10206 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
10208 if (row
< m_rowCount
&& col
< m_colCount
)
10214 // Calculate range, taking row/cell ordering into account instead of relying
10215 // on list ordering.
10216 void wxRichTextTable
::CalculateRange(long start
, long& end
)
10218 long current
= start
;
10219 long lastEnd
= current
;
10228 for (i
= 0; i
< m_rowCount
; i
++)
10230 for (j
= 0; j
< m_colCount
; j
++)
10232 wxRichTextCell
* child
= GetCell(i
, j
);
10237 child
->CalculateRange(current
, childEnd
);
10239 lastEnd
= childEnd
;
10240 current
= childEnd
+ 1;
10245 // A top-level object always has a range of size 1,
10246 // because its children don't count at this level.
10248 m_range
.SetRange(start
, start
);
10250 // An object with no children has zero length
10251 if (m_children
.GetCount() == 0)
10253 m_ownRange
.SetRange(0, lastEnd
);
10256 // Gets the range size.
10257 bool wxRichTextTable
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
10259 return wxRichTextBox
::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10262 // Deletes content in the given range.
10263 bool wxRichTextTable
::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10265 // TODO: implement deletion of cells
10269 // Gets any text in this object for the given range.
10270 wxString wxRichTextTable
::GetTextForRange(const wxRichTextRange
& range
) const
10272 return wxRichTextBox
::GetTextForRange(range
);
10275 // Copies this object.
10276 void wxRichTextTable
::Copy(const wxRichTextTable
& obj
)
10278 wxRichTextBox
::Copy(obj
);
10282 m_rowCount
= obj
.m_rowCount
;
10283 m_colCount
= obj
.m_colCount
;
10285 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10288 for (i
= 0; i
< m_rowCount
; i
++)
10290 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10291 for (j
= 0; j
< m_colCount
; j
++)
10293 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10296 colArray
.Add(cell
);
10301 void wxRichTextTable
::ClearTable()
10309 bool wxRichTextTable
::CreateTable(int rows
, int cols
)
10313 wxRichTextAttr cellattr
;
10314 cellattr
.SetTextColour(GetBasicStyle().GetTextColour());
10319 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10322 for (i
= 0; i
< rows
; i
++)
10324 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10325 for (j
= 0; j
< cols
; j
++)
10327 wxRichTextCell
* cell
= new wxRichTextCell
;
10328 cell
->GetAttributes() = cellattr
;
10331 cell
->AddParagraph(wxEmptyString
);
10333 colArray
.Add(cell
);
10340 wxRichTextCell
* wxRichTextTable
::GetCell(int row
, int col
) const
10342 wxASSERT(row
< m_rowCount
);
10343 wxASSERT(col
< m_colCount
);
10345 if (row
< m_rowCount
&& col
< m_colCount
)
10347 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10348 wxRichTextObject
* obj
= colArray
[col
];
10349 return wxDynamicCast(obj
, wxRichTextCell
);
10355 // Returns a selection object specifying the selections between start and end character positions.
10356 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10357 wxRichTextSelection wxRichTextTable
::GetSelection(long start
, long end
) const
10359 wxRichTextSelection selection
;
10360 selection
.SetContainer((wxRichTextTable
*) this);
10369 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10371 if (end
>= (m_colCount
* m_rowCount
))
10374 // We need to find the rectangle of cells that is described by the rectangle
10375 // with start, end as the diagonal. Make sure we don't add cells that are
10376 // not currenty visible because they are overlapped by spanning cells.
10378 --------------------------
10379 | 0 | 1 | 2 | 3 | 4 |
10380 --------------------------
10381 | 5 | 6 | 7 | 8 | 9 |
10382 --------------------------
10383 | 10 | 11 | 12 | 13 | 14 |
10384 --------------------------
10385 | 15 | 16 | 17 | 18 | 19 |
10386 --------------------------
10388 Let's say we select 6 -> 18.
10390 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10391 which is left and which is right.
10393 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10395 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10401 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10402 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10404 int topRow
= int(start
/m_colCount
);
10405 int bottomRow
= int(end
/m_colCount
);
10407 if (leftCol
> rightCol
)
10409 int tmp
= rightCol
;
10410 rightCol
= leftCol
;
10414 if (topRow
> bottomRow
)
10416 int tmp
= bottomRow
;
10417 bottomRow
= topRow
;
10422 for (i
= topRow
; i
<= bottomRow
; i
++)
10424 for (j
= leftCol
; j
<= rightCol
; j
++)
10426 wxRichTextCell
* cell
= GetCell(i
, j
);
10427 if (cell
&& cell
->IsShown())
10428 selection
.Add(cell
->GetRange());
10435 // Sets the attributes for the cells specified by the selection.
10436 bool wxRichTextTable
::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10438 if (selection
.GetContainer() != this)
10441 wxRichTextBuffer
* buffer
= GetBuffer();
10442 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10443 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10446 buffer
->BeginBatchUndo(_("Set Cell Style"));
10448 wxRichTextObjectList
::compatibility_iterator node
= m_children
.GetFirst();
10451 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10452 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10453 SetStyle(cell
, style
, flags
);
10454 node
= node
->GetNext();
10457 // Do action, or delay it until end of batch.
10459 buffer
->EndBatchUndo();
10464 wxPosition wxRichTextTable
::GetFocusedCell() const
10466 wxPosition
position(-1, -1);
10467 const wxRichTextObject
* focus
= GetBuffer()->GetRichTextCtrl()->GetFocusObject();
10469 for (int row
= 0; row
< GetRowCount(); ++row
)
10471 for (int col
= 0; col
< GetColumnCount(); ++col
)
10473 if (GetCell(row
, col
) == focus
)
10475 position
.SetRow(row
);
10476 position
.SetCol(col
);
10485 bool wxRichTextTable
::DeleteRows(int startRow
, int noRows
)
10487 wxASSERT((startRow
+ noRows
) <= m_rowCount
);
10488 if ((startRow
+ noRows
) > m_rowCount
)
10491 wxCHECK_MSG(noRows
!= m_rowCount
, false, "Trying to delete all the cells in a table");
10493 wxRichTextBuffer
* buffer
= GetBuffer();
10494 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
10496 wxPosition position
= GetFocusedCell();
10497 int focusCol
= position
.GetCol();
10498 int focusRow
= position
.GetRow();
10499 if (focusRow
>= startRow
&& focusRow
< (startRow
+noRows
))
10501 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10502 if ((startRow
+ noRows
) < m_rowCount
)
10504 // There are more rows after the one(s) to be deleted, so set focus in the first of them
10505 rtc
->SetFocusObject(GetCell(startRow
+ noRows
, focusCol
));
10509 // Otherwise set focus in the preceding row
10510 rtc
->SetFocusObject(GetCell(startRow
- 1, focusCol
));
10514 wxRichTextAction
* action
= NULL
;
10515 wxRichTextTable
* clone
= NULL
;
10516 if (!rtc
->SuppressingUndo())
10518 // Create a clone containing the current state of the table. It will be used to Undo the action
10519 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10520 clone
->SetParent(GetParent());
10521 action
= new wxRichTextAction(NULL
, _("Delete row"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, rtc
);
10522 action
->SetObject(this);
10523 action
->SetPosition(GetRange().GetStart());
10527 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10529 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10530 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10532 wxRichTextObject
* cell
= colArray
[j
];
10533 RemoveChild(cell
, true);
10536 // Keep deleting at the same position, since we move all
10538 m_cells
.RemoveAt(startRow
);
10541 m_rowCount
= m_rowCount
- noRows
;
10543 if (!rtc
->SuppressingUndo())
10545 buffer
->SubmitAction(action
);
10546 // Finally store the original-state clone; doing so earlier would cause various failures
10547 action
->StoreObject(clone
);
10553 bool wxRichTextTable
::DeleteColumns(int startCol
, int noCols
)
10555 wxASSERT((startCol
+ noCols
) <= m_colCount
);
10556 if ((startCol
+ noCols
) > m_colCount
)
10559 wxCHECK_MSG(noCols
!= m_colCount
, false, "Trying to delete all the cells in a table");
10561 wxRichTextBuffer
* buffer
= GetBuffer();
10562 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
10564 wxPosition position
= GetFocusedCell();
10565 int focusCol
= position
.GetCol();
10566 int focusRow
= position
.GetRow();
10567 if (focusCol
>= startCol
&& focusCol
< (startCol
+noCols
))
10569 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10570 if ((startCol
+ noCols
) < m_colCount
)
10572 // There are more columns after the one(s) to be deleted, so set focus in the first of them
10573 rtc
->SetFocusObject(GetCell(focusRow
, startCol
+ noCols
));
10577 // Otherwise set focus in the preceding column
10578 rtc
->SetFocusObject(GetCell(focusRow
, startCol
- 1));
10582 wxRichTextAction
* action
= NULL
;
10583 wxRichTextTable
* clone
= NULL
;
10584 if (!rtc
->SuppressingUndo())
10586 // Create a clone containing the current state of the table. It will be used to Undo the action
10587 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10588 clone
->SetParent(GetParent());
10589 action
= new wxRichTextAction(NULL
, _("Delete column"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, rtc
);
10590 action
->SetObject(this);
10591 action
->SetPosition(GetRange().GetStart());
10594 bool deleteRows
= (noCols
== m_colCount
);
10597 for (i
= 0; i
< m_rowCount
; i
++)
10599 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows ?
0 : i
];
10600 for (j
= 0; j
< noCols
; j
++)
10602 wxRichTextObject
* cell
= colArray
[startCol
];
10603 RemoveChild(cell
, true);
10604 colArray
.RemoveAt(startCol
);
10608 m_cells
.RemoveAt(0);
10613 m_colCount
= m_colCount
- noCols
;
10615 if (!rtc
->SuppressingUndo())
10617 buffer
->SubmitAction(action
);
10618 // Finally store the original-state clone; doing so earlier would cause various failures
10619 action
->StoreObject(clone
);
10625 bool wxRichTextTable
::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10627 wxASSERT(startRow
<= m_rowCount
);
10628 if (startRow
> m_rowCount
)
10631 wxRichTextBuffer
* buffer
= GetBuffer();
10632 wxRichTextAction
* action
= NULL
;
10633 wxRichTextTable
* clone
= NULL
;
10634 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10636 // Create a clone containing the current state of the table. It will be used to Undo the action
10637 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10638 clone
->SetParent(GetParent());
10639 action
= new wxRichTextAction(NULL
, _("Add row"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, buffer
->GetRichTextCtrl());
10640 action
->SetObject(this);
10641 action
->SetPosition(GetRange().GetStart());
10645 for (i
= 0; i
< noRows
; i
++)
10648 if (startRow
== m_rowCount
)
10650 m_cells
.Add(wxRichTextObjectPtrArray());
10651 idx
= m_cells
.GetCount() - 1;
10655 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10659 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10660 for (j
= 0; j
< m_colCount
; j
++)
10662 wxRichTextCell
* cell
= new wxRichTextCell
;
10663 cell
->GetAttributes() = attr
;
10666 cell
->AddParagraph(wxEmptyString
);
10667 colArray
.Add(cell
);
10671 m_rowCount
= m_rowCount
+ noRows
;
10673 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10675 buffer
->SubmitAction(action
);
10676 // Finally store the original-state clone; doing so earlier would cause various failures
10677 action
->StoreObject(clone
);
10683 bool wxRichTextTable
::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10685 wxASSERT(startCol
<= m_colCount
);
10686 if (startCol
> m_colCount
)
10689 wxRichTextBuffer
* buffer
= GetBuffer();
10690 wxRichTextAction
* action
= NULL
;
10691 wxRichTextTable
* clone
= NULL
;
10692 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10694 // Create a clone containing the current state of the table. It will be used to Undo the action
10695 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10696 clone
->SetParent(GetParent());
10697 action
= new wxRichTextAction(NULL
, _("Add column"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, buffer
->GetRichTextCtrl());
10698 action
->SetObject(this);
10699 action
->SetPosition(GetRange().GetStart());
10703 for (i
= 0; i
< m_rowCount
; i
++)
10705 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10706 for (j
= 0; j
< noCols
; j
++)
10708 wxRichTextCell
* cell
= new wxRichTextCell
;
10709 cell
->GetAttributes() = attr
;
10712 cell
->AddParagraph(wxEmptyString
);
10714 if (startCol
== m_colCount
)
10715 colArray
.Add(cell
);
10717 colArray
.Insert(cell
, startCol
+j
);
10721 m_colCount
= m_colCount
+ noCols
;
10723 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10725 buffer
->SubmitAction(action
);
10726 // Finally store the original-state clone; doing so earlier would cause various failures
10727 action
->StoreObject(clone
);
10733 // Edit properties via a GUI
10734 bool wxRichTextTable
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10736 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10737 boxDlg
.SetAttributes(GetAttributes());
10739 if (boxDlg
.ShowModal() == wxID_OK
)
10741 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10749 * Module to initialise and clean up handlers
10752 class wxRichTextModule
: public wxModule
10754 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10756 wxRichTextModule() {}
10759 wxRichTextBuffer
::SetRenderer(new wxRichTextStdRenderer
);
10760 wxRichTextBuffer
::InitStandardHandlers();
10761 wxRichTextParagraph
::InitDefaultTabs();
10763 wxRichTextXMLHandler
::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10764 wxRichTextXMLHandler
::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10765 wxRichTextXMLHandler
::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10766 wxRichTextXMLHandler
::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10767 wxRichTextXMLHandler
::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10768 wxRichTextXMLHandler
::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10769 wxRichTextXMLHandler
::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10770 wxRichTextXMLHandler
::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10771 wxRichTextXMLHandler
::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10777 wxRichTextBuffer
::CleanUpHandlers();
10778 wxRichTextBuffer
::CleanUpDrawingHandlers();
10779 wxRichTextBuffer
::CleanUpFieldTypes();
10780 wxRichTextXMLHandler
::ClearNodeToClassMap();
10781 wxRichTextDecimalToRoman(-1);
10782 wxRichTextParagraph
::ClearDefaultTabs();
10783 wxRichTextCtrl
::ClearAvailableFontNames();
10784 wxRichTextBuffer
::SetRenderer(NULL
);
10788 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10791 // If the richtext lib is dynamically loaded after the app has already started
10792 // (such as from wxPython) then the built-in module system will not init this
10793 // module. Provide this function to do it manually.
10794 void wxRichTextModuleInit()
10796 wxModule
* module
= new wxRichTextModule
;
10797 wxModule
::RegisterModule(module
);
10798 wxModule
::InitializeModules();
10803 * Commands for undo/redo
10807 wxRichTextCommand
::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10808 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10810 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10813 wxRichTextCommand
::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10817 wxRichTextCommand
::~wxRichTextCommand()
10822 void wxRichTextCommand
::AddAction(wxRichTextAction
* action
)
10824 if (!m_actions
.Member(action
))
10825 m_actions
.Append(action
);
10828 bool wxRichTextCommand
::Do()
10830 for (wxList
::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10832 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10839 bool wxRichTextCommand
::Undo()
10841 for (wxList
::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10843 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10850 void wxRichTextCommand
::ClearActions()
10852 WX_CLEAR_LIST(wxList
, m_actions
);
10856 * Individual action
10860 wxRichTextAction
::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10861 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10862 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10866 m_containerAddress
.Create(buffer
, container
);
10867 m_ignoreThis
= ignoreFirstTime
;
10872 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10873 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10875 cmd
->AddAction(this);
10878 wxRichTextAction
::~wxRichTextAction()
10884 // Returns the container that this action refers to, using the container address and top-level buffer.
10885 wxRichTextParagraphLayoutBox
* wxRichTextAction
::GetContainer() const
10887 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10892 void wxRichTextAction
::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10894 // Store a list of line start character and y positions so we can figure out which area
10895 // we need to refresh
10897 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10898 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10899 wxASSERT(container
!= NULL
);
10903 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10904 // If we had several actions, which only invalidate and leave layout until the
10905 // paint handler is called, then this might not be true. So we may need to switch
10906 // optimisation on only when we're simply adding text and not simultaneously
10907 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10908 // first, but of course this means we'll be doing it twice.
10909 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10911 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10912 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10913 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10915 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10916 wxRichTextObjectList
::compatibility_iterator node
= container
->GetChildren().Find(para
);
10919 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10920 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
10923 wxRichTextLine
* line
= node2
->GetData();
10924 wxPoint pt
= line
->GetAbsolutePosition();
10925 wxRichTextRange range
= line
->GetAbsoluteRange();
10929 node2
= wxRichTextLineList
::compatibility_iterator();
10930 node
= wxRichTextObjectList
::compatibility_iterator();
10932 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10934 optimizationLineCharPositions
.Add(range
.GetStart());
10935 optimizationLineYPositions
.Add(pt
.y
);
10939 node2
= node2
->GetNext();
10943 node
= node
->GetNext();
10949 bool wxRichTextAction
::Do()
10951 m_buffer
->Modify(true);
10953 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10954 wxASSERT(container
!= NULL
);
10960 case wxRICHTEXT_INSERT
:
10962 // Store a list of line start character and y positions so we can figure out which area
10963 // we need to refresh
10964 wxArrayInt optimizationLineCharPositions
;
10965 wxArrayInt optimizationLineYPositions
;
10967 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10968 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10971 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10972 container
->UpdateRanges();
10974 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10975 // Layout() would stop prematurely at the top level.
10976 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10978 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10980 // Character position to caret position
10981 newCaretPosition
--;
10983 // Don't take into account the last newline
10984 if (m_newParagraphs
.GetPartialParagraph())
10985 newCaretPosition
--;
10987 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10989 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10990 if (p
->GetRange().GetLength() == 1)
10991 newCaretPosition
--;
10994 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10996 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10998 wxRichTextEvent
cmdEvent(
10999 wxEVT_RICHTEXT_CONTENT_INSERTED
,
11000 m_ctrl ? m_ctrl
->GetId() : -1);
11001 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11002 cmdEvent
.SetRange(GetRange());
11003 cmdEvent
.SetPosition(GetRange().GetStart());
11004 cmdEvent
.SetContainer(container
);
11006 m_buffer
->SendEvent(cmdEvent
);
11010 case wxRICHTEXT_DELETE
:
11012 wxArrayInt optimizationLineCharPositions
;
11013 wxArrayInt optimizationLineYPositions
;
11015 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11016 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11019 container
->DeleteRange(GetRange());
11020 container
->UpdateRanges();
11021 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11022 // Layout() would stop prematurely at the top level.
11023 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11025 long caretPos
= GetRange().GetStart()-1;
11026 if (caretPos
>= container
->GetOwnRange().GetEnd())
11029 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
11031 wxRichTextEvent
cmdEvent(
11032 wxEVT_RICHTEXT_CONTENT_DELETED
,
11033 m_ctrl ? m_ctrl
->GetId() : -1);
11034 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11035 cmdEvent
.SetRange(GetRange());
11036 cmdEvent
.SetPosition(GetRange().GetStart());
11037 cmdEvent
.SetContainer(container
);
11039 m_buffer
->SendEvent(cmdEvent
);
11043 case wxRICHTEXT_CHANGE_STYLE
:
11044 case wxRICHTEXT_CHANGE_PROPERTIES
:
11046 ApplyParagraphs(GetNewParagraphs());
11048 // Invalidate the whole buffer if there were floating objects
11049 if (wxRichTextBuffer
::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11050 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11053 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11054 // Layout() would stop prematurely at the top level.
11055 container
->InvalidateHierarchy(GetRange());
11058 UpdateAppearance(GetPosition());
11060 wxRichTextEvent
cmdEvent(
11061 m_cmdId
== wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED
: wxEVT_RICHTEXT_PROPERTIES_CHANGED
,
11062 m_ctrl ? m_ctrl
->GetId() : -1);
11063 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11064 cmdEvent
.SetRange(GetRange());
11065 cmdEvent
.SetPosition(GetRange().GetStart());
11066 cmdEvent
.SetContainer(container
);
11068 m_buffer
->SendEvent(cmdEvent
);
11072 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
11074 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
11077 wxRichTextAttr oldAttr
= obj
->GetAttributes();
11078 obj
->GetAttributes() = m_attributes
;
11079 m_attributes
= oldAttr
;
11082 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11083 // Layout() would stop prematurely at the top level.
11084 // Invalidate the whole buffer if there were floating objects
11085 if (wxRichTextBuffer
::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11086 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11088 container
->InvalidateHierarchy(GetRange());
11090 UpdateAppearance(GetPosition());
11092 wxRichTextEvent
cmdEvent(
11093 wxEVT_RICHTEXT_STYLE_CHANGED
,
11094 m_ctrl ? m_ctrl
->GetId() : -1);
11095 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11096 cmdEvent
.SetRange(GetRange());
11097 cmdEvent
.SetPosition(GetRange().GetStart());
11098 cmdEvent
.SetContainer(container
);
11100 m_buffer
->SendEvent(cmdEvent
);
11104 case wxRICHTEXT_CHANGE_OBJECT
:
11106 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
11107 if (obj
&& m_object
&& m_ctrl
)
11109 // The plan is to swap the current object with the stored, previous-state, clone
11110 // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
11111 // so use the parent paragraph
11112 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
11113 wxCHECK_MSG(para
, false, "Invalid parent paragraph");
11115 // The stored object, m_object, may have a stale parent paragraph. This would cause
11116 // a crash during layout, so use obj's parent para, which should be the correct one.
11117 // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
11118 // or to set obj's parent there before returning)
11119 m_object
->SetParent(para
);
11121 wxRichTextObjectList
::compatibility_iterator node
= para
->GetChildren().Find(obj
);
11124 wxRichTextObject
* obj
= node
->GetData();
11125 node
->SetData(m_object
);
11130 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11131 // Layout() would stop prematurely at the top level.
11132 // Invalidate the whole buffer if there were floating objects
11133 if (wxRichTextBuffer
::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11134 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11136 container
->InvalidateHierarchy(GetRange());
11138 UpdateAppearance(GetPosition());
11140 // TODO: send new kind of modification event
11151 bool wxRichTextAction
::Undo()
11153 m_buffer
->Modify(true);
11155 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11156 wxASSERT(container
!= NULL
);
11162 case wxRICHTEXT_INSERT
:
11164 wxArrayInt optimizationLineCharPositions
;
11165 wxArrayInt optimizationLineYPositions
;
11167 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11168 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11171 container
->DeleteRange(GetRange());
11172 container
->UpdateRanges();
11174 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11175 // Layout() would stop prematurely at the top level.
11176 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11178 long newCaretPosition
= GetPosition() - 1;
11180 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
11182 wxRichTextEvent
cmdEvent(
11183 wxEVT_RICHTEXT_CONTENT_DELETED
,
11184 m_ctrl ? m_ctrl
->GetId() : -1);
11185 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11186 cmdEvent
.SetRange(GetRange());
11187 cmdEvent
.SetPosition(GetRange().GetStart());
11188 cmdEvent
.SetContainer(container
);
11190 m_buffer
->SendEvent(cmdEvent
);
11194 case wxRICHTEXT_DELETE
:
11196 wxArrayInt optimizationLineCharPositions
;
11197 wxArrayInt optimizationLineYPositions
;
11199 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11200 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11203 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
11204 container
->UpdateRanges();
11206 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11207 // Layout() would stop prematurely at the top level.
11208 container
->InvalidateHierarchy(GetRange());
11210 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
11212 wxRichTextEvent
cmdEvent(
11213 wxEVT_RICHTEXT_CONTENT_INSERTED
,
11214 m_ctrl ? m_ctrl
->GetId() : -1);
11215 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11216 cmdEvent
.SetRange(GetRange());
11217 cmdEvent
.SetPosition(GetRange().GetStart());
11218 cmdEvent
.SetContainer(container
);
11220 m_buffer
->SendEvent(cmdEvent
);
11224 case wxRICHTEXT_CHANGE_STYLE
:
11225 case wxRICHTEXT_CHANGE_PROPERTIES
:
11227 ApplyParagraphs(GetOldParagraphs());
11228 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11229 // Layout() would stop prematurely at the top level.
11230 container
->InvalidateHierarchy(GetRange());
11232 UpdateAppearance(GetPosition());
11234 wxRichTextEvent
cmdEvent(
11235 m_cmdId
== wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED
: wxEVT_RICHTEXT_PROPERTIES_CHANGED
,
11236 m_ctrl ? m_ctrl
->GetId() : -1);
11237 cmdEvent
.SetEventObject(m_ctrl ?
(wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11238 cmdEvent
.SetRange(GetRange());
11239 cmdEvent
.SetPosition(GetRange().GetStart());
11240 cmdEvent
.SetContainer(container
);
11242 m_buffer
->SendEvent(cmdEvent
);
11246 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
11247 case wxRICHTEXT_CHANGE_OBJECT
:
11258 /// Update the control appearance
11259 void wxRichTextAction
::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
11261 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11262 wxASSERT(container
!= NULL
);
11268 m_ctrl
->SetFocusObject(container
);
11269 m_ctrl
->SetCaretPosition(caretPosition
);
11271 if (!m_ctrl
->IsFrozen())
11273 wxRect containerRect
= container
->GetRect();
11275 m_ctrl
->LayoutContent();
11277 // Refresh everything if there were floating objects or the container changed size
11278 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
11279 if ((wxRichTextBuffer
::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
11281 m_ctrl
->Refresh(false);
11285 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11286 // Find refresh rectangle if we are in a position to optimise refresh
11287 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
11291 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
11292 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
11294 // Start/end positions
11296 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11298 bool foundEnd
= false;
11300 // position offset - how many characters were inserted
11301 int positionOffset
= GetRange().GetLength();
11303 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
11304 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
11305 positionOffset
= - positionOffset
;
11307 // find the first line which is being drawn at the same position as it was
11308 // before. Since we're talking about a simple insertion, we can assume
11309 // that the rest of the window does not need to be redrawn.
11310 long pos
= GetRange().GetStart();
11312 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(pos
, false /* is not caret pos */);
11313 // Since we support floating layout, we should redraw the whole para instead of just
11314 // the first line touching the invalid range.
11317 // In case something was drawn above the paragraph,
11318 // such as a line break, allow a little extra.
11319 firstY
= para
->GetPosition().y
- 4;
11322 wxRichTextObjectList
::compatibility_iterator node
= container
->GetChildren().Find(para
);
11325 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
11326 wxRichTextLineList
::compatibility_iterator node2
= child
->GetLines().GetFirst();
11329 wxRichTextLine
* line
= node2
->GetData();
11330 wxPoint pt
= line
->GetAbsolutePosition();
11331 wxRichTextRange range
= line
->GetAbsoluteRange();
11333 // we want to find the first line that is in the same position
11334 // as before. This will mean we're at the end of the changed text.
11336 if (pt
.y
> lastY
) // going past the end of the window, no more info
11338 node2
= wxRichTextLineList
::compatibility_iterator();
11339 node
= wxRichTextObjectList
::compatibility_iterator();
11341 // Detect last line in the buffer
11342 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
11344 // If deleting text, make sure we refresh below as well as above
11345 if (positionOffset
>= 0)
11348 lastY
= pt
.y
+ line
->GetSize().y
;
11351 node2
= wxRichTextLineList
::compatibility_iterator();
11352 node
= wxRichTextObjectList
::compatibility_iterator();
11358 // search for this line being at the same position as before
11359 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
11361 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
11362 ((*optimizationLineYPositions
)[i
] == pt
.y
))
11364 // Stop, we're now the same as we were
11367 lastY
= pt
.y
+ line
->GetSize().y
;
11369 node2
= wxRichTextLineList
::compatibility_iterator();
11370 node
= wxRichTextObjectList
::compatibility_iterator();
11378 node2
= node2
->GetNext();
11382 node
= node
->GetNext();
11385 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
11387 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11389 // Convert to device coordinates
11390 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
11391 m_ctrl
->RefreshRect(rect
);
11395 m_ctrl
->Refresh(false);
11397 m_ctrl
->PositionCaret();
11399 // This causes styles to persist when doing programmatic
11400 // content creation except when Freeze/Thaw is used, so
11401 // disable this and check for the consequences.
11402 // m_ctrl->SetDefaultStyleToCursorStyle();
11404 if (sendUpdateEvent
)
11405 wxTextCtrl
::SendTextUpdatedEvent(m_ctrl
);
11410 /// Replace the buffer paragraphs with the new ones.
11411 void wxRichTextAction
::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11413 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11414 wxASSERT(container
!= NULL
);
11418 wxRichTextObjectList
::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11421 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11422 wxASSERT (para
!= NULL
);
11424 // We'll replace the existing paragraph by finding the paragraph at this position,
11425 // delete its node data, and setting a copy as the new node data.
11426 // TODO: make more efficient by simply swapping old and new paragraph objects.
11428 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11431 wxRichTextObjectList
::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11432 if (bufferParaNode
)
11434 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11435 newPara
->SetParent(container
);
11437 bufferParaNode
->SetData(newPara
);
11439 delete existingPara
;
11443 node
= node
->GetNext();
11450 * This stores beginning and end positions for a range of data.
11453 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11455 /// Limit this range to be within 'range'
11456 bool wxRichTextRange
::LimitTo(const wxRichTextRange
& range
)
11458 if (m_start
< range
.m_start
)
11459 m_start
= range
.m_start
;
11461 if (m_end
> range
.m_end
)
11462 m_end
= range
.m_end
;
11468 * wxRichTextImage implementation
11469 * This object represents an image.
11472 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11474 wxRichTextImage
::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11475 wxRichTextObject(parent
)
11478 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11480 SetAttributes(*charStyle
);
11483 wxRichTextImage
::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11484 wxRichTextObject(parent
)
11487 m_imageBlock
= imageBlock
;
11489 SetAttributes(*charStyle
);
11492 wxRichTextImage
::~wxRichTextImage()
11496 void wxRichTextImage
::Init()
11498 m_originalImageSize
= wxSize(-1, -1);
11501 /// Create a cached image at the required size
11502 bool wxRichTextImage
::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11504 if (!m_imageBlock
.IsOk())
11507 // If we have an original image size, use that to compute the cached bitmap size
11508 // instead of loading the image each time. This way we can avoid loading
11509 // the image so long as the new cached bitmap size hasn't changed.
11512 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11514 m_imageCache
= wxNullBitmap
;
11516 m_imageBlock
.Load(image
);
11520 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11523 int width
= m_originalImageSize
.GetWidth();
11524 int height
= m_originalImageSize
.GetHeight();
11526 int parentWidth
= 0;
11527 int parentHeight
= 0;
11530 int maxHeight
= -1;
11532 wxSize sz
= parentSize
;
11533 if (sz
== wxDefaultSize
)
11535 if (GetParent() && GetParent()->GetParent())
11536 sz
= GetParent()->GetParent()->GetCachedSize();
11539 if (sz
!= wxDefaultSize
)
11541 wxRichTextBuffer
* buffer
= GetBuffer();
11544 // Find the actual space available when margin is taken into account
11545 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11546 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11547 if (GetParent() && GetParent()->GetParent())
11549 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11550 sz
= contentRect
.GetSize();
11553 // Use a minimum size to stop images becoming very small
11554 parentWidth
= wxMax(100, sz
.GetWidth());
11555 parentHeight
= wxMax(100, sz
.GetHeight());
11557 if (buffer
->GetRichTextCtrl())
11558 // Start with a maximum width of the control size, even if not specified by the content,
11559 // to minimize the amount of picture overlapping the right-hand side
11560 maxWidth
= parentWidth
;
11564 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11566 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11567 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11568 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11569 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11570 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11571 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11574 // Limit to max width
11576 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11580 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11581 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11582 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11583 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11584 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11585 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11587 // If we already have a smaller max width due to the constraints of the control size,
11588 // don't use the larger max width.
11589 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11593 if (maxWidth
> 0 && width
> maxWidth
)
11596 // Preserve the aspect ratio
11597 if (width
!= m_originalImageSize
.GetWidth())
11598 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11600 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11602 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11603 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11604 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11605 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11606 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11607 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11609 // Preserve the aspect ratio
11610 if (height
!= m_originalImageSize
.GetHeight())
11611 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11614 // Limit to max height
11616 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11618 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11619 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11620 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11621 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11622 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11623 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11626 if (maxHeight
> 0 && height
> maxHeight
)
11628 height
= maxHeight
;
11630 // Preserve the aspect ratio
11631 if (height
!= m_originalImageSize
.GetHeight())
11632 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11635 // Prevent the use of zero size
11636 width
= wxMax(1, width
);
11637 height
= wxMax(1, height
);
11639 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11641 // Do nothing, we didn't need to change the image cache
11647 m_imageBlock
.Load(image
);
11652 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11653 m_imageCache
= wxBitmap(image
);
11656 // If the original width and height is small, e.g. 400 or below,
11657 // scale up and then down to improve image quality. This can make
11658 // a big difference, with not much performance hit.
11659 int upscaleThreshold
= 400;
11661 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11663 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11664 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11667 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11668 m_imageCache
= wxBitmap(img
);
11672 return m_imageCache
.IsOk();
11676 bool wxRichTextImage
::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11681 // Don't need cached size AFAIK
11682 // wxSize size = GetCachedSize();
11683 if (!LoadImageCache(dc
))
11686 wxRichTextAttr
attr(GetAttributes());
11687 context
.ApplyVirtualAttributes(attr
, this);
11689 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11691 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11692 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11693 marginRect
= rect
; // outer rectangle, will calculate contentRect
11694 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11696 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11698 if (selection
.WithinSelection(GetRange().GetStart(), this))
11700 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11701 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11702 dc
.SetLogicalFunction(wxINVERT
);
11703 dc
.DrawRectangle(contentRect
);
11704 dc
.SetLogicalFunction(wxCOPY
);
11710 /// Lay the item out
11711 bool wxRichTextImage
::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11713 if (!LoadImageCache(dc
))
11716 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11717 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11718 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11720 wxRichTextAttr
attr(GetAttributes());
11721 context
.ApplyVirtualAttributes(attr
, this);
11723 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11725 wxSize overallSize
= marginRect
.GetSize();
11727 SetCachedSize(overallSize
);
11728 SetMaxSize(overallSize
);
11729 SetMinSize(overallSize
);
11730 SetPosition(rect
.GetPosition());
11735 /// Get/set the object size for the given range. Returns false if the range
11736 /// is invalid for this object.
11737 bool wxRichTextImage
::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), const wxPoint
& WXUNUSED(position
), const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
11739 if (!range
.IsWithin(GetRange()))
11742 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11744 size
.x
= 0; size
.y
= 0;
11745 if (partialExtents
)
11746 partialExtents
->Add(0);
11750 wxRichTextAttr
attr(GetAttributes());
11751 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11753 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11754 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11755 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11756 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11758 wxSize overallSize
= marginRect
.GetSize();
11760 if (partialExtents
)
11761 partialExtents
->Add(overallSize
.x
);
11763 size
= overallSize
;
11768 // Get the 'natural' size for an object. For an image, it would be the
11770 wxTextAttrSize wxRichTextImage
::GetNaturalSize() const
11772 wxTextAttrSize size
;
11773 if (GetImageCache().IsOk())
11775 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11776 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11783 void wxRichTextImage
::Copy(const wxRichTextImage
& obj
)
11785 wxRichTextObject
::Copy(obj
);
11787 m_imageBlock
= obj
.m_imageBlock
;
11788 m_originalImageSize
= obj
.m_originalImageSize
;
11791 /// Edit properties via a GUI
11792 bool wxRichTextImage
::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11794 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11795 imageDlg
.SetAttributes(GetAttributes());
11797 if (imageDlg
.ShowModal() == wxID_OK
)
11799 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11800 // indeterminate in the object.
11801 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11813 /// Compare two attribute objects
11814 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11816 return (attr1
== attr2
);
11820 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11822 if (tabs1
.GetCount() != tabs2
.GetCount())
11826 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11828 if (tabs1
[i
] != tabs2
[i
])
11834 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11836 return destStyle
.Apply(style
, compareWith
);
11839 // Remove attributes
11840 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11842 return destStyle
.RemoveStyle(style
);
11845 /// Combine two bitlists, specifying the bits of interest with separate flags.
11846 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11848 return wxRichTextAttr
::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11851 /// Compare two bitlists
11852 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11854 return wxRichTextAttr
::BitlistsEqPartial(valueA
, valueB
, flags
);
11857 /// Split into paragraph and character styles
11858 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11860 return wxRichTextAttr
::SplitParaCharStyles(style
, parStyle
, charStyle
);
11863 /// Convert a decimal to Roman numerals
11864 wxString
wxRichTextDecimalToRoman(long n
)
11866 static wxArrayInt decimalNumbers
;
11867 static wxArrayString romanNumbers
;
11872 decimalNumbers
.Clear();
11873 romanNumbers
.Clear();
11874 return wxEmptyString
;
11877 if (decimalNumbers
.GetCount() == 0)
11879 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11881 wxRichTextAddDecRom(1000, wxT("M"));
11882 wxRichTextAddDecRom(900, wxT("CM"));
11883 wxRichTextAddDecRom(500, wxT("D"));
11884 wxRichTextAddDecRom(400, wxT("CD"));
11885 wxRichTextAddDecRom(100, wxT("C"));
11886 wxRichTextAddDecRom(90, wxT("XC"));
11887 wxRichTextAddDecRom(50, wxT("L"));
11888 wxRichTextAddDecRom(40, wxT("XL"));
11889 wxRichTextAddDecRom(10, wxT("X"));
11890 wxRichTextAddDecRom(9, wxT("IX"));
11891 wxRichTextAddDecRom(5, wxT("V"));
11892 wxRichTextAddDecRom(4, wxT("IV"));
11893 wxRichTextAddDecRom(1, wxT("I"));
11899 while (n
> 0 && i
< 13)
11901 if (n
>= decimalNumbers
[i
])
11903 n
-= decimalNumbers
[i
];
11904 roman
+= romanNumbers
[i
];
11911 if (roman
.IsEmpty())
11917 * wxRichTextFileHandler
11918 * Base class for file handlers
11921 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11923 #if wxUSE_FFILE && wxUSE_STREAMS
11924 bool wxRichTextFileHandler
::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11926 wxFFileInputStream
stream(filename
);
11928 return LoadFile(buffer
, stream
);
11933 bool wxRichTextFileHandler
::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11935 wxFFileOutputStream
stream(filename
);
11937 return SaveFile(buffer
, stream
);
11941 #endif // wxUSE_FFILE && wxUSE_STREAMS
11943 /// Can we handle this filename (if using files)? By default, checks the extension.
11944 bool wxRichTextFileHandler
::CanHandle(const wxString
& filename
) const
11946 wxString path
, file
, ext
;
11947 wxFileName
::SplitPath(filename
, & path
, & file
, & ext
);
11949 return (ext
.Lower() == GetExtension());
11953 * wxRichTextTextHandler
11954 * Plain text handler
11957 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11960 bool wxRichTextPlainTextHandler
::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11962 if (!stream
.IsOk())
11968 while (!stream
.Eof())
11970 int ch
= stream
.GetC();
11974 if (ch
== 10 && lastCh
!= 13)
11977 if (ch
> 0 && ch
!= 10)
11984 buffer
->ResetAndClearCommands();
11986 buffer
->AddParagraphs(str
);
11987 buffer
->UpdateRanges();
11992 bool wxRichTextPlainTextHandler
::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11994 if (!stream
.IsOk())
11997 wxString text
= buffer
->GetText();
11999 wxString newLine
= wxRichTextLineBreakChar
;
12000 text
.Replace(newLine
, wxT("\n"));
12002 wxCharBuffer buf
= text
.ToAscii();
12004 stream
.Write((const char*) buf
, text
.length());
12007 #endif // wxUSE_STREAMS
12010 * Stores information about an image, in binary in-memory form
12013 wxRichTextImageBlock
::wxRichTextImageBlock()
12018 wxRichTextImageBlock
::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
12024 wxRichTextImageBlock
::~wxRichTextImageBlock()
12029 void wxRichTextImageBlock
::Init()
12033 m_imageType
= wxBITMAP_TYPE_INVALID
;
12036 void wxRichTextImageBlock
::Clear()
12040 m_imageType
= wxBITMAP_TYPE_INVALID
;
12044 // Load the original image into a memory block.
12045 // If the image is not a JPEG, we must convert it into a JPEG
12046 // to conserve space.
12047 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
12048 // load the image a 2nd time.
12050 bool wxRichTextImageBlock
::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
12051 wxImage
& image
, bool convertToJPEG
)
12053 m_imageType
= imageType
;
12055 wxString
filenameToRead(filename
);
12056 bool removeFile
= false;
12058 if (imageType
== wxBITMAP_TYPE_INVALID
)
12059 return false; // Could not determine image type
12061 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
12063 wxString tempFile
=
12064 wxFileName
::CreateTempFileName(_("image"));
12066 wxASSERT(!tempFile
.IsEmpty());
12068 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
12069 filenameToRead
= tempFile
;
12072 m_imageType
= wxBITMAP_TYPE_JPEG
;
12075 if (!file
.Open(filenameToRead
))
12078 m_dataSize
= (size_t) file
.Length();
12083 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
12086 wxRemoveFile(filenameToRead
);
12088 return (m_data
!= NULL
);
12091 // Make an image block from the wxImage in the given
12093 bool wxRichTextImageBlock
::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
12095 image
.SetOption(wxT("quality"), quality
);
12097 if (imageType
== wxBITMAP_TYPE_INVALID
)
12098 return false; // Could not determine image type
12100 return DoMakeImageBlock(image
, imageType
);
12103 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
12104 bool wxRichTextImageBlock
::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
12106 if (imageType
== wxBITMAP_TYPE_INVALID
)
12107 return false; // Could not determine image type
12109 return DoMakeImageBlock(image
, imageType
);
12112 // Makes the image block
12113 bool wxRichTextImageBlock
::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
12115 wxMemoryOutputStream memStream
;
12116 if (!image
.SaveFile(memStream
, imageType
))
12121 unsigned char* block
= new unsigned char[memStream
.GetSize()];
12129 m_imageType
= imageType
;
12130 m_dataSize
= memStream
.GetSize();
12132 memStream
.CopyTo(m_data
, m_dataSize
);
12134 return (m_data
!= NULL
);
12138 bool wxRichTextImageBlock
::Write(const wxString
& filename
)
12140 return WriteBlock(filename
, m_data
, m_dataSize
);
12143 void wxRichTextImageBlock
::Copy(const wxRichTextImageBlock
& block
)
12145 m_imageType
= block
.m_imageType
;
12147 m_dataSize
= block
.m_dataSize
;
12148 if (m_dataSize
== 0)
12151 m_data
= new unsigned char[m_dataSize
];
12153 for (i
= 0; i
< m_dataSize
; i
++)
12154 m_data
[i
] = block
.m_data
[i
];
12158 void wxRichTextImageBlock
::operator=(const wxRichTextImageBlock
& block
)
12163 // Load a wxImage from the block
12164 bool wxRichTextImageBlock
::Load(wxImage
& image
)
12169 // Read in the image.
12171 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
12172 bool success
= image
.LoadFile(mstream
, GetImageType());
12174 wxString tempFile
= wxFileName
::CreateTempFileName(_("image"));
12175 wxASSERT(!tempFile
.IsEmpty());
12177 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
12181 success
= image
.LoadFile(tempFile
, GetImageType());
12182 wxRemoveFile(tempFile
);
12188 // Write data in hex to a stream
12189 bool wxRichTextImageBlock
::WriteHex(wxOutputStream
& stream
)
12191 if (m_dataSize
== 0)
12194 int bufSize
= 100000;
12195 if (int(2*m_dataSize
) < bufSize
)
12196 bufSize
= 2*m_dataSize
;
12197 char* buf
= new char[bufSize
+1];
12199 int left
= m_dataSize
;
12204 if (left
*2 > bufSize
)
12206 n
= bufSize
; left
-= (bufSize
/2);
12210 n
= left
*2; left
= 0;
12214 for (i
= 0; i
< (n
/2); i
++)
12216 wxDecToHex(m_data
[j
], b
, b
+1);
12221 stream
.Write((const char*) buf
, n
);
12227 // Read data in hex from a stream
12228 bool wxRichTextImageBlock
::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
12230 int dataSize
= length
/2;
12235 // create a null terminated temporary string:
12239 m_data
= new unsigned char[dataSize
];
12241 for (i
= 0; i
< dataSize
; i
++)
12243 str
[0] = (char)stream
.GetC();
12244 str
[1] = (char)stream
.GetC();
12246 m_data
[i
] = (unsigned char)wxHexToDec(str
);
12249 m_dataSize
= dataSize
;
12250 m_imageType
= imageType
;
12255 // Allocate and read from stream as a block of memory
12256 unsigned char* wxRichTextImageBlock
::ReadBlock(wxInputStream
& stream
, size_t size
)
12258 unsigned char* block
= new unsigned char[size
];
12262 stream
.Read(block
, size
);
12267 unsigned char* wxRichTextImageBlock
::ReadBlock(const wxString
& filename
, size_t size
)
12269 wxFileInputStream
stream(filename
);
12270 if (!stream
.IsOk())
12273 return ReadBlock(stream
, size
);
12276 // Write memory block to stream
12277 bool wxRichTextImageBlock
::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
12279 stream
.Write((void*) block
, size
);
12280 return stream
.IsOk();
12284 // Write memory block to file
12285 bool wxRichTextImageBlock
::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
12287 wxFileOutputStream
outStream(filename
);
12288 if (!outStream
.IsOk())
12291 return WriteBlock(outStream
, block
, size
);
12294 // Gets the extension for the block's type
12295 wxString wxRichTextImageBlock
::GetExtension() const
12297 wxImageHandler
* handler
= wxImage
::FindHandler(GetImageType());
12299 return handler
->GetExtension();
12301 return wxEmptyString
;
12307 * The data object for a wxRichTextBuffer
12310 const wxChar
*wxRichTextBufferDataObject
::ms_richTextBufferFormatId
= wxT("wxRichText");
12312 wxRichTextBufferDataObject
::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
12314 m_richTextBuffer
= richTextBuffer
;
12316 // this string should uniquely identify our format, but is otherwise
12318 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
12320 SetFormat(m_formatRichTextBuffer
);
12323 wxRichTextBufferDataObject
::~wxRichTextBufferDataObject()
12325 delete m_richTextBuffer
;
12328 // after a call to this function, the richTextBuffer is owned by the caller and it
12329 // is responsible for deleting it!
12330 wxRichTextBuffer
* wxRichTextBufferDataObject
::GetRichTextBuffer()
12332 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
12333 m_richTextBuffer
= NULL
;
12335 return richTextBuffer
;
12338 wxDataFormat wxRichTextBufferDataObject
::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
12340 return m_formatRichTextBuffer
;
12343 size_t wxRichTextBufferDataObject
::GetDataSize() const
12345 if (!m_richTextBuffer
)
12351 wxStringOutputStream
stream(& bufXML
);
12352 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
12354 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12360 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
12361 return strlen(buffer
) + 1;
12363 return bufXML
.Length()+1;
12367 bool wxRichTextBufferDataObject
::GetDataHere(void *pBuf
) const
12369 if (!pBuf
|| !m_richTextBuffer
)
12375 wxStringOutputStream
stream(& bufXML
);
12376 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
12378 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12384 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
12385 size_t len
= strlen(buffer
);
12386 memcpy((char*) pBuf
, (const char*) buffer
, len
);
12387 ((char*) pBuf
)[len
] = 0;
12389 size_t len
= bufXML
.Length();
12390 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
12391 ((char*) pBuf
)[len
] = 0;
12397 bool wxRichTextBufferDataObject
::SetData(size_t WXUNUSED(len
), const void *buf
)
12399 wxDELETE(m_richTextBuffer
);
12401 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12403 m_richTextBuffer
= new wxRichTextBuffer
;
12405 wxStringInputStream
stream(bufXML
);
12406 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12408 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12410 wxDELETE(m_richTextBuffer
);
12422 * wxRichTextFontTable
12423 * Manages quick access to a pool of fonts for rendering rich text
12426 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12428 class wxRichTextFontTableData
: public wxObjectRefData
12431 wxRichTextFontTableData() {}
12433 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12435 wxRichTextFontTableHashMap m_hashMap
;
12438 wxFont wxRichTextFontTableData
::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12440 wxString
facename(fontSpec
.GetFontFaceName());
12442 int fontSize
= fontSpec
.GetFontSize();
12443 if (fontScale
!= 1.0)
12444 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12447 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12451 wxString spec
= wxString
::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12452 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12453 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12455 wxRichTextFontTableHashMap
::iterator entry
= m_hashMap
.find(spec
);
12456 if ( entry
== m_hashMap
.end() )
12458 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12460 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12461 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12462 font
.SetStrikethrough(true);
12463 m_hashMap
[spec
] = font
;
12468 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12469 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12470 font
.SetStrikethrough(true);
12472 m_hashMap
[spec
] = font
;
12478 return entry
->second
;
12482 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12484 wxRichTextFontTable
::wxRichTextFontTable()
12486 m_refData
= new wxRichTextFontTableData
;
12490 wxRichTextFontTable
::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12496 wxRichTextFontTable
::~wxRichTextFontTable()
12501 bool wxRichTextFontTable
::operator == (const wxRichTextFontTable
& table
) const
12503 return (m_refData
== table
.m_refData
);
12506 void wxRichTextFontTable
::operator= (const wxRichTextFontTable
& table
)
12509 m_fontScale
= table
.m_fontScale
;
12512 wxFont wxRichTextFontTable
::FindFont(const wxRichTextAttr
& fontSpec
)
12514 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12516 return data
->FindFont(fontSpec
, m_fontScale
);
12521 void wxRichTextFontTable
::Clear()
12523 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12525 data
->m_hashMap
.clear();
12528 void wxRichTextFontTable
::SetFontScale(double fontScale
)
12530 if (fontScale
!= m_fontScale
)
12532 m_fontScale
= fontScale
;
12537 void wxTextBoxAttr
::Reset()
12540 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12541 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12542 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12543 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12544 m_boxStyleName
= wxEmptyString
;
12548 m_position
.Reset();
12559 bool wxTextBoxAttr
::operator== (const wxTextBoxAttr
& attr
) const
12562 m_flags
== attr
.m_flags
&&
12563 m_floatMode
== attr
.m_floatMode
&&
12564 m_clearMode
== attr
.m_clearMode
&&
12565 m_collapseMode
== attr
.m_collapseMode
&&
12566 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12568 m_margins
== attr
.m_margins
&&
12569 m_padding
== attr
.m_padding
&&
12570 m_position
== attr
.m_position
&&
12572 m_size
== attr
.m_size
&&
12573 m_minSize
== attr
.m_minSize
&&
12574 m_maxSize
== attr
.m_maxSize
&&
12576 m_border
== attr
.m_border
&&
12577 m_outline
== attr
.m_outline
&&
12579 m_boxStyleName
== attr
.m_boxStyleName
12583 // Partial equality test
12584 bool wxTextBoxAttr
::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12587 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12588 (!HasClearMode() && attr
.HasClearMode()) ||
12589 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12590 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12591 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12595 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12598 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12601 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12604 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12607 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12612 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12617 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12619 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12621 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12626 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12631 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12636 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12641 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12647 // Merges the given attributes. If compareWith
12648 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12649 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12650 bool wxTextBoxAttr
::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12652 if (attr
.HasFloatMode())
12654 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12655 SetFloatMode(attr
.GetFloatMode());
12658 if (attr
.HasClearMode())
12660 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12661 SetClearMode(attr
.GetClearMode());
12664 if (attr
.HasCollapseBorders())
12666 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12667 SetCollapseBorders(attr
.GetCollapseBorders());
12670 if (attr
.HasVerticalAlignment())
12672 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12673 SetVerticalAlignment(attr
.GetVerticalAlignment());
12676 if (attr
.HasBoxStyleName())
12678 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12679 SetBoxStyleName(attr
.GetBoxStyleName());
12682 m_margins
.Apply(attr
.m_margins
, compareWith ?
(& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12683 m_padding
.Apply(attr
.m_padding
, compareWith ?
(& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12684 m_position
.Apply(attr
.m_position
, compareWith ?
(& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12686 m_size
.Apply(attr
.m_size
, compareWith ?
(& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12687 m_minSize
.Apply(attr
.m_minSize
, compareWith ?
(& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12688 m_maxSize
.Apply(attr
.m_maxSize
, compareWith ?
(& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12690 m_border
.Apply(attr
.m_border
, compareWith ?
(& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12691 m_outline
.Apply(attr
.m_outline
, compareWith ?
(& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12696 // Remove specified attributes from this object
12697 bool wxTextBoxAttr
::RemoveStyle(const wxTextBoxAttr
& attr
)
12699 if (attr
.HasFloatMode())
12700 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12702 if (attr
.HasClearMode())
12703 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12705 if (attr
.HasCollapseBorders())
12706 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12708 if (attr
.HasVerticalAlignment())
12709 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12711 if (attr
.HasBoxStyleName())
12713 SetBoxStyleName(wxEmptyString
);
12714 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12717 m_margins
.RemoveStyle(attr
.m_margins
);
12718 m_padding
.RemoveStyle(attr
.m_padding
);
12719 m_position
.RemoveStyle(attr
.m_position
);
12721 m_size
.RemoveStyle(attr
.m_size
);
12722 m_minSize
.RemoveStyle(attr
.m_minSize
);
12723 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12725 m_border
.RemoveStyle(attr
.m_border
);
12726 m_outline
.RemoveStyle(attr
.m_outline
);
12731 // Collects the attributes that are common to a range of content, building up a note of
12732 // which attributes are absent in some objects and which clash in some objects.
12733 void wxTextBoxAttr
::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12735 if (attr
.HasFloatMode())
12737 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12739 if (HasFloatMode())
12741 if (GetFloatMode() != attr
.GetFloatMode())
12743 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12744 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12748 SetFloatMode(attr
.GetFloatMode());
12752 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12754 if (attr
.HasClearMode())
12756 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12758 if (HasClearMode())
12760 if (GetClearMode() != attr
.GetClearMode())
12762 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12763 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12767 SetClearMode(attr
.GetClearMode());
12771 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12773 if (attr
.HasCollapseBorders())
12775 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12777 if (HasCollapseBorders())
12779 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12781 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12782 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12786 SetCollapseBorders(attr
.GetCollapseBorders());
12790 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12792 if (attr
.HasVerticalAlignment())
12794 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12796 if (HasVerticalAlignment())
12798 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12800 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12801 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12805 SetVerticalAlignment(attr
.GetVerticalAlignment());
12809 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12811 if (attr
.HasBoxStyleName())
12813 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12815 if (HasBoxStyleName())
12817 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12819 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12820 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12824 SetBoxStyleName(attr
.GetBoxStyleName());
12828 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12830 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12831 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12832 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12834 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12835 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12836 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12838 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12839 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12842 bool wxTextBoxAttr
::IsDefault() const
12844 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12845 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12846 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12851 void wxRichTextAttr
::Copy(const wxRichTextAttr
& attr
)
12853 wxTextAttr
::Copy(attr
);
12855 m_textBoxAttr
= attr
.m_textBoxAttr
;
12858 bool wxRichTextAttr
::operator==(const wxRichTextAttr
& attr
) const
12860 if (!(wxTextAttr
::operator==(attr
)))
12863 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12866 // Partial equality test
12867 bool wxRichTextAttr
::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12869 if (!(wxTextAttr
::EqPartial(attr
, weakTest
)))
12872 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12875 // Merges the given attributes. If compareWith
12876 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12877 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12878 bool wxRichTextAttr
::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12880 wxTextAttr
::Apply(style
, compareWith
);
12882 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith ?
(& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12885 // Remove specified attributes from this object
12886 bool wxRichTextAttr
::RemoveStyle(const wxRichTextAttr
& attr
)
12888 wxTextAttr
::RemoveStyle(*this, attr
);
12890 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12893 // Collects the attributes that are common to a range of content, building up a note of
12894 // which attributes are absent in some objects and which clash in some objects.
12895 void wxRichTextAttr
::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12897 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12899 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12902 // Partial equality test
12903 bool wxTextAttrBorder
::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12906 ((!HasStyle() && border
.HasStyle()) ||
12907 (!HasColour() && border
.HasColour()) ||
12908 (!HasWidth() && border
.HasWidth())))
12913 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12916 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12919 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12925 // Apply border to 'this', but not if the same as compareWith
12926 bool wxTextAttrBorder
::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12928 if (border
.HasStyle())
12930 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12931 SetStyle(border
.GetStyle());
12933 if (border
.HasColour())
12935 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12936 SetColour(border
.GetColourLong());
12938 if (border
.HasWidth())
12940 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12941 SetWidth(border
.GetWidth());
12947 // Remove specified attributes from this object
12948 bool wxTextAttrBorder
::RemoveStyle(const wxTextAttrBorder
& attr
)
12950 if (attr
.HasStyle() && HasStyle())
12951 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12952 if (attr
.HasColour() && HasColour())
12953 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12954 if (attr
.HasWidth() && HasWidth())
12955 m_borderWidth
.Reset();
12960 // Collects the attributes that are common to a range of content, building up a note of
12961 // which attributes are absent in some objects and which clash in some objects.
12962 void wxTextAttrBorder
::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12964 if (attr
.HasStyle())
12966 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12970 if (GetStyle() != attr
.GetStyle())
12972 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12973 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12977 SetStyle(attr
.GetStyle());
12981 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12983 if (attr
.HasColour())
12985 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12989 if (GetColour() != attr
.GetColour())
12991 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12992 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12996 SetColour(attr
.GetColourLong());
13000 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
13002 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
13005 // Partial equality test
13006 bool wxTextAttrBorders
::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
13008 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
13009 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
13012 // Apply border to 'this', but not if the same as compareWith
13013 bool wxTextAttrBorders
::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
13015 m_left
.Apply(borders
.m_left
, compareWith ?
(& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
13016 m_right
.Apply(borders
.m_right
, compareWith ?
(& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
13017 m_top
.Apply(borders
.m_top
, compareWith ?
(& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
13018 m_bottom
.Apply(borders
.m_bottom
, compareWith ?
(& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
13022 // Remove specified attributes from this object
13023 bool wxTextAttrBorders
::RemoveStyle(const wxTextAttrBorders
& attr
)
13025 m_left
.RemoveStyle(attr
.m_left
);
13026 m_right
.RemoveStyle(attr
.m_right
);
13027 m_top
.RemoveStyle(attr
.m_top
);
13028 m_bottom
.RemoveStyle(attr
.m_bottom
);
13032 // Collects the attributes that are common to a range of content, building up a note of
13033 // which attributes are absent in some objects and which clash in some objects.
13034 void wxTextAttrBorders
::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
13036 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
13037 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
13038 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
13039 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
13042 // Set style of all borders
13043 void wxTextAttrBorders
::SetStyle(int style
)
13045 m_left
.SetStyle(style
);
13046 m_right
.SetStyle(style
);
13047 m_top
.SetStyle(style
);
13048 m_bottom
.SetStyle(style
);
13051 // Set colour of all borders
13052 void wxTextAttrBorders
::SetColour(unsigned long colour
)
13054 m_left
.SetColour(colour
);
13055 m_right
.SetColour(colour
);
13056 m_top
.SetColour(colour
);
13057 m_bottom
.SetColour(colour
);
13060 void wxTextAttrBorders
::SetColour(const wxColour
& colour
)
13062 m_left
.SetColour(colour
);
13063 m_right
.SetColour(colour
);
13064 m_top
.SetColour(colour
);
13065 m_bottom
.SetColour(colour
);
13068 // Set width of all borders
13069 void wxTextAttrBorders
::SetWidth(const wxTextAttrDimension
& width
)
13071 m_left
.SetWidth(width
);
13072 m_right
.SetWidth(width
);
13073 m_top
.SetWidth(width
);
13074 m_bottom
.SetWidth(width
);
13077 // Partial equality test
13078 bool wxTextAttrDimension
::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
13080 if (!weakTest
&& !IsValid() && dim
.IsValid())
13083 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
13089 bool wxTextAttrDimension
::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
13093 if (!(compareWith
&& dim
== (*compareWith
)))
13100 // Collects the attributes that are common to a range of content, building up a note of
13101 // which attributes are absent in some objects and which clash in some objects.
13102 void wxTextAttrDimension
::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
13104 if (attr
.IsValid())
13106 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
13110 if (!((*this) == attr
))
13112 clashingAttr
.SetValid(true);
13121 absentAttr
.SetValid(true);
13124 wxTextAttrDimensionConverter
::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
13126 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
13129 wxTextAttrDimensionConverter
::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
13131 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
13134 int wxTextAttrDimensionConverter
::ConvertTenthsMMToPixels(int units
) const
13136 return wxRichTextObject
::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
13139 int wxTextAttrDimensionConverter
::ConvertPixelsToTenthsMM(int pixels
) const
13141 return wxRichTextObject
::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
13144 int wxTextAttrDimensionConverter
::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
13146 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
13147 return ConvertTenthsMMToPixels(dim
.GetValue());
13148 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
13149 return dim
.GetValue();
13150 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
13152 wxASSERT(m_parentSize
!= wxDefaultSize
);
13153 if (direction
== wxHORIZONTAL
)
13154 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
13156 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
13165 int wxTextAttrDimensionConverter
::GetTenthsMM(const wxTextAttrDimension
& dim
) const
13167 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
13168 return dim
.GetValue();
13169 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
13170 return ConvertPixelsToTenthsMM(dim
.GetValue());
13178 // Partial equality test
13179 bool wxTextAttrDimensions
::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
13181 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
13184 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
13187 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
13190 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
13196 // Apply border to 'this', but not if the same as compareWith
13197 bool wxTextAttrDimensions
::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
13199 m_left
.Apply(dims
.m_left
, compareWith ?
(& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
13200 m_right
.Apply(dims
.m_right
, compareWith ?
(& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
13201 m_top
.Apply(dims
.m_top
, compareWith ?
(& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
13202 m_bottom
.Apply(dims
.m_bottom
, compareWith ?
(& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
13207 // Remove specified attributes from this object
13208 bool wxTextAttrDimensions
::RemoveStyle(const wxTextAttrDimensions
& attr
)
13210 if (attr
.m_left
.IsValid())
13212 if (attr
.m_right
.IsValid())
13214 if (attr
.m_top
.IsValid())
13216 if (attr
.m_bottom
.IsValid())
13222 // Collects the attributes that are common to a range of content, building up a note of
13223 // which attributes are absent in some objects and which clash in some objects.
13224 void wxTextAttrDimensions
::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
13226 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
13227 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
13228 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
13229 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
13232 // Partial equality test
13233 bool wxTextAttrSize
::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
13235 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
13238 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
13244 // Apply border to 'this', but not if the same as compareWith
13245 bool wxTextAttrSize
::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
13247 m_width
.Apply(size
.m_width
, compareWith ?
(& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
13248 m_height
.Apply(size
.m_height
, compareWith ?
(& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
13253 // Remove specified attributes from this object
13254 bool wxTextAttrSize
::RemoveStyle(const wxTextAttrSize
& attr
)
13256 if (attr
.m_width
.IsValid())
13258 if (attr
.m_height
.IsValid())
13264 // Collects the attributes that are common to a range of content, building up a note of
13265 // which attributes are absent in some objects and which clash in some objects.
13266 void wxTextAttrSize
::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
13268 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
13269 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
13272 // Collects the attributes that are common to a range of content, building up a note of
13273 // which attributes are absent in some objects and which clash in some objects.
13274 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
13276 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
13277 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
13279 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
13281 // If different font size units are being used, this is a clash.
13282 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
13284 currentStyle
.SetFontSize(0);
13285 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
13286 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
13290 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
13292 if (currentStyle
.HasFontPointSize())
13294 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
13296 // Clash of attr - mark as such
13297 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13298 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13302 currentStyle
.SetFontSize(attr
.GetFontSize());
13304 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
13306 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13307 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13310 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
13312 if (currentStyle
.HasFontPixelSize())
13314 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
13316 // Clash of attr - mark as such
13317 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13318 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13322 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
13324 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
13326 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13327 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13331 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
13333 if (currentStyle
.HasFontItalic())
13335 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
13337 // Clash of attr - mark as such
13338 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
13339 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
13343 currentStyle
.SetFontStyle(attr
.GetFontStyle());
13345 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
13347 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
13348 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
13351 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
13353 if (currentStyle
.HasFontFamily())
13355 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
13357 // Clash of attr - mark as such
13358 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
13359 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
13363 currentStyle
.SetFontFamily(attr
.GetFontFamily());
13365 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
13367 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
13368 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
13371 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
13373 if (currentStyle
.HasFontWeight())
13375 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
13377 // Clash of attr - mark as such
13378 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13379 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13383 currentStyle
.SetFontWeight(attr
.GetFontWeight());
13385 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
13387 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13388 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13391 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
13393 if (currentStyle
.HasFontFaceName())
13395 wxString
faceName1(currentStyle
.GetFontFaceName());
13396 wxString
faceName2(attr
.GetFontFaceName());
13398 if (faceName1
!= faceName2
)
13400 // Clash of attr - mark as such
13401 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13402 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13406 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13408 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13410 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13411 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13414 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13416 if (currentStyle
.HasFontUnderlined())
13418 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13420 // Clash of attr - mark as such
13421 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13422 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13426 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13428 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13430 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13431 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13434 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13436 if (currentStyle
.HasFontStrikethrough())
13438 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13440 // Clash of attr - mark as such
13441 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13442 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13446 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13448 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13450 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13451 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13454 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13456 if (currentStyle
.HasTextColour())
13458 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13460 // Clash of attr - mark as such
13461 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13462 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13466 currentStyle
.SetTextColour(attr
.GetTextColour());
13468 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13470 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13471 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13474 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13476 if (currentStyle
.HasBackgroundColour())
13478 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13480 // Clash of attr - mark as such
13481 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13482 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13486 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13488 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13490 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13491 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13494 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13496 if (currentStyle
.HasAlignment())
13498 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13500 // Clash of attr - mark as such
13501 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13502 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13506 currentStyle
.SetAlignment(attr
.GetAlignment());
13508 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13510 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13511 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13514 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13516 if (currentStyle
.HasTabs())
13518 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13520 // Clash of attr - mark as such
13521 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13522 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13526 currentStyle
.SetTabs(attr
.GetTabs());
13528 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13530 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13531 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13534 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13536 if (currentStyle
.HasLeftIndent())
13538 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13540 // Clash of attr - mark as such
13541 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13542 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13546 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13548 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13550 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13551 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13554 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13556 if (currentStyle
.HasRightIndent())
13558 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13560 // Clash of attr - mark as such
13561 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13562 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13566 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13568 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13570 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13571 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13574 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13576 if (currentStyle
.HasParagraphSpacingAfter())
13578 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13580 // Clash of attr - mark as such
13581 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13582 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13586 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13588 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13590 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13591 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13594 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13596 if (currentStyle
.HasParagraphSpacingBefore())
13598 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13600 // Clash of attr - mark as such
13601 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13602 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13606 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13608 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13610 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13611 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13614 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13616 if (currentStyle
.HasLineSpacing())
13618 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13620 // Clash of attr - mark as such
13621 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13622 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13626 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13628 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13630 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13631 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13634 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13636 if (currentStyle
.HasCharacterStyleName())
13638 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13640 // Clash of attr - mark as such
13641 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13642 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13646 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13648 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13650 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13651 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13654 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13656 if (currentStyle
.HasParagraphStyleName())
13658 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13660 // Clash of attr - mark as such
13661 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13662 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13666 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13668 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13670 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13671 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13674 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13676 if (currentStyle
.HasListStyleName())
13678 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13680 // Clash of attr - mark as such
13681 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13682 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13686 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13688 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13690 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13691 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13694 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13696 if (currentStyle
.HasBulletStyle())
13698 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13700 // Clash of attr - mark as such
13701 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13702 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13706 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13708 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13710 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13711 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13714 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13716 if (currentStyle
.HasBulletNumber())
13718 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13720 // Clash of attr - mark as such
13721 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13722 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13726 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13728 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13730 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13731 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13734 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13736 if (currentStyle
.HasBulletText())
13738 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13740 // Clash of attr - mark as such
13741 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13742 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13747 currentStyle
.SetBulletText(attr
.GetBulletText());
13748 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13751 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13753 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13754 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13757 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13759 if (currentStyle
.HasBulletName())
13761 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13763 // Clash of attr - mark as such
13764 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13765 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13770 currentStyle
.SetBulletName(attr
.GetBulletName());
13773 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13775 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13776 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13779 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13781 if (currentStyle
.HasURL())
13783 if (currentStyle
.GetURL() != attr
.GetURL())
13785 // Clash of attr - mark as such
13786 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13787 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13792 currentStyle
.SetURL(attr
.GetURL());
13795 else if (!attr
.HasURL() && currentStyle
.HasURL())
13797 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13798 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13801 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13803 if (currentStyle
.HasTextEffects())
13805 // We need to find the bits in the new attr that are different:
13806 // just look at those bits that are specified by the new attr.
13808 // We need to remove the bits and flags that are not common between current attr
13809 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13810 // previous styles.
13812 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13813 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13815 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13817 // Find the text effects that were different, using XOR
13818 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13820 // Clash of attr - mark as such
13821 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13822 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13827 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13828 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13831 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13832 // that we've looked at so far
13833 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13834 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13836 if (currentStyle
.GetTextEffectFlags() == 0)
13837 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13839 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13841 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13842 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13845 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13847 if (currentStyle
.HasOutlineLevel())
13849 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13851 // Clash of attr - mark as such
13852 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13853 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13857 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13859 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13861 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13862 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13866 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13869 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13871 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13873 bool wxRichTextProperties
::operator==(const wxRichTextProperties
& props
) const
13875 if (m_properties
.GetCount() != props
.GetCount())
13879 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13881 const wxVariant
& var1
= m_properties
[i
];
13882 int idx
= props
.Find(var1
.GetName());
13885 const wxVariant
& var2
= props
.m_properties
[idx
];
13886 if (!(var1
== var2
))
13893 wxArrayString wxRichTextProperties
::GetPropertyNames() const
13897 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13899 arr
.Add(m_properties
[i
].GetName());
13904 int wxRichTextProperties
::Find(const wxString
& name
) const
13907 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13909 if (m_properties
[i
].GetName() == name
)
13915 bool wxRichTextProperties
::Remove(const wxString
& name
)
13917 int idx
= Find(name
);
13920 m_properties
.RemoveAt(idx
);
13927 wxVariant
* wxRichTextProperties
::FindOrCreateProperty(const wxString
& name
)
13929 int idx
= Find(name
);
13930 if (idx
== wxNOT_FOUND
)
13931 SetProperty(name
, wxString());
13933 if (idx
!= wxNOT_FOUND
)
13935 return & (*this)[idx
];
13941 const wxVariant
& wxRichTextProperties
::GetProperty(const wxString
& name
) const
13943 static const wxVariant nullVariant
;
13944 int idx
= Find(name
);
13946 return m_properties
[idx
];
13948 return nullVariant
;
13951 wxString wxRichTextProperties
::GetPropertyString(const wxString
& name
) const
13953 return GetProperty(name
).GetString();
13956 long wxRichTextProperties
::GetPropertyLong(const wxString
& name
) const
13958 return GetProperty(name
).GetLong();
13961 bool wxRichTextProperties
::GetPropertyBool(const wxString
& name
) const
13963 return GetProperty(name
).GetBool();
13966 double wxRichTextProperties
::GetPropertyDouble(const wxString
& name
) const
13968 return GetProperty(name
).GetDouble();
13971 void wxRichTextProperties
::SetProperty(const wxVariant
& variant
)
13973 wxASSERT(!variant
.GetName().IsEmpty());
13975 int idx
= Find(variant
.GetName());
13978 m_properties
.Add(variant
);
13980 m_properties
[idx
] = variant
;
13983 void wxRichTextProperties
::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13985 int idx
= Find(name
);
13986 wxVariant
var(variant
);
13990 m_properties
.Add(var
);
13992 m_properties
[idx
] = var
;
13995 void wxRichTextProperties
::SetProperty(const wxString
& name
, const wxString
& value
)
13997 SetProperty(name
, wxVariant(value
, name
));
14000 void wxRichTextProperties
::SetProperty(const wxString
& name
, long value
)
14002 SetProperty(name
, wxVariant(value
, name
));
14005 void wxRichTextProperties
::SetProperty(const wxString
& name
, double value
)
14007 SetProperty(name
, wxVariant(value
, name
));
14010 void wxRichTextProperties
::SetProperty(const wxString
& name
, bool value
)
14012 SetProperty(name
, wxVariant(value
, name
));
14015 void wxRichTextProperties
::RemoveProperties(const wxRichTextProperties
& properties
)
14018 for (i
= 0; i
< properties
.GetCount(); i
++)
14020 wxString name
= properties
.GetProperties()[i
].GetName();
14021 if (HasProperty(name
))
14026 void wxRichTextProperties
::MergeProperties(const wxRichTextProperties
& properties
)
14029 for (i
= 0; i
< properties
.GetCount(); i
++)
14031 SetProperty(properties
.GetProperties()[i
]);
14035 wxRichTextObject
* wxRichTextObjectAddress
::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
14037 if (m_address
.GetCount() == 0)
14038 return topLevelContainer
;
14040 wxRichTextCompositeObject
* p
= topLevelContainer
;
14042 while (p
&& i
< m_address
.GetCount())
14044 int pos
= m_address
[i
];
14045 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
14046 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
14049 wxRichTextObject
* p1
= p
->GetChild(pos
);
14050 if (i
== (m_address
.GetCount()-1))
14053 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
14059 bool wxRichTextObjectAddress
::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
14063 if (topLevelContainer
== obj
)
14066 wxRichTextObject
* o
= obj
;
14069 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
14073 int pos
= p
->GetChildren().IndexOf(o
);
14077 m_address
.Insert(pos
, 0);
14079 if (p
== topLevelContainer
)
14088 bool wxRichTextSelection
::operator==(const wxRichTextSelection
& sel
) const
14090 if (m_container
!= sel
.m_container
)
14092 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
14095 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
14096 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
14101 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
14102 // or none at the level of the object's container.
14103 wxRichTextRangeArray wxRichTextSelection
::GetSelectionForObject(wxRichTextObject
* obj
) const
14107 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
14109 if (container
== m_container
)
14112 container
= obj
->GetContainer();
14115 if (container
->GetParent())
14117 // If we found that our object's container is within the range of
14118 // a selection higher up, then assume the whole original object
14119 // is also selected.
14120 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
14121 if (parentContainer
== m_container
)
14123 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
14125 wxRichTextRangeArray ranges
;
14126 ranges
.Add(obj
->GetRange());
14131 container
= parentContainer
;
14140 return wxRichTextRangeArray();
14143 // Is the given position within the selection?
14144 bool wxRichTextSelection
::WithinSelection(long pos
, wxRichTextObject
* obj
) const
14150 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
14151 return WithinSelection(pos
, selectionRanges
);
14155 // Is the given position within the selection range?
14156 bool wxRichTextSelection
::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
14159 for (i
= 0; i
< ranges
.GetCount(); i
++)
14161 const wxRichTextRange
& range
= ranges
[i
];
14162 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
14168 // Is the given range completely within the selection range?
14169 bool wxRichTextSelection
::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
14172 for (i
= 0; i
< ranges
.GetCount(); i
++)
14174 const wxRichTextRange
& eachRange
= ranges
[i
];
14175 if (range
.IsWithin(eachRange
))
14181 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
14182 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
14184 wxRichTextDrawingContext
::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
14188 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
14189 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
14192 bool wxRichTextDrawingContext
::HasVirtualAttributes(wxRichTextObject
* obj
) const
14194 if (!GetVirtualAttributesEnabled())
14197 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14200 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14201 if (handler
->HasVirtualAttributes(obj
))
14204 node
= node
->GetNext();
14209 wxRichTextAttr wxRichTextDrawingContext
::GetVirtualAttributes(wxRichTextObject
* obj
) const
14211 wxRichTextAttr attr
;
14212 if (!GetVirtualAttributesEnabled())
14215 // We apply all handlers, so we can may combine several different attributes
14216 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14219 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14220 if (handler
->HasVirtualAttributes(obj
))
14222 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
14224 wxUnusedVar(success
);
14227 node
= node
->GetNext();
14232 bool wxRichTextDrawingContext
::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
14234 if (!GetVirtualAttributesEnabled())
14237 if (HasVirtualAttributes(obj
))
14239 wxRichTextAttr
a(GetVirtualAttributes(obj
));
14247 int wxRichTextDrawingContext
::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
14249 if (!GetVirtualAttributesEnabled())
14252 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14255 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14256 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
14260 node
= node
->GetNext();
14265 int wxRichTextDrawingContext
::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
14267 if (!GetVirtualAttributesEnabled())
14270 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14273 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14274 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
14275 return positions
.GetCount();
14277 node
= node
->GetNext();
14282 bool wxRichTextDrawingContext
::HasVirtualText(const wxRichTextPlainText
* obj
) const
14284 if (!GetVirtualAttributesEnabled())
14287 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14290 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14291 if (handler
->HasVirtualText(obj
))
14294 node
= node
->GetNext();
14299 bool wxRichTextDrawingContext
::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
14301 if (!GetVirtualAttributesEnabled())
14304 wxList
::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14307 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14308 if (handler
->GetVirtualText(obj
, text
))
14311 node
= node
->GetNext();
14316 /// Adds a handler to the end
14317 void wxRichTextBuffer
::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
14319 sm_drawingHandlers
.Append(handler
);
14322 /// Inserts a handler at the front
14323 void wxRichTextBuffer
::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
14325 sm_drawingHandlers
.Insert( handler
);
14328 /// Removes a handler
14329 bool wxRichTextBuffer
::RemoveDrawingHandler(const wxString
& name
)
14331 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
14334 sm_drawingHandlers
.DeleteObject(handler
);
14342 wxRichTextDrawingHandler
* wxRichTextBuffer
::FindDrawingHandler(const wxString
& name
)
14344 wxList
::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
14347 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14348 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
14350 node
= node
->GetNext();
14355 void wxRichTextBuffer
::CleanUpDrawingHandlers()
14357 wxList
::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
14360 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14361 wxList
::compatibility_iterator next
= node
->GetNext();
14366 sm_drawingHandlers
.Clear();
14369 void wxRichTextBuffer
::AddFieldType(wxRichTextFieldType
*fieldType
)
14371 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
14374 bool wxRichTextBuffer
::RemoveFieldType(const wxString
& name
)
14376 wxRichTextFieldTypeHashMap
::iterator it
= sm_fieldTypes
.find(name
);
14377 if (it
== sm_fieldTypes
.end())
14381 wxRichTextFieldType
* fieldType
= it
->second
;
14382 sm_fieldTypes
.erase(it
);
14388 wxRichTextFieldType
*wxRichTextBuffer
::FindFieldType(const wxString
& name
)
14390 wxRichTextFieldTypeHashMap
::iterator it
= sm_fieldTypes
.find(name
);
14391 if (it
== sm_fieldTypes
.end())
14397 void wxRichTextBuffer
::CleanUpFieldTypes()
14399 wxRichTextFieldTypeHashMap
::iterator it
;
14400 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14402 wxRichTextFieldType
* fieldType
= it
->second
;
14406 sm_fieldTypes
.clear();